• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

iTowns / itowns / 7166285413

11 Dec 2023 10:44AM UTC coverage: 76.943% (-0.1%) from 77.075%
7166285413

Pull #2128

github

web-flow
Merge 06b44bf2c into ac9cea429
Pull Request #2128: Mapbox style multiple source

4020 of 5962 branches covered (0.0%)

Branch coverage included in aggregate %.

17 of 44 new or added lines in 2 files covered. (38.64%)

14 existing lines in 2 files now uncovered.

7990 of 9647 relevant lines covered (82.82%)

1870.05 hits per line

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

93.14
/src/Source/Source.js
1
import Extent from 'Core/Geographic/Extent';
1✔
2
import GeoJsonParser from 'Parser/GeoJsonParser';
1✔
3
import KMLParser from 'Parser/KMLParser';
1✔
4
import GDFParser from 'Parser/GDFParser';
1✔
5
import GpxParser from 'Parser/GpxParser';
1✔
6
import GTXParser from 'Parser/GTXParser';
1✔
7
import ISGParser from 'Parser/ISGParser';
1✔
8
import VectorTileParser from 'Parser/VectorTileParser';
1✔
9
import Fetcher from 'Provider/Fetcher';
1✔
10
import Cache from 'Core/Scheduler/Cache';
1✔
11
import CRS from 'Core/Geographic/Crs';
596✔
12

13
export const supportedFetchers = new Map([
1✔
14
    ['image/x-bil;bits=32', Fetcher.textureFloat],
15
    ['geojson', Fetcher.json],
16
    ['application/json', Fetcher.json],
17
    ['application/kml', Fetcher.xml],
18
    ['application/gpx', Fetcher.xml],
19
    ['application/x-protobuf;type=mapbox-vector', Fetcher.arrayBuffer],
20
    ['application/gtx', Fetcher.arrayBuffer],
21
    ['application/isg', Fetcher.text],
22
    ['application/gdf', Fetcher.text],
23
]);
1✔
24

25
export const supportedParsers = new Map([
1✔
26
    ['geojson', GeoJsonParser.parse],
27
    ['application/json', GeoJsonParser.parse],
28
    ['application/kml', KMLParser.parse],
29
    ['application/gpx', GpxParser.parse],
30
    ['application/x-protobuf;type=mapbox-vector', VectorTileParser.parse],
31
    ['application/gtx', GTXParser.parse],
32
    ['application/isg', ISGParser.parse],
33
    ['application/gdf', GDFParser.parse],
34
]);
1✔
35

36
const noCache = { getByArray: () => {}, setByArray: a => a, clear: () => {} };
9✔
37

38
/**
39
 * @property {string} crs - data crs projection.
40
 * @property {boolean} isInverted - This option is to be set to the
41
 * correct value, true or false (default being false), if the computation of
42
 * the coordinates needs to be inverted to same scheme as OSM, Google Maps
43
 * or other system. See [this link]{@link
44
 * https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates}
45
 * for more informations.
46
 *
47
 */
48
class InformationsData {
1✔
49
    constructor(options) {
118✔
50
        /* istanbul ignore next */
51
        if (options.projection) {
52
            console.warn('Source projection parameter is deprecated, use crs instead.');
53
            options.crs = options.crs || options.projection;
54
        }
55
        if (options.crs) {
118✔
56
            CRS.isValid(options.crs);
45✔
57
        }
58
        this.crs = options.crs;
118✔
59
    }
60
}
61
/**
62
 * This class describes parsing options.
63
 * @property {InformationsData|Source} in - data informations contained in the file.
64
 * @property {FeatureBuildingOptions|Layer} out - options indicates how the features should be built.
65
 */
66
// eslint-disable-next-line
67
class /* istanbul ignore next */ ParsingOptions {}
68

69
function fetchSourceData(source, extent) {
70
    const url = source.urlFromExtent(extent);
12✔
71
    return source.fetcher(url, source.networkOptions).then((f) => {
12✔
72
        f.extent = extent;
12✔
73
        return f;
12✔
UNCOV
74
    }, err => source.handlingError(err));
×
75
}
76

77
let uid = 0;
1✔
78

79
/**
80
 * @classdesc
81
 * Sources are object containing informations on how to fetch resources, from a
82
 * set source.
83
 *
84
 * To extend a Source, it is necessary to implement two functions:
85
 * `urlFromExtent` and `extentInsideLimit`.
86
 *
87
 * @property {boolean} isSource - Used to checkout whether this source is a
88
 * Source. Default is true. You should not change this, as it is used internally
89
 * for optimisation.
90
 * @property {number} uid - Unique uid mainly used to store data linked to this
91
 * source into Cache.
92
 * @property {string} url - The url of the resources that are fetched.
93
 * @property {string} format - The format of the resources that are fetched.
94
 * @property {function} fetcher - The method used to fetch the resources from
95
 * the source. iTowns provides some methods in {@link Fetcher}, but it can be
96
 * specified a custom one. This method should return a `Promise` containing the
97
 * fetched resource. If this property is set, it overrides the chosen fetcher
98
 * method with `format`.
99
 * @property {Object} networkOptions - Fetch options (passed directly to
100
 * `fetch()`), see [the syntax for more information]{@link
101
 * https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax}.
102
 * By default, set to `{ crossOrigin: 'anonymous' }`.
103
 * @property {string} crs - The crs projection of the resources.
104
 * @property {string} attribution - The intellectual property rights for the
105
 * resources.
106
 * @property {Extent} extent - The extent of the resources.
107
 * @property {function} parser - The method used to parse the resources attached
108
 * to the layer. iTowns provides some parsers, visible in the `Parser/` folder.
109
 * If the method is custom, it should return a `Promise` containing the parsed
110
 * resource. If this property is set, it overrides the default selected parser
111
 * method with `source.format`. If `source.format` is also empty, no parsing
112
 * action is done.
113
 * <br><br>
114
 * When calling this method, two parameters are passed:
115
 * <ul>
116
 *  <li>the fetched data, i.e. the data to parse</li>
117
 *  <li>an {@link ParsingOptions}  containing severals properties, set when this method is
118
 *  called: it is specific to each call, so the value of each property can vary
119
 *  depending on the current fetched tile for example</li>
120
 * </ul>
121
 */
122
class Source extends InformationsData {
2✔
123
    /**
124
     * @param {Object} source - An object that can contain all properties of a
125
     * Source. Only the `url` property is mandatory.
126
     *
127
     * @constructor
128
     * @extends InformationsData
129
     */
130
    constructor(source) {
118✔
131
        super(source);
118✔
132
        this.isSource = true;
118✔
133

134
        if (!source.url) {
118✔
135
            throw new Error('New Source: url is required');
2✔
136
        }
137

138
        this.uid = uid++;
116✔
139

140
        this.url = source.url;
116✔
141
        this.format = source.format;
116✔
142
        this.fetcher = source.fetcher || supportedFetchers.get(source.format) || Fetcher.texture;
116✔
143
        this.parser = source.parser || supportedParsers.get(source.format) || (d => d);
116✔
144
        this.isVectorSource = (source.parser || supportedParsers.get(source.format)) != undefined;
116✔
145
        this.networkOptions = source.networkOptions || { crossOrigin: 'anonymous' };
116✔
146
        this.attribution = source.attribution;
116✔
147
        this.whenReady = Promise.resolve();
116✔
148
        this._featuresCaches = {};
116✔
149
        if (source.extent && !(source.extent.isExtent)) {
116✔
150
            this.extent = new Extent(this.crs, source.extent);
3✔
151
        } else {
152
            this.extent = source.extent;
113✔
153
        }
116✔
154
    }
1✔
155

156
    handlingError(err) {
157
        throw new Error(err);
1✔
158
    }
159

160
    /**
161
     * Generates an url from an extent. This url is a link to fetch the
162
     * resources inside the extent.
163
     *
164
     * @param {Extent} extent - Extent to convert in url.
165

166
     * @return {string} The URL constructed from the extent.
167
     */
168
    // eslint-disable-next-line
169
    urlFromExtent(extent) {
170
        throw new Error('In extended Source, you have to implement the method urlFromExtent!');
1✔
171
    }
172

173
    requestToKey(extent) {
174
        return [extent.zoom, extent.row, extent.col];
27✔
175
    }
176

177
    /**
178
     * Load  data from cache or Fetch/Parse data.
179
     * The loaded data is a Feature or Texture.
180
     *
181
     * @param      {Extent}  extent   extent requested parsed data.
182
     * @param      {FeatureBuildingOptions|Layer}  out     The feature returned options
183
     * @return     {FeatureCollection|Texture}  The parsed data.
184
     */
185
    loadData(extent, out) {
12✔
186
        const cache = this._featuresCaches[out.crs];
12✔
187
        const key = this.requestToKey(extent);
12✔
188
        // try to get parsed data from cache
189
        let features = cache.getByArray(key);
12✔
190
        if (!features) {
12!
191
            // otherwise fetch/parse the data
192
            features = cache.setByArray(fetchSourceData(this, extent)
12✔
193
                .then(file => this.parser(file, { out, in: this }),
12✔
NEW
UNCOV
194
                    err => this.handlingError(err)), key);
×
195
            /* istanbul ignore next */
196
            if (this.onParsedFile) {
197
                features.then((feat) => {
198
                    this.onParsedFile(feat);
199
                    console.warn('Source.onParsedFile was deprecated');
200
                    return feat;
201
                });
202
            }
203
        }
204
        return features;
12✔
205
    }
206

207
    /**
208
     * Called when layer added.
209
     *
210
     * @param {object} options
211
     */
212
    onLayerAdded(options) {
213
        // Added new cache by crs
214
        if (!this._featuresCaches[options.out.crs]) {
80✔
215
            // Cache feature only if it's vector data, the feature are cached in source.
216
            // It's not necessary to cache raster in Source,
217
            // because it's already cached on layer.
218
            this._featuresCaches[options.out.crs] = this.isVectorSource ? new Cache() : noCache;
73✔
219
        }
220
    }
221

222
    /**
223
     * Called when layer removed.
224
     *
225
     * @param {options}  [options={}] options
226
     */
227
    onLayerRemoved(options = {}) {
2!
228
        // delete unused cache
229
        const unusedCache = this._featuresCaches[options.unusedCrs];
2✔
230
        if (unusedCache) {
2!
UNCOV
231
            unusedCache.clear();
×
UNCOV
232
            delete this._featuresCaches[options.unusedCrs];
×
233
        }
234
    }
235

236
    /**
237
     * Tests if an extent is inside the source limits.
238
     *
239
     * @param {Extent} extent - Extent to test.
240

241
     * @return {boolean} True if the extent is inside the limit, false otherwise.
242
     */
243
    // eslint-disable-next-line
244
    extentInsideLimit(extent) {
245
        throw new Error('In extented Source, you have to implement the method extentInsideLimit!');
1✔
246
    }
1✔
247
}
248

249
export default Source;
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

© 2025 Coveralls, Inc