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

iTowns / itowns / 23500884659

24 Mar 2026 04:35PM UTC coverage: 87.727% (-0.5%) from 88.182%
23500884659

Pull #2609

github

web-flow
Merge 06e93474d into e3f0b8bae
Pull Request #2609: Simplification of the loading process for rasterized tiles and mesh tiles

2709 of 3488 branches covered (77.67%)

Branch coverage included in aggregate %.

453 of 496 new or added lines in 20 files covered. (91.33%)

273 existing lines in 11 files now uncovered.

28185 of 31728 relevant lines covered (88.83%)

1866.56 hits per line

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

23.24
/packages/Main/src/Converter/Feature2Texture.js
1
import * as THREE from 'three';
2✔
2
import { FEATURE_TYPES } from 'Core/Feature';
2✔
3
import { Extent, Coordinates } from '@itowns/geographic';
2✔
4
import Style, { StyleContext } from 'Core/Style';
2✔
5

2✔
6
const defaultStyle = new Style();
2✔
7
const context = new StyleContext();
2✔
8
let style;
2✔
9

2✔
10
/**
2✔
11
 * Draw polygon (contour, line edge and fill) based on feature vertices into canvas
2✔
12
 * using the given style(s). Several styles will re-draws the polygon each one with
2✔
13
 * a different style.
2✔
14
 * @param      {CanvasRenderingContext2D} ctx - canvas' 2D rendering context.
2✔
15
 * @param      {Number[]} vertices - All the vertices of the Feature.
2✔
16
 * @param      {Object[]} indices - Contains the indices that define the geometry.
2✔
17
 * Objects stored in this array have two properties, an `offset` and a `count`.
2✔
18
* The offset is related to the overall number of vertices in the Feature.
2✔
19
 * @param      {Number} size - The size of the feature.
2✔
20
 * @param      {Number} extent - The extent.
2✔
21
 * @param      {Number} invCtxScale - The ration to scale line width and radius circle.
2✔
22
 * @param      {Boolean} canBeFilled - true if feature.type == FEATURE_TYPES.POLYGON
2✔
23
 */
2✔
UNCOV
24
function drawPolygon(ctx, vertices, indices = [{ offset: 0, count: 1 }], size, extent, invCtxScale, canBeFilled) {
×
UNCOV
25
    if (vertices.length === 0) {
×
26
        return;
×
27
    }
×
UNCOV
28
    // build contour
×
UNCOV
29
    const path = new Path2D();
×
UNCOV
30

×
UNCOV
31
    for (const indice of indices) {
×
UNCOV
32
        if (indice.extent && Extent.intersectsExtent(indice.extent, extent)) {
×
UNCOV
33
            const offset = indice.offset * size;
×
UNCOV
34
            const count = offset + indice.count * size;
×
UNCOV
35
            path.moveTo(vertices[offset], vertices[offset + 1]);
×
UNCOV
36
            for (let j = offset + size; j < count; j += size) {
×
UNCOV
37
                path.lineTo(vertices[j], vertices[j + 1]);
×
UNCOV
38
            }
×
UNCOV
39
        }
×
UNCOV
40
    }
×
UNCOV
41
    style.applyToCanvasPolygon(ctx, path, invCtxScale, canBeFilled);
×
UNCOV
42
}
×
43

2✔
UNCOV
44
function drawPoint(ctx, x, y, invCtxScale) {
×
UNCOV
45
    ctx.beginPath();
×
UNCOV
46
    const opacity = style.point.opacity == undefined ? 1.0 : style.point.opacity;
×
UNCOV
47
    if (opacity !== ctx.globalAlpha) {
×
UNCOV
48
        ctx.globalAlpha = opacity;
×
UNCOV
49
    }
×
UNCOV
50

×
UNCOV
51
    ctx.arc(x, y, (style.point.radius || 3.0) * invCtxScale, 0, 2 * Math.PI, false);
×
UNCOV
52
    if (style.point.color) {
×
UNCOV
53
        ctx.fillStyle = style.point.color;
×
UNCOV
54
        ctx.fill();
×
UNCOV
55
    }
×
UNCOV
56
    if (style.point.line) {
×
UNCOV
57
        ctx.lineWidth = (style.point.width || 1.0) * invCtxScale;
×
UNCOV
58
        ctx.strokeStyle = style.point.line;
×
UNCOV
59
        ctx.stroke();
×
UNCOV
60
    }
×
UNCOV
61
}
×
62

2✔
63
const coord = new Coordinates('EPSG:4326', 0, 0, 0);
2✔
64

2✔
UNCOV
65
function drawFeature(ctx, feature, extent, invCtxScale) {
×
UNCOV
66
    const extentDim = extent.planarDimensions();
×
UNCOV
67
    const scaleRadius = extentDim.x / ctx.canvas.width;
×
UNCOV
68

×
UNCOV
69
    for (const geometry of feature.geometries) {
×
UNCOV
70
        if (Extent.intersectsExtent(geometry.extent, extent)) {
×
UNCOV
71
            context.setGeometry(geometry);
×
UNCOV
72
            if (style.zoom.min > style.context.zoom || style.zoom.max <= style.context.zoom) {
×
73
                return;
×
74
            }
×
UNCOV
75

×
UNCOV
76
            if (
×
UNCOV
77
                feature.type === FEATURE_TYPES.POINT && style.point
×
UNCOV
78
            ) {
×
UNCOV
79
                // cross multiplication to know in the extent system the real size of
×
UNCOV
80
                // the point
×
UNCOV
81
                const px = (Math.round(style.point.radius * invCtxScale) || 3 * invCtxScale) * scaleRadius;
×
UNCOV
82
                for (const indice of geometry.indices) {
×
UNCOV
83
                    const offset = indice.offset * feature.size;
×
UNCOV
84
                    const count = offset + indice.count * feature.size;
×
UNCOV
85
                    for (let j = offset; j < count; j += feature.size) {
×
UNCOV
86
                        coord.setFromArray(feature.vertices, j);
×
UNCOV
87
                        if (extent.isPointInside(coord, px)) {
×
UNCOV
88
                            drawPoint(ctx, feature.vertices[j], feature.vertices[j + 1], invCtxScale);
×
UNCOV
89
                        }
×
UNCOV
90
                    }
×
UNCOV
91
                }
×
UNCOV
92
            } else {
×
UNCOV
93
                drawPolygon(ctx, feature.vertices, geometry.indices, feature.size, extent, invCtxScale, (feature.type == FEATURE_TYPES.POLYGON));
×
UNCOV
94
            }
×
UNCOV
95
        }
×
UNCOV
96
    }
×
UNCOV
97
}
×
98

2✔
99
const origin = new THREE.Vector3();
2✔
100
const dimension = new THREE.Vector3(0, 0, 1);
2✔
101
const scale = new THREE.Vector3();
2✔
102
const quaternion = new THREE.Quaternion();
2✔
103
const world2texture = new THREE.Matrix4();
2✔
104
const feature2texture = new THREE.Matrix4();
2✔
105
const worldTextureOrigin = new THREE.Vector3();
2✔
106

2✔
107
const featureExtent = new Extent('EPSG:4326', 0, 0, 0, 0);
2✔
108

2✔
109
export default {
2✔
110
    // backgroundColor is a THREE.Color to specify a color to fill the texture
2✔
111
    // with, given there is no feature passed in parameter
2✔
112
    createTextureFromFeature(collection, extent, sizeTexture, layerStyle, backgroundColor) {
2✔
UNCOV
113
        style = layerStyle || defaultStyle;
×
UNCOV
114
        style.setContext(context);
×
UNCOV
115
        let texture;
×
UNCOV
116

×
UNCOV
117
        if (collection) {
×
UNCOV
118
            // A texture is instancied drawn canvas
×
UNCOV
119
            // origin and dimension are used to transform the feature's coordinates to canvas's space
×
UNCOV
120
            extent.planarDimensions(dimension);
×
UNCOV
121
            const c = document.createElement('canvas');
×
UNCOV
122

×
UNCOV
123
            coord.crs = extent.crs;
×
UNCOV
124

×
UNCOV
125
            c.width = sizeTexture;
×
UNCOV
126
            c.height = sizeTexture;
×
UNCOV
127
            const ctx = c.getContext('2d', { willReadFrequently: true });
×
UNCOV
128
            if (backgroundColor) {
×
129
                ctx.fillStyle = backgroundColor.getStyle();
×
130
                ctx.fillRect(0, 0, sizeTexture, sizeTexture);
×
131
            }
×
UNCOV
132

×
UNCOV
133
            // Documentation needed !!
×
UNCOV
134
            ctx.globalCompositeOperation = layerStyle.globalCompositeOperation || 'source-over';
×
UNCOV
135
            ctx.imageSmoothingEnabled = false;
×
UNCOV
136
            ctx.lineJoin = 'round';
×
UNCOV
137

×
UNCOV
138
            // transform extent to feature projection
×
UNCOV
139
            extent.as(collection.crs, featureExtent);
×
UNCOV
140
            // transform extent to local system
×
UNCOV
141
            featureExtent.applyMatrix4(collection.matrixWorldInverse);
×
UNCOV
142

×
UNCOV
143
            // compute matrix transformation `world2texture` to convert coordinates to texture coordinates
×
UNCOV
144
            if (collection.isInverted) {
×
145
                worldTextureOrigin.set(extent.west, extent.north, 0);
×
146
                scale.set(ctx.canvas.width, -ctx.canvas.height, 1.0).divide(dimension);
×
UNCOV
147
            } else {
×
UNCOV
148
                worldTextureOrigin.set(extent.west, extent.south, 0);
×
UNCOV
149
                scale.set(ctx.canvas.width, ctx.canvas.height, 1.0).divide(dimension);
×
UNCOV
150
            }
×
UNCOV
151

×
UNCOV
152
            world2texture.compose(worldTextureOrigin.multiply(scale).negate(), quaternion, scale);
×
UNCOV
153

×
UNCOV
154
            // compute matrix transformation `feature2texture` to convert features coordinates to texture coordinates
×
UNCOV
155
            feature2texture.multiplyMatrices(world2texture, collection.matrixWorld);
×
UNCOV
156
            feature2texture.decompose(origin, quaternion, scale);
×
UNCOV
157

×
UNCOV
158
            ctx.setTransform(scale.x, 0, 0, scale.y, origin.x, origin.y);
×
UNCOV
159

×
UNCOV
160
            // to scale line width and radius circle
×
UNCOV
161
            const invCtxScale = Math.abs(1 / scale.x);
×
UNCOV
162

×
UNCOV
163
            context.setZoom(extent.zoom);
×
UNCOV
164

×
UNCOV
165
            // Draw the canvas
×
UNCOV
166
            for (const feature of collection.features) {
×
UNCOV
167
                context.setFeature(feature);
×
UNCOV
168
                drawFeature(ctx, feature, featureExtent, invCtxScale);
×
UNCOV
169
            }
×
UNCOV
170

×
UNCOV
171
            texture = new THREE.CanvasTexture(c);
×
UNCOV
172
            texture.flipY = collection.isInverted;
×
UNCOV
173
        } else if (backgroundColor) {
×
174
            const data = new Uint8Array(3);
×
175
            data[0] = backgroundColor.r * 255;
×
176
            data[1] = backgroundColor.g * 255;
×
177
            data[2] = backgroundColor.b * 255;
×
178
            texture = new THREE.DataTexture(data, 1, 1, THREE.RGBAFormat);
×
179
        } else {
×
180
            texture = new THREE.Texture();
×
181
        }
×
UNCOV
182

×
UNCOV
183
        return texture;
×
UNCOV
184
    },
×
185
};
2✔
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