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

iTowns / itowns / 27004518448

05 Jun 2026 08:33AM UTC coverage: 88.483% (+0.002%) from 88.481%
27004518448

push

github

ftoromanoff
refactor(Errors): clean up catch path of errors

2893 of 3706 branches covered (78.06%)

Branch coverage included in aggregate %.

35 of 46 new or added lines in 6 files covered. (76.09%)

11 existing lines in 2 files now uncovered.

29367 of 32753 relevant lines covered (89.66%)

1281.74 hits per line

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

98.36
/packages/Main/src/Source/Source.js
1
import { Extent, CRS } from '@itowns/geographic';
2✔
2
import GeoJsonParser from 'Parser/GeoJsonParser';
2✔
3
import KMLParser from 'Parser/KMLParser';
2✔
4
import GeotiffParser from 'Parser/GeotiffParser';
2✔
5
import GDFParser from 'Parser/GDFParser';
2✔
6
import GpxParser from 'Parser/GpxParser';
2✔
7
import GTXParser from 'Parser/GTXParser';
2✔
8
import ISGParser from 'Parser/ISGParser';
2✔
9
import VectorTileParser from 'Parser/VectorTileParser';
2✔
10
import Fetcher from 'Provider/Fetcher';
2✔
11
import { LRUCache } from 'lru-cache';
2✔
12

2✔
13
/** @private */
2✔
14
export const supportedParsers = new Map([
2✔
15
    ['application/geo+json', GeoJsonParser.parse],
2✔
16
    ['application/json', GeoJsonParser.parse],
2✔
17
    ['application/kml', KMLParser.parse],
2✔
18
    ['application/gpx', GpxParser.parse],
2✔
19
    ['application/x-protobuf;type=mapbox-vector', VectorTileParser.parse],
2✔
20
    ['application/gtx', GTXParser.parse],
2✔
21
    ['application/isg', ISGParser.parse],
2✔
22
    ['application/gdf', GDFParser.parse],
2✔
23
    ['image/geotiff', GeotiffParser.parse],
2✔
24
]);
2✔
25

2✔
26
const noCache = { get: () => {}, set: a => a, clear: () => {} };
2✔
27

2✔
28
/**
2✔
29
 * This interface describes parsing options.
2✔
30
 * @typedef {object} ParsingOptions
2✔
31
 * @property {Source} in - data informations contained in the file.
2✔
32
 * @property {FeatureBuildingOptions|Layer} out - options indicates how the features should be built.
2✔
33
 */
2✔
34

2✔
35
let uid = 0;
2✔
36

2✔
37
/**
2✔
38
 * Sources are object containing informations on how to fetch resources, from a
2✔
39
 * set source.
2✔
40
 *
2✔
41
 * To extend a Source, it is necessary to implement two functions:
2✔
42
 * `urlFromExtent` and `extentInsideLimit`.
2✔
43
 *
2✔
44
 * @extends InformationsData
2✔
45
 *
2✔
46
 * @property {boolean} isSource - Used to checkout whether this source is a
2✔
47
 * Source. Default is true. You should not change this, as it is used internally
2✔
48
 * for optimisation.
2✔
49
 * @property {number} uid - Unique uid mainly used to store data linked to this
2✔
50
 * source into Cache.
2✔
51
 * @property {string} url - The url of the resources that are fetched.
2✔
52
 * @property {string} format - The format of the resources that are fetched.
2✔
53
 * @property {Function} fetcher - The method used to fetch the resources from
2✔
54
 * the source. iTowns provides some methods in {@link Fetcher}, but it can be
2✔
55
 * specified a custom one. This method should return a `Promise` containing the
2✔
56
 * fetched resource. If this property is set, it overrides the chosen fetcher
2✔
57
 * method with `format`.
2✔
58
 * @property {object} networkOptions - Fetch options (passed directly to
2✔
59
 * `fetch()`), see [the syntax for more information](
2✔
60
 * https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
2✔
61
 * By default, set to `{ crossOrigin: 'anonymous' }`.
2✔
62
 * @property {string} crs - The crs projection of the resources.
2✔
63
 * @property {string} attribution - The intellectual property rights for the
2✔
64
 * resources.
2✔
65
 * @property {Extent} extent - The extent of the resources.
2✔
66
 * @property {Function} parser - The method used to parse the resources attached
2✔
67
 * to the layer. iTowns provides some parsers, visible in the `Parser/` folder.
2✔
68
 * If the method is custom, it should return a `Promise` containing the parsed
2✔
69
 * resource. If this property is set, it overrides the default selected parser
2✔
70
 * method with `source.format`. If `source.format` is also empty, no parsing
2✔
71
 * action is done.
2✔
72
 * <br><br>
2✔
73
 * When calling this method, two parameters are passed:
2✔
74
 * <ul>
2✔
75
 *  <li>the fetched data, i.e. the data to parse</li>
2✔
76
 *  <li>an {@link ParsingOptions}  containing severals properties, set when this method is
2✔
77
 *  called: it is specific to each call, so the value of each property can vary
2✔
78
 *  depending on the current fetched tile for example</li>
2✔
79
 * </ul>
2✔
80
 */
2✔
81
class Source {
2✔
82
    /**
2✔
83
     * @param {object} source - An object that can contain all properties of a
2✔
84
     * Source. Only the `url` property is mandatory.
2✔
85
     */
2✔
86
    constructor(source) {
2✔
87
        if (source.projection) {
294✔
88
            console.warn('Source projection parameter is deprecated, use crs instead.');
2✔
89
            source.crs = source.crs || source.projection;
2✔
90
        }
2✔
91
        if (source.crs) {
294✔
92
            CRS.isValid(source.crs);
116✔
93
        }
116✔
94
        this.crs = source.crs;
294✔
95
        this.isSource = true;
294✔
96

294✔
97
        if (!source.url) {
294✔
98
            throw new Error('New Source: url is required');
4✔
99
        }
4✔
100

290✔
101
        this.uid = uid++;
290✔
102

290✔
103
        this.url = source.url;
290✔
104
        this.format = source.format;
290✔
105
        this.fetcher = source.fetcher || Fetcher.get(source.format);
294✔
106
        this.parser = source.parser || supportedParsers.get(source.format) || ((d, opt) => { d.extent = opt.extent; return d; });
294✔
107
        this.isVectorSource = (source.parser || supportedParsers.get(source.format)) != undefined;
294✔
108
        this.networkOptions = source.networkOptions || { crossOrigin: 'anonymous' };
294✔
109
        this.attribution = source.attribution;
294✔
110
        /** @type {Promise<any>} */
294✔
111
        this.whenReady = Promise.resolve();
294✔
112
        this._featuresCaches = {};
294✔
113
        if (source.extent && !(source.extent.isExtent)) {
294✔
114
            this.extent = new Extent(this.crs).setFromExtent(source.extent);
6✔
115
        } else {
294✔
116
            this.extent = source.extent;
284✔
117
        }
284✔
118
    }
294✔
119

2✔
120
    handlingError(err) {
2✔
121
        if (Error.isError(err)) { throw err; }
6!
UNCOV
122
        throw new Error(err);
×
UNCOV
123
    }
×
124

2✔
125
    /**
2✔
126
     * Generates an url from an extent. This url is a link to fetch the
2✔
127
     * resources inside the extent.
2✔
128
     *
2✔
129
     * @param {Extent} extent - Extent to convert in url.
2✔
130
     * @returns {string} The URL constructed from the extent.
2✔
131
     */
2✔
132
    // eslint-disable-next-line
2✔
133
    urlFromExtent(extent) {
2✔
134
        throw new Error('In extended Source, you have to implement the method urlFromExtent!');
2✔
135
    }
2✔
136

2✔
137
    getDataKey(extent) {
2✔
138
        return `z${extent.zoom}r${extent.row}c${extent.col}`;
56✔
139
    }
56✔
140

2✔
141
    /**
2✔
142
     * Load  data from cache or Fetch/Parse data.
2✔
143
     * The loaded data is a Feature or Texture.
2✔
144
     *
2✔
145
     * @param      {Extent}  extent   extent requested parsed data.
2✔
146
     * @param      {FeatureBuildingOptions|Layer}  out     The feature returned options
2✔
147
     * @returns     {FeatureCollection|Texture}  The parsed data.
2✔
148
     */
2✔
149
    loadData(extent, out) {
2✔
150
        const cache = this._featuresCaches[out.crs];
16✔
151
        const key = this.getDataKey(extent);
16✔
152
        // try to get parsed data from cache
16✔
153
        let features = cache.get(key);
16✔
154
        if (!features) {
16✔
155
            // otherwise fetch/parse the data
16✔
156
            features = this.fetcher(this.urlFromExtent(extent), this.networkOptions)
16✔
157
                .then(file => this.parser(file, { out, in: this, extent }))
16✔
158
                .catch(err => this.handlingError(err));
16✔
159

16✔
160
            cache.set(key, features);
16✔
161
        }
16✔
162
        return features;
16✔
163
    }
16✔
164

2✔
165
    /**
2✔
166
     * Called when layer added.
2✔
167
     *
2✔
168
     * @param {object} options
2✔
169
     */
2✔
170
    onLayerAdded(options) {
2✔
171
        // Added new cache by crs
190✔
172
        if (!this._featuresCaches[options.out.crs]) {
190✔
173
            // Cache feature only if it's vector data, the feature are cached in source.
174✔
174
            // It's not necessary to cache raster in Source,
174✔
175
            // because it's already cached on layer.
174✔
176
            this._featuresCaches[options.out.crs] = this.isVectorSource ? new LRUCache({ max: 500 }) : noCache;
174✔
177
        }
174✔
178
    }
190✔
179

2✔
180
    /**
2✔
181
     * Called when layer removed.
2✔
182
     *
2✔
183
     * @param {options}  [options={}] options
2✔
184
     */
2✔
185
    onLayerRemoved(options = {}) {
2✔
186
        // delete unused cache
6✔
187
        const unusedCache = this._featuresCaches[options.unusedCrs];
6✔
188
        if (unusedCache) {
6✔
189
            unusedCache.clear();
2✔
190
            delete this._featuresCaches[options.unusedCrs];
2✔
191
        }
2✔
192
    }
6✔
193

2✔
194
    /**
2✔
195
     * Determines whether this source has data intersecting the given extent.
2✔
196
     *
2✔
197
     * If the source has no defined extent, it is assumed to cover all areas and
2✔
198
     * this method always returns `true`. Otherwise, the source's extent is
2✔
199
     * tested for intersection with the provided extent.
2✔
200
     *
2✔
201
     * @param {Extent|Tile} extentOrTile - The extent or tile to test against.
2✔
202
     *
2✔
203
     * @returns {boolean} `true` if the source has data for the
2✔
204
     * given extent, `false` otherwise.
2✔
205
     */
2✔
206
    hasData(extentOrTile) {
2✔
207
        return this.extent ? this.extent.intersectsExtent(extentOrTile.isExtent ? extentOrTile : extentOrTile.toExtent(this.crs)) : true;
40!
208
    }
40✔
209
}
2✔
210

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

© 2026 Coveralls, Inc