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

iTowns / itowns / 25318013074

04 May 2026 12:06PM UTC coverage: 88.397% (+0.002%) from 88.395%
25318013074

Pull #2756

github

web-flow
Merge e473e5a9c into 74811a183
Pull Request #2756: Feat/tms and pbf

2842 of 3662 branches covered (77.61%)

Branch coverage included in aggregate %.

55 of 75 new or added lines in 9 files covered. (73.33%)

2 existing lines in 1 file now uncovered.

28882 of 32226 relevant lines covered (89.62%)

948.1 hits per line

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

98.74
/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 Cache from 'Core/Scheduler/Cache';
1✔
12
import { LRUCache } from 'lru-cache';
1✔
13

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

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

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

1✔
36
let uid = 0;
1✔
37

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

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

121✔
102
        this.uid = uid++;
121✔
103

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

1✔
121
    handlingError(err) {
1✔
122
        if (Error.isError(err)) { throw err; }
3!
UNCOV
123
        throw new Error(err);
×
UNCOV
124
    }
×
125

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

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

1✔
139
    getDataKey(extent) {
1✔
140
        return `z${extent.zoom}r${extent.row}c${extent.col}`;
28✔
141
    }
28✔
142

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

8✔
162
            cache.set(key, features);
8✔
163
        }
8✔
164
        return features;
8✔
165
    }
8✔
166

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

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

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

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

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