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

iTowns / itowns / 19769738429

28 Nov 2025 04:52PM UTC coverage: 87.243% (-0.6%) from 87.839%
19769738429

Pull #2609

github

web-flow
Merge ab558ea29 into 628f03ec1
Pull Request #2609: Simplification of the loading process for rasterized tiles and mesh tiles

2665 of 3441 branches covered (77.45%)

Branch coverage included in aggregate %.

231 of 277 new or added lines in 21 files covered. (83.39%)

288 existing lines in 16 files now uncovered.

27617 of 31269 relevant lines covered (88.32%)

1242.71 hits per line

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

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

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

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

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

1✔
35
let uid = 0;
1✔
36

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

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

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

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

1✔
120
    handlingError(err) {
1✔
121
        throw new Error(err);
2✔
122
    }
2✔
123

1✔
124
    /**
1✔
125
     * Generates an url from an extent. This url is a link to fetch the
1✔
126
     * resources inside the extent.
1✔
127
     *
1✔
128
     * @param {Extent} extent - Extent to convert in url.
1✔
129

1✔
130
     * @return {string} The URL constructed from the extent.
1✔
131
     */
1✔
132
    // eslint-disable-next-line
1✔
133
    urlFromExtent(extent) {
1✔
134
        throw new Error('In extended Source, you have to implement the method urlFromExtent!');
1✔
135
    }
1✔
136

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

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

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

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

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

1✔
194
    /**
1✔
195
     * Tests if an extent is inside the source limits.
1✔
196
     *
1✔
197
     * @param {Extent} extent - Extent to test.
1✔
198

1✔
199
     * @return {boolean} True if the extent is inside the limit, false otherwise.
1✔
200
     */
1✔
201
    // eslint-disable-next-line
1✔
202
    extentInsideLimit(extent) {
1✔
UNCOV
203
        throw new Error('In extented Source, you have to implement the method extentInsideLimit!');
×
UNCOV
204
    }
×
205

1✔
206
    // eslint-disable-next-line
1✔
207
    anyVisibleData(extent) {
1✔
208
        throw new Error('In extented Source, you have to implement the method isVisibleData!');
1✔
209
    }
1✔
210
}
1✔
211

1✔
212
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

© 2026 Coveralls, Inc