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

iTowns / itowns / 4531193493

pending completion
4531193493

Pull #2006

github

GitHub
Merge 8837dad06 into b5123f51b
Pull Request #2006: Rework style

3267 of 5027 branches covered (64.99%)

Branch coverage included in aggregate %.

121 of 121 new or added lines in 10 files covered. (100.0%)

7008 of 8686 relevant lines covered (80.68%)

1494.54 hits per line

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

80.24
/src/Parser/VectorTileParser.js
1
import { Vector2, Vector3 } from 'three';
1✔
2
import Protobuf from 'pbf';
1✔
3
import { VectorTile } from '@mapbox/vector-tile';
1✔
4
import { globalExtentTMS } from 'Core/Geographic/Extent';
1✔
5
import { FeatureCollection, FEATURE_TYPES } from 'Core/Feature';
1✔
6
import { deprecatedParsingOptionsToNewOne } from 'Core/Deprecated/Undeprecator';
1✔
7

8
const worldDimension3857 = globalExtentTMS.get('EPSG:3857').planarDimensions();
1✔
9
const globalExtent = new Vector3(worldDimension3857.x, worldDimension3857.y, 1);
1✔
10
const lastPoint = new Vector2();
1✔
11
const firstPoint = new Vector2();
1✔
12

13
// Classify option, it allows to classify a full polygon and its holes.
14
// Each polygon with its holes are in one FeatureGeometry.
15
// A polygon is determined by its clockwise direction and the holes are in the opposite direction.
16
// Clockwise direction is determined by Shoelace formula https://en.wikipedia.org/wiki/Shoelace_formula
17
// Draw polygon with canvas doesn't need to classify however it is necessary for meshs.
18
function vtFeatureToFeatureGeometry(vtFeature, feature, classify = false) {
1!
19
    let geometry = feature.bindNewGeometry();
1✔
20
    const isPolygon = feature.type === FEATURE_TYPES.POLYGON;
1✔
21
    classify = classify && isPolygon;
1!
22

23
    geometry.properties = vtFeature.properties;
1✔
24
    const pbf = vtFeature._pbf;
1✔
25
    pbf.pos = vtFeature._geometry;
1✔
26

27
    const end = pbf.readVarint() + pbf.pos;
1✔
28
    let cmd = 1;
1✔
29
    let length = 0;
1✔
30
    let x = 0;
1✔
31
    let y = 0;
1✔
32
    let count = 0;
1✔
33
    let sum = 0;
1✔
34

35
    while (pbf.pos < end) {
1✔
36
        if (length <= 0) {
10✔
37
            const cmdLen = pbf.readVarint();
6✔
38
            cmd = cmdLen & 0x7;
6✔
39
            length = cmdLen >> 3;
6✔
40
        }
41

42
        length--;
10✔
43

44
        if (cmd === 1 || cmd === 2) {
10✔
45
            x += pbf.readSVarint();
8✔
46
            y += pbf.readSVarint();
8✔
47

48
            if (cmd === 1) {
8✔
49
                if (count) {
2✔
50
                    if (classify && sum > 0 && geometry.indices.length > 0) {
1!
51
                        feature.updateExtent(geometry);
×
52
                        geometry = feature.bindNewGeometry();
×
53
                        geometry.properties = vtFeature.properties;
×
54
                    }
55
                    geometry.closeSubGeometry(count, feature);
1✔
56
                    geometry.getLastSubGeometry().ccw = sum < 0;
1✔
57
                }
58
                count = 0;
2✔
59
                sum = 0;
2✔
60
            }
61
            count++;
8✔
62
            geometry.pushCoordinatesValues(feature, x, y);
8✔
63
            if (count == 1) {
8✔
64
                firstPoint.set(x, y);
2✔
65
                lastPoint.set(x, y);
2✔
66
            } else if (isPolygon && count > 1) {
6!
67
                sum += (lastPoint.x - x) * (lastPoint.y + y);
6✔
68
                lastPoint.set(x, y);
6✔
69
            }
70
        } else if (cmd === 7) {
2!
71
            if (count) {
2!
72
                count++;
2✔
73
                geometry.pushCoordinatesValues(feature, firstPoint.x, firstPoint.y);
2✔
74
                if (isPolygon) {
2!
75
                    sum += (lastPoint.x - firstPoint.x) * (lastPoint.y + firstPoint.y);
2✔
76
                }
77
            }
78
        } else {
79
            throw new Error(`unknown command ${cmd}`);
×
80
        }
81
    }
82

83
    if (count) {
1!
84
        if (classify && sum > 0 && geometry.indices.length > 0) {
1!
85
            feature.updateExtent(geometry);
×
86
            geometry = feature.bindNewGeometry();
×
87
            geometry.properties = vtFeature.properties;
×
88
        }
89
        geometry.closeSubGeometry(count, feature);
1✔
90
        geometry.getLastSubGeometry().ccw = sum < 0;
1✔
91
    }
92
    feature.updateExtent(geometry);
1✔
93
}
94

95
function readPBF(file, options) {
96
    options.out = options.out || {};
3!
97
    const vectorTile = new VectorTile(new Protobuf(file));
3✔
98
    const sourceLayers = Object.keys(vectorTile.layers);
3✔
99

100
    if (sourceLayers.length < 1) {
3✔
101
        return;
1✔
102
    }
103

104
    // x,y,z tile coordinates
105
    const x = file.extent.col;
2✔
106
    const z = file.extent.zoom;
2✔
107
    // We need to move from TMS to Google/Bing/OSM coordinates
108
    // https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates/
109
    // Only if the layer.origin is top
110
    const y = options.in.isInverted ? file.extent.row : (1 << z) - file.extent.row - 1;
2!
111

112
    const collection = new FeatureCollection(options.out);
2✔
113

114
    const vFeature = vectorTile.layers[sourceLayers[0]];
2✔
115
    // TODO: verify if size is correct because is computed with only one feature (vFeature).
116
    const size = vFeature.extent * 2 ** z;
2✔
117
    const center = -0.5 * size;
2✔
118

119
    collection.scale.set(globalExtent.x / size, -globalExtent.y / size, 1);
2✔
120
    collection.position.set(vFeature.extent * x + center, vFeature.extent * y + center, 0).multiply(collection.scale);
2✔
121
    collection.updateMatrixWorld();
2✔
122

123
    sourceLayers.forEach((layer_id) => {
6✔
124
        if (!options.in.layers[layer_id]) { return; }
2✔
125

126
        const sourceLayer = vectorTile.layers[layer_id];
1✔
127

128
        for (let i = sourceLayer.length - 1; i >= 0; i--) {
1✔
129
            const vtFeature = sourceLayer.feature(i);
1✔
130
            const layers = options.in.layers[layer_id].filter(l => l.filterExpression.filter({ zoom: z }, vtFeature) && z >= l.zoom.min && z < l.zoom.max);
1✔
131
            let feature;
1✔
132

133
            for (const layer of layers) {
1✔
134
                if (!feature) {
1!
135
                    feature = collection.requestFeatureById(layer.id, vtFeature.type - 1);
1✔
136
                    feature.id = layer.id;
1✔
137
                    feature.order = layer.order;
1✔
138
                    feature.hasRawElevationData = false;
1✔
139
                    // feature.style = options.in.styles[feature.id];
140
                    vtFeature.properties.style = options.in.styles[feature.id];
1✔
141
                    vtFeatureToFeatureGeometry(vtFeature, feature);
1✔
142
                } else if (!collection.features.find(f => f.id === layer.id)) {
×
143
                    feature = collection.newFeatureByReference(feature);
×
144
                    feature.id = layer.id;
×
145
                    feature.order = layer.order;
×
146
                    feature.style = options.in.styles[feature.id];
×
147
                    feature.style.isExtraStyle = true;
×
148
                    feature.hasExtraStyle = feature.id;
×
149
                }
150
            }
151
        }
152
    });
153

154
    collection.removeEmptyFeature();
2✔
155
    // TODO Some vector tiles are already sorted
156
    collection.features.sort((a, b) => a.order - b.order);
2✔
157
    // TODO verify if is needed to updateExtent for previous features.
158
    collection.updateExtent();
2✔
159
    collection.extent = file.extent;
2✔
160
    collection.isInverted = options.in.isInverted;
2✔
161
    return Promise.resolve(collection);
2✔
162
}
163

164
/**
165
 * @module VectorTileParser
166
 */
167
export default {
1✔
168
    /**
169
     * Parse a vector tile file and return a [Feature]{@link module:GeoJsonParser.Feature}
170
     * or an array of Features. While multiple formats of vector tile are
171
     * available, the only one supported for the moment is the
172
     * [Mapbox Vector Tile]{@link https://www.mapbox.com/vector-tiles/specification/}.
173
     *
174
     * @param {ArrayBuffer} file - The vector tile file to parse.
175
     *
176
     * @param {ParsingOptions} options - Options controlling the parsing {@link ParsingOptions}.
177
     *
178
     * @param {InformationsData} options.in - Object containing all styles,
179
     * layers and informations data, see {@link InformationsData}.
180
     *
181
     * @param {Object} options.in.Styles - Object containing subobject with
182
     * informations on a specific style layer. Styles available is by `layer.id` and by zoom.
183
     *
184
     * @param {Object} options.in.layers - Object containing subobject with
185
     *
186
     * @param {FeatureBuildingOptions} options.out - options indicates how the features should be built,
187
     * see {@link FeatureBuildingOptions}.
188
     *
189
     * @return {Promise} A Promise resolving with a Feature or an array a
190
     * Features.
191
     */
192
    parse(file, options) {
193
        options = deprecatedParsingOptionsToNewOne(options);
3✔
194
        return Promise.resolve(readPBF(file, options));
3✔
195
    },
196
};
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