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

iTowns / itowns / 12866516025

20 Jan 2025 10:58AM UTC coverage: 87.191% (-0.008%) from 87.199%
12866516025

Pull #2451

github

web-flow
Merge 71f8cb618 into c4629d673
Pull Request #2451: replace Cache by Lru cache

2772 of 3685 branches covered (75.22%)

Branch coverage included in aggregate %.

68 of 68 new or added lines in 9 files covered. (100.0%)

8 existing lines in 2 files now uncovered.

25212 of 28410 relevant lines covered (88.74%)

1147.45 hits per line

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

93.78
/src/Source/VectorTilesSource.js
1
import { featureFilter } from '@maplibre/maplibre-gl-style-spec';
1✔
2
import Style from 'Core/Style';
1✔
3
import TMSSource from 'Source/TMSSource';
1✔
4
import URLBuilder from 'Provider/URLBuilder';
1✔
5
import Fetcher from 'Provider/Fetcher';
1✔
6
import urlParser from 'Parser/MapBoxUrlParser';
1✔
7

1✔
8
function toTMSUrl(url) {
13✔
9
    return url.replace(/\{/g, '${');
13✔
10
}
13✔
11

1✔
12
function mergeCollections(collections) {
1✔
13
    const collection = collections[0];
1✔
14
    collections.forEach((col, index) => {
1✔
15
        if (index === 0) { return; }
2✔
16
        col.features.forEach((feature) => {
1✔
17
            collection.features.push(feature);
1✔
18
        });
1✔
19
    });
1✔
20
    return collection;
1✔
21
}
1✔
22

1✔
23
// A deprecated (but still in use) Mapbox spec allows using 'ref' as a propertie to reference an other layer
1✔
24
// instead of duplicating the following properties: 'type', 'source', 'source-layer', 'minzoom', 'maxzoom', 'filter', 'layout'
1✔
25
function getPropertiesFromRefLayer(layers, layer) {
1✔
26
    const refProperties = ['type', 'source', 'source-layer', 'minzoom', 'maxzoom', 'filter', 'layout'];
1✔
27
    const refLayer = layers.filter(l => l.id === layer.ref)[0];
1✔
28
    refProperties.forEach((prop) => {
1✔
29
        layer[prop] = refLayer[prop];
7✔
30
    });
1✔
31
}
1✔
32

1✔
33
/**
1✔
34
 * VectorTilesSource are object containing informations on how to fetch vector
1✔
35
 * tiles resources.
1✔
36
 *
1✔
37
 * @property {function} filter - function to filter vector tiles layers, the
1✔
38
 * parameter function is a layer.
1✔
39
 * @property {boolean} [symbolToCircle=false] - If true, all symbols from a tile
1✔
40
 * will be considered as circle, and render as circles.
1✔
41
 */
1✔
42
class VectorTilesSource extends TMSSource {
1✔
43
    /**
1✔
44
     * @param {Object} source - An object that can contain all properties of a
1✔
45
     * VectorTilesSource and {@link Source}.
1✔
46
     * @param {string|Object} source.style - The URL of the JSON style, of the
1✔
47
     * JSON style directly.
1✔
48
     * @param {string} [source.sprite] - The base URL to load informations about
1✔
49
     * the sprite of the style. If this is set, it overrides the `sprite` value
1✔
50
     * of the `source.style`. A style's sprite property supplies a URL template
1✔
51
     * for loading small images.
1✔
52
     * ```js
1✔
53
     * {
1✔
54
     *      sprite: 'http//:xxxxx/maps/sprites/'
1✔
55
     * }
1✔
56
     * ```
1✔
57
     * A valid sprite source must supply two types of files:
1✔
58
     * * An index file, which is a JSON document containing a description of each image contained in the sprite.
1✔
59
     * * Image files, which are PNG images containing the sprite data.
1✔
60
     *
1✔
61
     * For more specification : [the Mapbox sprite Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/)
1✔
62
     *
1✔
63
     * @param {string} [source.url] - The base URL to load the tiles. If no url
1✔
64
     * is specified, it reads it from the loaded style. Read [the Mapbox Style
1✔
65
     * Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)
1✔
66
     * for more informations.
1✔
67
     * @param {string} [source.accessToken] - Mapbox access token
1✔
68
     */
1✔
69
    constructor(source) {
1✔
70
        source.format = 'application/x-protobuf;type=mapbox-vector';
11✔
71
        source.crs = 'EPSG:3857';
11✔
72
        source.isInverted = true;
11✔
73
        source.url = source.url || '.';
11✔
74
        super(source);
11✔
75
        const ffilter = source.filter || (() => true);
11✔
76
        this.urls = [];
11✔
77
        this.layers = {};
11✔
78
        this.styles = {};
11✔
79
        let promise;
11✔
80
        this.isVectorTileSource = true;
11✔
81

11✔
82
        this.accessToken = source.accessToken;
11✔
83

11✔
84
        let mvtStyleUrl;
11✔
85
        if (source.style) {
11✔
86
            if (typeof source.style == 'string') {
10✔
87
                mvtStyleUrl = urlParser.normalizeStyleURL(source.style, this.accessToken);
1✔
88
                promise = Fetcher.json(mvtStyleUrl, this.networkOptions);
1✔
89
            } else {
10✔
90
                promise = Promise.resolve(source.style);
9✔
91
            }
9✔
92
        } else {
11✔
93
            throw new Error('New VectorTilesSource: style is required');
1✔
94
        }
1✔
95

10✔
96
        this.whenReady = promise.then((mvtStyle) => {
10✔
97
            this.jsonStyle = mvtStyle;
10✔
98
            let baseurl = source.sprite || mvtStyle.sprite;
10✔
99
            if (baseurl) {
10✔
100
                baseurl = new URL(baseurl, mvtStyleUrl).toString();
1✔
101
                const spriteUrl = urlParser.normalizeSpriteURL(baseurl, '', '.json', this.accessToken);
1✔
102
                return Fetcher.json(spriteUrl, this.networkOptions).then((sprites) => {
1✔
103
                    this.sprites = sprites;
1✔
104
                    const imgUrl = urlParser.normalizeSpriteURL(baseurl, '', '.png', this.accessToken);
1✔
105
                    this.sprites.source = imgUrl;
1✔
106
                    return mvtStyle;
1✔
107
                });
1✔
108
            }
1✔
109

9✔
110
            return mvtStyle;
9✔
111
        }).then((mvtStyle) => {
10✔
112
            mvtStyle.layers.forEach((layer, order) => {
10✔
113
                layer.sourceUid = this.uid;
13✔
114
                if (layer.type === 'background') {
13✔
115
                    this.backgroundLayer = layer;
2✔
116
                } else if (ffilter(layer)) {
13✔
117
                    if (layer['source-layer'] === undefined) {
11✔
118
                        getPropertiesFromRefLayer(mvtStyle.layers, layer);
1✔
119
                    }
1✔
120
                    const style = Style.setFromVectorTileLayer(layer, this.sprites, this.symbolToCircle);
11✔
121
                    this.styles[layer.id] = style;
11✔
122

11✔
123
                    if (!this.layers[layer['source-layer']]) {
11✔
124
                        this.layers[layer['source-layer']] = [];
6✔
125
                    }
6✔
126
                    this.layers[layer['source-layer']].push({
11✔
127
                        id: layer.id,
11✔
128
                        order,
11✔
129
                        filterExpression: featureFilter(layer.filter),
11✔
130
                    });
11✔
131
                }
11✔
132
            });
10✔
133

10✔
134
            if (this.url == '.') {
10✔
135
                const TMSUrlList = Object.values(mvtStyle.sources).map((sourceVT) => {
6✔
136
                    if (sourceVT.url) {
9✔
137
                        sourceVT.url = new URL(sourceVT.url, mvtStyleUrl).toString();
2✔
138
                        const urlSource = urlParser.normalizeSourceURL(sourceVT.url, this.accessToken);
2✔
139
                        return Fetcher.json(urlSource, this.networkOptions).then((tileJSON) => {
2✔
140
                            if (tileJSON.tiles[0]) {
2✔
141
                                tileJSON.tiles[0] = decodeURIComponent(new URL(tileJSON.tiles[0], urlSource).toString());
2✔
142
                                return toTMSUrl(tileJSON.tiles[0]);
2✔
143
                            }
2✔
144
                        });
2✔
145
                    } else if (sourceVT.tiles) {
9✔
146
                        return Promise.resolve(toTMSUrl(sourceVT.tiles[0]));
7✔
147
                    }
7!
148
                    return Promise.reject();
×
149
                });
6✔
150
                return Promise.all(TMSUrlList);
6✔
151
            }
6✔
152
            return (Promise.resolve([toTMSUrl(this.url)]));
4✔
153
        }).then((TMSUrlList) => {
10✔
154
            this.urls = Array.from(new Set(TMSUrlList));
10✔
155
        });
10✔
156
    }
10✔
157

1✔
158
    urlFromExtent(tile, url) {
1✔
159
        return URLBuilder.xyz(tile, { tileMatrixCallback: this.tileMatrixCallback, url });
2✔
160
    }
2✔
161

1✔
162
    onLayerAdded(options) {
1✔
163
        super.onLayerAdded(options);
1✔
164
        if (options.out.style) {
1!
165
            if (options.out.isFeatureGeometryLayer && options.out.accurate) {
×
166
                console.warn('With VectorTilesSource and FeatureGeometryLayer, the accurate option is always false');
×
167
                options.out.accurate = false;
×
168
            }
×
169
        }
×
170
    }
1✔
171

1✔
172
    loadData(extent, out) {
1✔
173
        const cache = this._featuresCaches[out.crs];
1✔
174
        const key = this.keysFromExtent(extent);
1✔
175
        // try to get parsed data from cache
1✔
176
        let features = cache.get(...key);
1✔
177
        if (!features) {
1✔
178
            // otherwise fetch/parse the data
1✔
179
            features = cache.set(
1✔
180
                Promise.all(this.urls.map(url =>
1✔
181
                    this.fetcher(this.urlFromExtent(extent, url), this.networkOptions)
2✔
182
                        .then(file => this.parser(file, { out, in: this, extent }))))
1✔
183
                    .then(collections => mergeCollections(collections))
1✔
184
                    .catch(err => this.handlingError(err)),
1✔
185
                ...key);
1✔
186

1✔
187
            if (this.onParsedFile) {
1!
UNCOV
188
                features.then((feat) => {
×
UNCOV
189
                    this.onParsedFile(feat);
×
UNCOV
190
                    console.warn('Source.onParsedFile was deprecated');
×
UNCOV
191
                    return feat;
×
UNCOV
192
                });
×
UNCOV
193
            }
×
194
        }
1✔
195
        return features;
1✔
196
    }
1✔
197
}
1✔
198

1✔
199
export default VectorTilesSource;
1✔
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