• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 19933858499

04 Dec 2025 01:54PM UTC coverage: 76.636% (-0.03%) from 76.661%
19933858499

Pull #11648

github

web-flow
Update User Guide - Itinerary plugin (#11768)

* add_11664

* Update docs/user-guide/itinerary.md

Co-authored-by: Suren <dsuren1@gmail.com>

* Update docs/user-guide/itinerary.md

Co-authored-by: Suren <dsuren1@gmail.com>

* Update docs/user-guide/itinerary.md

Co-authored-by: Suren <dsuren1@gmail.com>

* Update docs/user-guide/itinerary.md

Co-authored-by: Suren <dsuren1@gmail.com>

---------

Co-authored-by: Suren <dsuren1@gmail.com>
Pull Request #11648: #11644: Implement dynamic request configurations

32316 of 50296 branches covered (64.25%)

132 of 155 new or added lines in 40 files covered. (85.16%)

283 existing lines in 32 files now uncovered.

40207 of 52465 relevant lines covered (76.64%)

37.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.45
/web/client/api/WMTS.js
1
/**
2
 * Copyright 2016, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8
import axios from '../libs/ajax';
9

10
import { getConfigProp } from '../utils/ConfigUtils';
11
import urlUtil from 'url';
12
import xml2js from 'xml2js';
13

14
const capabilitiesCache = {};
1✔
15

16
import { castArray } from 'lodash';
17
import { getEPSGCode } from '../utils/CoordinatesUtils';
18
import { getDefaultUrl } from '../utils/URLUtils';
19

20
import {
21
    getOperations,
22
    getOperation,
23
    getRequestEncoding,
24
    getDefaultStyleIdentifier,
25
    getDefaultFormat
26
} from '../utils/WMTSUtils';
27
import { getAuthorizationBasic } from '../utils/SecurityUtils';
28

29
export const parseUrl = (url) => {
1✔
30
    const parsed = urlUtil.parse(getDefaultUrl(url), true);
15✔
31
    return urlUtil.format(Object.assign({}, parsed, {search: null}, {
15✔
32
        query: Object.assign({
33
            SERVICE: "WMTS",
34
            VERSION: "1.0.0",
35
            REQUEST: "GetCapabilities"
36
        }, parsed.query)
37
    }));
38
};
39

40
const searchAndPaginate = (json, startPosition, maxRecords, text, url) => {
1✔
41
    const root = json.Capabilities.Contents;
5✔
42
    const operations = getOperations(json);
5✔
43
    const requestEncoding = getRequestEncoding(json);
5✔
44
    const TileMatrixSet = root.TileMatrixSet && castArray(root.TileMatrixSet) || [];
5!
45
    let SRSList = [];
5✔
46
    let len = TileMatrixSet.length;
5✔
47
    for (let i = 0; i < len; i++) {
5✔
48
        SRSList.push(getEPSGCode(TileMatrixSet[i]["ows:SupportedCRS"]));
5✔
49
    }
50
    const layersObj = root.Layer;
5✔
51
    const layers = castArray(layersObj);
5✔
52
    const filteredLayers = layers
5✔
53
        .filter((layer) => !text || layer["ows:Identifier"].toLowerCase().indexOf(text.toLowerCase()) !== -1 || layer["ows:Title"] && layer["ows:Title"].toLowerCase().indexOf(text.toLowerCase()) !== -1);
19✔
54
    return {
5✔
55
        numberOfRecordsMatched: filteredLayers.length,
56
        numberOfRecordsReturned: Math.min(maxRecords, filteredLayers.length),
57
        nextRecord: startPosition + Math.min(maxRecords, filteredLayers.length) + 1,
58
        records: filteredLayers
59
            .filter((layer, index) => index >= startPosition - 1 && index < startPosition - 1 + maxRecords)
15✔
60
            .map((layer) => Object.assign({}, layer, {
12✔
61
                SRS: SRSList,
62
                TileMatrixSet,
63
                // Only KVP is supported by MapInfo, for the moment. TODO: Support single layer's InfoFormat
64
                queryable: !!getOperation(operations, "GetFeatureInfo", "KVP"),
65
                requestEncoding: requestEncoding,
66
                style: getDefaultStyleIdentifier(layer), // it must be collected because it can be used in RESTful version to create the path
67
                format: getDefaultFormat(layer),
68
                formats: castArray(layer.Format),
69
                GetTileURL: getOperation(operations, "GetTile", requestEncoding),
70
                capabilitiesURL: url
71
            }))
72
    };
73
};
74

75
const Api = {
1✔
76
    parseUrl,
77
    getRecords: function(url, startPosition, maxRecords, text, options) {
78
        const cached = capabilitiesCache[url];
5✔
79
        if (cached && new Date().getTime() < cached.timestamp + (getConfigProp('cacheExpire') || 60) * 1000) {
5✔
80
            return new Promise((resolve) => {
1✔
81
                resolve(searchAndPaginate(cached.data, startPosition, maxRecords, text, url));
1✔
82
            });
83
        }
84
        const protectedId = options?.options?.service?.protectedId;
4✔
85
        let headers = getAuthorizationBasic(protectedId);
4✔
86
        return axios.get(parseUrl(url), {headers}).then((response) => {
4✔
87
            let json;
88
            xml2js.parseString(response.data, {explicitArray: false}, (ignore, result) => {
4✔
89
                json = result;
4✔
90
            });
91
            capabilitiesCache[url] = {
4✔
92
                timestamp: new Date().getTime(),
93
                data: json
94
            };
95
            return searchAndPaginate(json, startPosition, maxRecords, text, url);
4✔
96
        });
97
    },
98
    getCapabilities: (url, options) => {
99
        if (options?.force && capabilitiesCache[url]) {
10!
UNCOV
100
            delete capabilitiesCache[url];
×
101
        }
102
        const cached = capabilitiesCache[url];
10✔
103
        if (cached && new Date().getTime() < cached.timestamp + (getConfigProp('cacheExpire') || 60) * 1000) {
10!
UNCOV
104
            return new Promise((resolve) => {
×
UNCOV
105
                resolve(cached.data);
×
106
            });
107
        }
108
        const protectedId = options?.options?.service?.protectedId;
10✔
109
        let headers = getAuthorizationBasic(protectedId);
10✔
110
        return axios.get(parseUrl(url), {headers}).then((response) => {
10✔
111
            let json;
112
            xml2js.parseString(response.data, {explicitArray: false}, (ignore, result) => {
9✔
113
                json = result;
9✔
114
            });
115
            capabilitiesCache[url] = {
9✔
116
                timestamp: new Date().getTime(),
117
                data: json
118
            };
119
            return json;
9✔
120
        });
121
    },
122
    textSearch: function(url, startPosition, maxRecords, text, options) {
123
        return Api.getRecords(url, startPosition, maxRecords, text, options);
2✔
124
    },
125
    reset: () => {
126
        Object.keys(capabilitiesCache).forEach(key => {
18✔
127
            delete capabilitiesCache[key];
13✔
128
        });
129
    }
130
};
131

132
/**
133
 * Return tileMatrixSets, tileMatrixSetLinks, tileGrids, styles and formats of from WMTS capabilities for a specific layer
134
 * @param {string} url wmts endpoint url
135
 * @param {string} layerName layer name
136
 * @param {object} options optional configuration
137
 * @param {boolean} options.force if true delete the cache for this url and force the request
138
 * @return {promise}
139
 */
140
export const getLayerTileMatrixSetsInfo = (url, layerName = '', options) => {
1!
141
    return Api.getCapabilities(url, { force: options?.force })
9✔
142
        .then((response) => {
143
            const layerParts = layerName.split(':');
8✔
144
            const layers = castArray(response?.Capabilities?.Contents?.Layer || []);
8✔
145
            const wmtsLayer = layers.find((layer) => layer['ows:Identifier'] === layerParts[1] || layer['ows:Identifier'] === layerName);
8!
146
            const tileMatrixSetLinks = castArray(wmtsLayer?.TileMatrixSetLink || []).map(({ TileMatrixSet }) => TileMatrixSet);
11✔
147
            const tileMatrixSets = castArray(response?.Capabilities?.Contents?.TileMatrixSet || []).filter((tileMatrixSet) => tileMatrixSetLinks.includes(tileMatrixSet['ows:Identifier']));
12✔
148
            const tileGrids = tileMatrixSets.map((tileMatrixSet) => {
8✔
149
                const origins = tileMatrixSet.TileMatrix.map((tileMatrixLevel) => tileMatrixLevel.TopLeftCorner.split(' ').map(parseFloat));
33✔
150
                const tileSizes = tileMatrixSet.TileMatrix.map((tileMatrixLevel) => [parseFloat(tileMatrixLevel.TileWidth), parseFloat(tileMatrixLevel.TileHeight)]);
33✔
151
                const firstOrigin = origins[0];
11✔
152
                const firsTileSize = tileSizes[0];
11✔
153
                const isSingleOrigin = origins.every(entry => firstOrigin[0] === entry[0] && firstOrigin[1] === entry[1]);
33✔
154
                const isSingleTileSize = tileSizes.every(entry => firsTileSize[0] === entry[0] && firsTileSize[1] === entry[1]);
33✔
155
                return {
11✔
156
                    id: tileMatrixSet['ows:Identifier'],
157
                    crs: getEPSGCode(tileMatrixSet['ows:SupportedCRS']),
158
                    scales: tileMatrixSet.TileMatrix.map((tileMatrixLevel) => parseFloat(tileMatrixLevel.ScaleDenominator)),
33✔
159
                    ...(isSingleOrigin ? { origin: firstOrigin } : { origins }),
11✔
160
                    ...(isSingleTileSize ? { tileSize: firsTileSize } : { tileSizes })
11!
161
                };
162
            });
163
            return {
8✔
164
                tileMatrixSets,
165
                tileMatrixSetLinks,
166
                tileGrids,
167
                styles: castArray(wmtsLayer?.Style || []).map((style) => style['ows:Identifier']),
6✔
168
                formats: castArray(wmtsLayer?.Format || [])
10✔
169
            };
170
        });
171
};
172

173
export default Api;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc