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

iTowns / itowns / 4530996590

pending completion
4530996590

Pull #2006

github

GitHub
Merge 5d8e8a11d into b5123f51b
Pull Request #2006: Rework style

3265 of 5027 branches covered (64.95%)

Branch coverage included in aggregate %.

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

7003 of 8686 relevant lines covered (80.62%)

1494.54 hits per line

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

75.1
/src/Converter/Feature2Texture.js
1
import * as THREE from 'three';
1✔
2
import { FEATURE_TYPES } from 'Core/Feature';
1✔
3
import Extent from 'Core/Geographic/Extent';
1✔
4
import Coordinates from 'Core/Geographic/Coordinates';
1✔
5
import Style from '../Core/Style';
1✔
6

7
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1✔
8
const matrix = svg.createSVGMatrix();
1✔
9

10
function drawPolygon(ctx, vertices, indices = [{ offset: 0, count: 1 }], style = {}, size, extent, invCtxScale, canBeFilled) {
12!
11
    if (vertices.length === 0) {
3!
12
        return;
×
13
    }
14
    if (style.length) {
3!
15
        for (const s of style) {
×
16
            _drawPolygon(ctx, vertices, indices, s, size, extent, invCtxScale, canBeFilled);
×
17
        }
18
    } else {
19
        _drawPolygon(ctx, vertices, indices, style, size, extent, invCtxScale, canBeFilled);
3✔
20
    }
21
}
22

23
function _drawPolygon(ctx, vertices, indices, style, size, extent, invCtxScale, canBeFilled) {
9✔
24
    // build contour
25
    ctx.beginPath();
3✔
26
    for (const indice of indices) {
4✔
27
        if (indice.extent && Extent.intersectsExtent(indice.extent, extent)) {
4!
28
            const offset = indice.offset * size;
4✔
29
            const count = offset + indice.count * size;
4✔
30
            ctx.moveTo(vertices[offset], vertices[offset + 1]);
4✔
31
            for (let j = offset + size; j < count; j += size) {
4✔
32
                ctx.lineTo(vertices[j], vertices[j + 1]);
12✔
33
            }
34
        }
35
    }
36

37
    // draw line or edge of polygon
38
    if (style.stroke) {
3!
39
        strokeStyle(style, ctx, invCtxScale);
3✔
40
        ctx.stroke();
3✔
41
    }
42

43
    // fill polygon only
44
    if (canBeFilled && style.fill) {
3!
45
        fillStyle(style, ctx, invCtxScale);
3✔
46
        ctx.fill();
3✔
47
    }
48
}
49

50
function fillStyle(style, ctx, invCtxScale) {
51
    if (style.fill.pattern && ctx.fillStyle.src !== style.fill.pattern.src) {
3!
52
        ctx.fillStyle = ctx.createPattern(style.fill.pattern, 'repeat');
×
53
        if (ctx.fillStyle.setTransform) {
×
54
            ctx.fillStyle.setTransform(matrix.scale(invCtxScale));
×
55
        } else {
56
            console.warn('Raster pattern isn\'t completely supported on Ie and edge');
×
57
        }
58
    } else if (ctx.fillStyle !== style.fill.color) {
3✔
59
        ctx.fillStyle = style.fill.color;
1✔
60
    }
61
    if (style.fill.opacity !== ctx.globalAlpha) {
3!
62
        ctx.globalAlpha = style.fill.opacity;
3✔
63
    }
64
}
65

66
function strokeStyle(style, ctx, invCtxScale) {
67
    if (ctx.strokeStyle !== style.stroke.color) {
3✔
68
        ctx.strokeStyle = style.stroke.color;
1✔
69
    }
70
    const width = (style.stroke.width || 2.0) * invCtxScale;
3!
71
    if (ctx.lineWidth !== width) {
3✔
72
        ctx.lineWidth = width;
1✔
73
    }
74
    const alpha = style.stroke.opacity == undefined ? 1.0 : style.stroke.opacity;
3!
75
    if (alpha !== ctx.globalAlpha && typeof alpha == 'number') {
3!
76
        ctx.globalAlpha = alpha;
3✔
77
    }
78
    if (ctx.lineCap !== style.stroke.lineCap) {
3!
79
        ctx.lineCap = style.stroke.lineCap;
×
80
    }
81
    ctx.setLineDash(style.stroke.dasharray.map(a => a * invCtxScale * 2));
3✔
82
}
83

84
function drawPoint(ctx, x, y, style = {}, invCtxScale) {
1!
85
    ctx.beginPath();
1✔
86
    const opacity = style.point.opacity == undefined ? 1.0 : style.point.opacity;
1!
87
    if (opacity !== ctx.globalAlpha) {
1!
88
        ctx.globalAlpha = opacity;
1✔
89
    }
90

91
    ctx.arc(x, y, (style.point.radius || 3.0) * invCtxScale, 0, 2 * Math.PI, false);
1!
92
    if (style.point.color) {
1!
93
        ctx.fillStyle = style.point.color;
1✔
94
        ctx.fill();
1✔
95
    }
96
    if (style.point.line) {
1!
97
        ctx.lineWidth = (style.point.width || 1.0) * invCtxScale;
1✔
98
        ctx.strokeStyle = style.point.line;
1✔
99
        ctx.stroke();
1✔
100
    }
101
}
102

103
const coord = new Coordinates('EPSG:4326', 0, 0, 0);
1✔
104

105
function drawFeature(ctx, feature, extent, style, invCtxScale) {
12✔
106
    const extentDim = extent.planarDimensions();
2✔
107
    const scaleRadius = extentDim.x / ctx.canvas.width;
2✔
108
    const globals = { zoom: extent.zoom };
2✔
109

110
    for (const geometry of feature.geometries) {
4✔
111
        if (Extent.intersectsExtent(geometry.extent, extent)) {
4!
112
            const context = { globals, properties: () => geometry.properties };
4✔
113
            // const contextStyle = (geometry.properties.style || style).drawingStylefromContext(context);
114
            const styleConc = {
4✔
115
                fill: {
116
                    ...geometry.properties.style && geometry.properties.style.fill ? geometry.properties.style.fill : {},
12!
117
                    ...feature.style.isExtraStyle && feature.style.fill ? feature.style.fill : {},
8!
118
                    ...style.fill,
119
                },
120
                stroke: {
121
                    ...geometry.properties.style && geometry.properties.style.stroke ? geometry.properties.style.stroke : {},
12!
122
                    ...feature.style.isExtraStyle && feature.style.stroke ? feature.style.stroke : {},
8!
123
                    ...style.stroke,
124
                },
125
                point: {
126
                    ...geometry.properties.style && geometry.properties.style.point ? geometry.properties.style.point : {},
12!
127
                    ...feature.style.isExtraStyle && feature.style.point ? feature.style.point : {},
8!
128
                    ...style.point,
129
                },
130
            };
131

132
            const contextStyle = new Style(styleConc).drawingStylefromContext(context);
4✔
133

134
            if (contextStyle) {
4!
135
                if (
4✔
136
                    feature.type === FEATURE_TYPES.POINT && contextStyle.point
5✔
137
                ) {
3✔
138
                    // console.log('^^^drawPoint^^^');
139
                    // cross multiplication to know in the extent system the real size of
140
                    // the point
141
                    const px = (Math.round(contextStyle.point.radius * invCtxScale) || 3 * invCtxScale) * scaleRadius;
1!
142
                    for (const indice of geometry.indices) {
1✔
143
                        const offset = indice.offset * feature.size;
1✔
144
                        const count = offset + indice.count * feature.size;
1✔
145
                        for (let j = offset; j < count; j += feature.size) {
1✔
146
                            coord.setFromArray(feature.vertices, j);
1✔
147
                            if (extent.isPointInside(coord, px)) {
1!
148
                                drawPoint(ctx, feature.vertices[j], feature.vertices[j + 1], contextStyle, invCtxScale);
1✔
149
                            }
150
                        }
151
                    }
152
                } else {
153
                    drawPolygon(ctx, feature.vertices, geometry.indices, contextStyle, feature.size, extent, invCtxScale, (feature.type == FEATURE_TYPES.POLYGON));
3✔
154
                }
155
            }
156
        }
157
    }
158
}
159

160
const origin = new THREE.Vector3();
1✔
161
const dimension = new THREE.Vector3(0, 0, 1);
1✔
162
const scale = new THREE.Vector3();
1✔
163
const quaternion = new THREE.Quaternion();
1✔
164
const world2texture = new THREE.Matrix4();
1✔
165
const feature2texture = new THREE.Matrix4();
1✔
166
const worldTextureOrigin = new THREE.Vector3();
1✔
167

168
const featureExtent = new Extent('EPSG:4326', 0, 0, 0, 0);
1✔
169

170
export default {
1✔
171
    // backgroundColor is a THREE.Color to specify a color to fill the texture
172
    // with, given there is no feature passed in parameter
173
    createTextureFromFeature(collection, extent, sizeTexture, style = {}, backgroundColor) {
1!
174
        let texture;
175

176
        if (collection) {
3!
177
            // A texture is instancied drawn canvas
178
            // origin and dimension are used to transform the feature's coordinates to canvas's space
179
            extent.planarDimensions(dimension);
1✔
180
            const c = document.createElement('canvas');
1✔
181

182
            coord.crs = extent.crs;
1✔
183

184
            c.width = sizeTexture;
1✔
185
            c.height = sizeTexture;
1✔
186
            const ctx = c.getContext('2d');
1✔
187
            if (backgroundColor) {
1!
188
                ctx.fillStyle = backgroundColor.getStyle();
×
189
                ctx.fillRect(0, 0, sizeTexture, sizeTexture);
×
190
            }
191

192
            // Documentation needed !!
193
            ctx.globalCompositeOperation = style.globalCompositeOperation || 'source-over';
1✔
194
            ctx.imageSmoothingEnabled = false;
1✔
195
            ctx.lineJoin = 'round';
1✔
196

197
            // transform extent to feature projection
198
            extent.as(collection.crs, featureExtent);
1✔
199
            // transform extent to local system
200
            featureExtent.applyMatrix4(collection.matrixWorldInverse);
1✔
201

202
            // compute matrix transformation `world2texture` to convert coordinates to texture coordinates
203
            if (collection.isInverted) {
1!
204
                worldTextureOrigin.set(extent.west, extent.north, 0);
×
205
                scale.set(ctx.canvas.width, -ctx.canvas.height, 1.0).divide(dimension);
×
206
            } else {
207
                worldTextureOrigin.set(extent.west, extent.south, 0);
1✔
208
                scale.set(ctx.canvas.width, ctx.canvas.height, 1.0).divide(dimension);
1✔
209
            }
210

211
            world2texture.compose(worldTextureOrigin.multiply(scale).negate(), quaternion, scale);
1✔
212

213
            // compute matrix transformation `feature2texture` to convert features coordinates to texture coordinates
214
            feature2texture.multiplyMatrices(world2texture, collection.matrixWorld);
1✔
215
            feature2texture.decompose(origin, quaternion, scale);
1✔
216

217
            ctx.setTransform(scale.x, 0, 0, scale.y, origin.x, origin.y);
1✔
218

219
            // to scale line width and radius circle
220
            const invCtxScale = Math.abs(1 / scale.x);
1✔
221

222
            // Draw the canvas
223
            for (const feature of collection.features) {
2✔
224
                // drawFeature(ctx, feature, featureExtent, feature.style || style, invCtxScale);
225
                drawFeature(ctx, feature, featureExtent, style, invCtxScale);
2✔
226
            }
227

228
            texture = new THREE.CanvasTexture(c);
1✔
229
            texture.flipY = collection.isInverted;
1✔
230
        } else if (backgroundColor) {
×
231
            const data = new Uint8Array(3);
×
232
            data[0] = backgroundColor.r * 255;
×
233
            data[1] = backgroundColor.g * 255;
×
234
            data[2] = backgroundColor.b * 255;
×
235
            texture = new THREE.DataTexture(data, 1, 1, THREE.RGBFormat);
×
236
        } else {
237
            texture = new THREE.Texture();
×
238
        }
239

240
        return texture;
1✔
241
    },
242
};
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