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

iTowns / itowns / 21872390968

10 Feb 2026 04:02PM UTC coverage: 87.577% (-0.5%) from 88.114%
21872390968

Pull #2609

github

web-flow
Merge 52df4ac5c into 4dca3d483
Pull Request #2609: Simplification of the loading process for rasterized tiles and mesh tiles

2699 of 3474 branches covered (77.69%)

Branch coverage included in aggregate %.

240 of 286 new or added lines in 20 files covered. (83.92%)

279 existing lines in 14 files now uncovered.

27925 of 31494 relevant lines covered (88.67%)

937.01 hits per line

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

90.15
/packages/Main/src/Source/WFSSource.js
1
import Source from 'Source/Source';
1✔
2
import URLBuilder from 'Provider/URLBuilder';
1✔
3
import { Extent } from '@itowns/geographic';
1✔
4

1✔
5
const _extent = new Extent('EPSG:4326');
1✔
6

1✔
7
/**
1✔
8
 * An object defining the source of resources to get from a
1✔
9
 * [WFS](http://www.opengeospatial.org/standards/wfs) server. It inherits
1✔
10
 * from {@link Source}.
1✔
11
 *
1✔
12
 * @extends Source
1✔
13
 *
1✔
14
 * @property {boolean} isWFSSource - Used to checkout whether this source is a
1✔
15
 * WFSSource. Default is true. You should not change this, as it is used
1✔
16
 * internally for optimisation.
1✔
17
 * @property {string} typeName - The name of the feature to get, used in the
1✔
18
 * generation of the url.
1✔
19
 * @property {string} version - The version of the WFS server to request on.
1✔
20
 * Default value is '2.0.2'.
1✔
21
 * @property {Object} zoom - Object containing the minimum and maximum values of
1✔
22
 * the level, to zoom in the source.
1✔
23
 * @property {number} zoom.min - The minimum level of the source. Default value
1✔
24
 * is 0.
1✔
25
 * @property {number} zoom.max - The maximum level of the source. Default value
1✔
26
 * is 21.
1✔
27
 * @property {string} bboxDigits - The bbox digits precision used in URL
1✔
28
 * @property {Object} vendorSpecific - An object containing vendor specific
1✔
29
 * parameters. See for example a [list of these parameters for GeoServer]{@link
1✔
30
 * https://docs.geoserver.org/latest/en/user/services/wfs/vendor.html}. This
1✔
31
 * object is read simply with the `key` being the name of the parameter and
1✔
32
 * `value` being the value of the parameter. If used, this property should be
1✔
33
 * set in the constructor parameters.
1✔
34
 *
1✔
35
 * @example
1✔
36
 * // Add color layer with WFS source
1✔
37
 * // Create the source
1✔
38
 * const wfsSource = new itowns.WFSSource({
1✔
39
 *     url: 'https://data.geopf.fr/wfs/ows?',
1✔
40
 *     version: '2.0.0',
1✔
41
 *     typeName: 'BDTOPO_BDD_WLD_WGS84G:bati_remarquable',
1✔
42
 *     crs: 'EPSG:4326',
1✔
43
 *     extent: {
1✔
44
 *         west: 4.568,
1✔
45
 *         east: 5.18,
1✔
46
 *         south: 45.437,
1✔
47
 *         north: 46.03,
1✔
48
 *     },
1✔
49
 *     zoom: { min: 14, max: 14 },
1✔
50
 *     format: 'application/json',
1✔
51
 * });
1✔
52
 *
1✔
53
 * // Create the layer
1✔
54
 * const colorlayer = new itowns.ColorLayer('color_build', {
1✔
55
 *     style: {
1✔
56
 *         fill: 'red',
1✔
57
 *         fillOpacity: 0.5,
1✔
58
 *         stroke: 'white',
1✔
59
 *     },
1✔
60
 *     source: wfsSource,
1✔
61
 * });
1✔
62
 *
1✔
63
 * // Add the layer
1✔
64
 * view.addLayer(colorlayer);
1✔
65
 *
1✔
66
 * @example
1✔
67
 * // Add geometry layer with WFS source
1✔
68
 * // Create the source
1✔
69
 * const wfsSource = new itowns.WFSSource({
1✔
70
 *     url: 'https://data.geopf.fr/wfs/ows?',
1✔
71
 *     version: '2.0.0',
1✔
72
 *     typeName: 'BDTOPO_BDD_WLD_WGS84G:bati_remarquable',
1✔
73
 *     crs: 'EPSG:4326',
1✔
74
 *     extent: {
1✔
75
 *         west: 4.568,
1✔
76
 *         east: 5.18,
1✔
77
 *         south: 45.437,
1✔
78
 *         north: 46.03,
1✔
79
 *     },
1✔
80
 *     zoom: { min: 14, max: 14 },
1✔
81
 *     format: 'application/json',
1✔
82
 * });
1✔
83
 *
1✔
84
 * // Create the layer
1✔
85
 * const geometryLayer = new itowns.FeatureGeometryLayer('mesh_build', {
1✔
86
 *     style: {
1✔
87
 *         fill: {
1✔
88
 *             color: new itowns.THREE.Color(0xffcc00),
1✔
89
 *             base_altitude: (p) => p.altitude,
1✔
90
 *             extrusion_height: (p) => p.height,
1✔
91
 *         }
1✔
92
 *     },
1✔
93
 *     source: wfsSource,
1✔
94
 *     zoom: { min: 14 },
1✔
95
 * };
1✔
96
 *
1✔
97
 * // Add the layer
1✔
98
 * view.addLayer(geometryLayer);
1✔
99
 */
1✔
100
class WFSSource extends Source {
1✔
101
    /**
1✔
102
     * @param {Object} source - An object that can contain all properties of a
1✔
103
     * WFSSource and {@link Source}. `url`, `typeName` and `crs` are
1✔
104
     * mandatory.
1✔
105
     */
1✔
106
    constructor(source) {
1✔
107
        if (source.projection) {
7!
108
            console.warn('WFSSource projection parameter is deprecated, use crs instead.');
×
109
            source.crs = source.crs || source.projection;
×
110
        }
×
111
        if (!source.typeName) {
7✔
112
            throw new Error('source.typeName is required in wfs source.');
1✔
113
        }
1✔
114

6✔
115
        if (!source.crs) {
7✔
116
            throw new Error('source.crs is required in wfs source');
1✔
117
        }
1✔
118

5✔
119
        source.format = source.format || 'application/json';
7✔
120

7✔
121
        super(source);
7✔
122

7✔
123
        this.isWFSSource = true;
7✔
124
        this.typeName = source.typeName;
7✔
125
        this.version = source.version || '2.0.2';
7✔
126
        this.bboxDigits = source.bboxDigits;
7✔
127
        this.zoom = { min: 0, max: Infinity };
7✔
128

7✔
129
        const urlObj = new URL(source.url);
7✔
130
        urlObj.searchParams.set('SERVICE', 'WFS');
7✔
131
        urlObj.searchParams.set('REQUEST', 'GetFeature');
7✔
132
        urlObj.searchParams.set('typeName', this.typeName);
7✔
133
        urlObj.searchParams.set('VERSION', this.version);
7✔
134
        urlObj.searchParams.set('SRSNAME', this.crs);
7✔
135
        urlObj.searchParams.set('outputFormat', this.format);
7✔
136
        urlObj.searchParams.set('BBOX', `%bbox,${this.crs}`);
7✔
137

7✔
138
        this.vendorSpecific = source.vendorSpecific;
7✔
139
        for (const name in this.vendorSpecific) {
7✔
140
            if (Object.prototype.hasOwnProperty.call(this.vendorSpecific, name)) {
9✔
141
                urlObj.searchParams.set(name, this.vendorSpecific[name]);
9✔
142
            }
9✔
143
        }
9✔
144

3✔
145
        this.url = decodeURIComponent(urlObj.toString());
3✔
146
    }
3✔
147

1✔
148
    handlingError(err) {
1✔
149
        if (err.response && err.response.status == 400) {
1!
150
            return err.response.text().then((text) => {
×
151
                const getCapUrl = `${this.url}SERVICE=WFS&REQUEST=GetCapabilities&VERSION=${this.version}`;
×
152
                const xml = new DOMParser().parseFromString(text, 'application/xml');
×
153
                const errorElem = xml.querySelector('Exception');
×
154
                const errorCode = errorElem.getAttribute('exceptionCode');
×
155
                const errorMessage = errorElem.querySelector('ExceptionText').textContent;
×
156
                console.error(`Source ${this.typeName}: bad request when fetching data. Server says: "${errorCode}: ${errorMessage}". \nReviewing ${getCapUrl} may help.`, err);
×
157
            });
×
158
        }
×
159
        return super.handlingError(err);
1✔
160
    }
1✔
161

1✔
162
    getDataKey(extent) {
1✔
163
        if (extent.isTile) {
2✔
164
            return super.getDataKey(extent);
1✔
165
        } else {
1✔
166
            return `z${extent.zoom}s${extent.south}w${extent.west}`;
1✔
167
        }
1✔
168
    }
2✔
169

1✔
170
    urlFromExtent(extentOrTile) {
1✔
171
        const extent = extentOrTile.isExtent ?
1✔
172
            extentOrTile.as(this.crs, _extent) :
1!
173
            extentOrTile.toExtent(this.crs, _extent);
1✔
174
        return URLBuilder.bbox(extent, this);
1✔
175
    }
1✔
176

1✔
177
    extentInsideLimit(extent) {
1✔
UNCOV
178
        return this.extent.intersectsExtent(extent);
×
UNCOV
179
    }
×
180

1✔
181
    anyVisibleData(e) {
1✔
NEW
182
        return this.extentInsideLimit(e);
×
NEW
183
    }
×
184
}
1✔
185

1✔
186
export default WFSSource;
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