diff --git a/package-lock.json b/package-lock.json index 50e60d2d78..5af9234473 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13967,8 +13967,7 @@ "@tmcw/togeojson": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/@tmcw/togeojson/-/togeojson-5.1.3.tgz", - "integrity": "sha512-X1/Z72MbkEjcInOqh9ht+Hr3xSxNgq+1VLO3ck1RKj0fqnlD47p+khH4nxu6k6mcGDYf82x6EAEWZ8Q8K8wXqQ==", - "requires": {} + "integrity": "sha512-X1/Z72MbkEjcInOqh9ht+Hr3xSxNgq+1VLO3ck1RKj0fqnlD47p+khH4nxu6k6mcGDYf82x6EAEWZ8Q8K8wXqQ==" }, "@tweenjs/tween.js": { "version": "18.6.4", @@ -14365,8 +14364,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", - "dev": true, - "requires": {} + "dev": true }, "@webpack-cli/info": { "version": "1.4.1", @@ -14381,8 +14379,7 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", - "dev": true, - "requires": {} + "dev": true }, "@xtuc/ieee754": { "version": "1.2.0", @@ -14422,15 +14419,13 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "add-stream": { "version": "1.0.0", @@ -14502,8 +14497,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "ansi-colors": { "version": "4.1.1", @@ -18465,8 +18459,7 @@ "version": "8.6.4", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.4.tgz", "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", - "dev": true, - "requires": {} + "dev": true }, "marked": { "version": "4.0.16", @@ -21746,8 +21739,7 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", - "dev": true, - "requires": {} + "dev": true }, "x-path": { "version": "0.0.2", diff --git a/src/Converter/Feature2Mesh.js b/src/Converter/Feature2Mesh.js index f0c5a1827b..9143dbc2ed 100644 --- a/src/Converter/Feature2Mesh.js +++ b/src/Converter/Feature2Mesh.js @@ -217,7 +217,6 @@ function addExtrudedPolygonSideFaces(indices, length, offset, count, isClockWise function featureToPoint(feature, options) { const ptsIn = feature.vertices; const normals = feature.normals; - const colors = new Uint8Array(ptsIn.length); const batchIds = options.batchId ? new Uint32Array(ptsIn.length / 3) : undefined; let featureId = 0; @@ -230,46 +229,29 @@ function featureToPoint(feature, options) { vertices = new Float32Array(ptsIn); } - const pointMaterialSize = []; - const globals = { point: true }; for (const geometry of feature.geometries) { - const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; - - const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); - const start = geometry.indices[0].offset; const count = geometry.indices[0].count; - fillColorArray(colors, count, toColor(style.point.color), start); if (batchIds) { const id = options.batchId(geometry.properties, featureId); fillBatchIdArray(id, batchIds, start, start + count); featureId++; } - const size = style.point.radius; - if (!pointMaterialSize.includes(size)) { - pointMaterialSize.push(size); - } } const geom = new THREE.BufferGeometry(); geom.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); - geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); if (batchIds) { geom.setAttribute('batchId', new THREE.BufferAttribute(batchIds, 1)); } - options.pointMaterial.size = pointMaterialSize[0]; - if (pointMaterialSize.length > 1) { - // TODO CREATE material for each feature - console.warn('Too many differents point.radius, only the first one will be used'); - } - return new THREE.Points(geom, options.pointMaterial); } + + function featureToLine(feature, options) { const ptsIn = feature.vertices; const normals = feature.normals; - const colors = new Uint8Array(ptsIn.length); const count = ptsIn.length / 3; const batchIds = options.batchId ? new Uint32Array(count) : undefined; @@ -287,18 +269,12 @@ function featureToLine(feature, options) { geom.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); let lines; - const lineMaterialWidth = []; - const globals = { stroke: true }; if (feature.geometries.length > 1) { // Multi line case const countIndices = (count - feature.geometries.length) * 2; const indices = getIntArrayFromSize(countIndices, count); let i = 0; for (const geometry of feature.geometries) { - const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; - - const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); - const start = geometry.indices[0].offset; // To avoid integer overflow with indice value (16 bits) if (start > 0xffff) { @@ -307,7 +283,6 @@ function featureToLine(feature, options) { } const count = geometry.indices[0].count; const end = start + count; - fillColorArray(colors, count, toColor(style.stroke.color), start); for (let j = start; j < end - 1; j++) { if (j < 0xffff) { indices[i++] = j; @@ -320,36 +295,19 @@ function featureToLine(feature, options) { const id = options.batchId(geometry.properties, featureId); fillBatchIdArray(id, batchIds, start, end); featureId++; - } - const width = style.stroke.width; - if (!lineMaterialWidth.includes(width)) { - lineMaterialWidth.push(width); + geom.setAttribute('batchId', new THREE.BufferAttribute(batchIds, 1)); } } - geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); - if (batchIds) { geom.setAttribute('batchId', new THREE.BufferAttribute(batchIds, 1)); } geom.setIndex(new THREE.BufferAttribute(indices, 1)); - options.lineMaterial.linewidth = lineMaterialWidth[0]; - if (lineMaterialWidth.length > 1) { - // TODO CREATE material for each feature - console.warn('Too many differents stroke.width, only the first one will be used'); - } lines = new THREE.LineSegments(geom, options.lineMaterial); } else { - const context = { globals, specifics: { properties: () => feature.geometries[0].properties, type: () => feature.type } }; - - const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); - - fillColorArray(colors, count, toColor(style.stroke.color)); - geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); if (batchIds) { const id = options.batchId(feature.geometries[0].properties, featureId); fillBatchIdArray(id, batchIds, 0, count); geom.setAttribute('batchId', new THREE.BufferAttribute(batchIds, 1)); } - options.lineMaterial.linewidth = style.stroke.width; lines = new THREE.Line(geom, options.lineMaterial); } return lines; @@ -368,11 +326,9 @@ function featureToPolygon(feature, options) { vertices = new Float32Array(ptsIn); } - const colors = new Uint8Array(ptsIn.length); const indices = []; const batchIds = options.batchId ? new Uint32Array(vertices.length / 3) : undefined; - const globals = { fill: true }; let featureId = 0; for (const geometry of feature.geometries) { @@ -382,15 +338,8 @@ function featureToPolygon(feature, options) { console.warn('Feature to Polygon: integer overflow, too many points in polygons'); break; } - const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; - - const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); - const lastIndice = geometry.indices.slice(-1)[0]; const end = lastIndice.offset + lastIndice.count; - const count = end - start; - - fillColorArray(colors, count, toColor(style.fill.color), start); const geomVertices = vertices.slice(start * 3, end * 3); const holesOffsets = geometry.indices.map(i => i.offset - start).slice(1); @@ -412,9 +361,7 @@ function featureToPolygon(feature, options) { const geom = new THREE.BufferGeometry(); geom.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); - geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); if (batchIds) { geom.setAttribute('batchId', new THREE.BufferAttribute(batchIds, 1)); } - geom.setIndex(new THREE.BufferAttribute(getIntArrayFromSize(indices, vertices.length / 3), 1)); return new THREE.Mesh(geom, options.polygonMaterial); @@ -438,7 +385,6 @@ function featureToExtrudedPolygon(feature, options) { const normals = feature.normals; const vertices = new Float32Array(ptsIn.length * 2); - const colors = new Uint8Array(ptsIn.length * 2); const indices = []; const totalVertices = ptsIn.length / 3; @@ -451,13 +397,9 @@ function featureToExtrudedPolygon(feature, options) { for (const geometry of feature.geometries) { const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; + // TODO use only extrusion_height const style = Style.merge(options.layer.style, feature.style, context).drawingStylefromContext(context); - - // topColor is assigned to the top of extruded polygon - const topColor = toColor(style.fill.color); - // bottomColor is assigned to the bottom of extruded polygon - bottomColor.copy(topColor); - bottomColor.multiplyScalar(0.5); + // const extrusion_height = options.layer.style.fill.extrusion_height || feature.style.fill.extrusion_height; const start = geometry.indices[0].offset; const lastIndice = geometry.indices.slice(-1)[0]; @@ -466,12 +408,10 @@ function featureToExtrudedPolygon(feature, options) { const isClockWise = geometry.indices[0].ccw ?? (area(ptsIn, start, count) < 0); coordinatesToVertices(ptsIn, normals, vertices, z, start, count); - fillColorArray(colors, count, bottomColor, start); const startTop = start + totalVertices; const endTop = end + totalVertices; coordinatesToVertices(ptsIn, normals, vertices, z + style.fill.extrusion_height, startTop, count, start); - fillColorArray(colors, count, topColor, startTop); const geomVertices = vertices.slice(startTop * 3, endTop * 3); const holesOffsets = geometry.indices.map(i => i.offset - start).slice(1); @@ -513,7 +453,6 @@ function featureToExtrudedPolygon(feature, options) { const geom = new THREE.BufferGeometry(); geom.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); - geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); if (batchIds) { geom.setAttribute('batchId', new THREE.BufferAttribute(batchIds, 1)); } geom.setIndex(new THREE.BufferAttribute(getIntArrayFromSize(indices, vertices.length / 3), 1)); @@ -521,6 +460,152 @@ function featureToExtrudedPolygon(feature, options) { return new THREE.Mesh(geom, options.polygonMaterial); } + +function setStyleOfExtrudedPolygon(mesh) { + const feature = mesh.feature; + const options = mesh.options; + const ptsIn = mesh.feature.vertices; + const colors = new Uint8Array(ptsIn.length * 2); + const totalVertices = ptsIn.length / 3; + const globals = { fill: true }; + for (const geometry of mesh.feature.geometries) { + const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; + const style = Style.merge(options.layer.style, feature.style, context).drawingStylefromContext(context); + + // topColor is assigned to the top of extruded polygon + const topColor = toColor(style.fill.color); + // bottomColor is assigned to the bottom of extruded polygon + bottomColor.copy(topColor); + bottomColor.multiplyScalar(0.5); + + const start = geometry.indices[0].offset; + const lastIndice = geometry.indices.slice(-1)[0]; + const end = lastIndice.offset + lastIndice.count; + const count = end - start; + fillColorArray(colors, count, bottomColor, start); + + const startTop = start + totalVertices; + fillColorArray(colors, count, topColor, startTop); + } + + const geom = mesh.geometry; + geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); +} + + +function setStyleOfPolygon(mesh) { + const feature = mesh.feature; + const options = mesh.options; + const ptsIn = feature.vertices; + const colors = new Uint8Array(ptsIn.length); + const globals = { fill: true }; + for (const geometry of feature.geometries) { + const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; + const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); + + const lastIndice = geometry.indices.slice(-1)[0]; + const start = geometry.indices[0].offset; + const end = lastIndice.offset + lastIndice.count; + const count = end - start; + + fillColorArray(colors, count, toColor(style.fill.color), start); + } + const geom = mesh.geometry; + geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); +} + +function setStyleOfLine(mesh) { + const feature = mesh.feature; + const options = mesh.options; + const ptsIn = feature.vertices; + const colors = new Uint8Array(ptsIn.length); + const count = ptsIn.length / 3; + const geom = mesh.geometry; + + const lineMaterialWidth = []; + const globals = { stroke: true }; + if (feature.geometries.length > 1) { + // const countIndices = (count - feature.geometries.length) * 2; + // const indices = getIntArrayFromSize(countIndices, count); + // Multi line case + for (const geometry of feature.geometries) { + const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; + const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); + + const start = geometry.indices[0].offset; + const count = geometry.indices[0].count; + fillColorArray(colors, count, toColor(style.stroke.color), start); + const width = style.stroke.width; + if (!lineMaterialWidth.includes(width)) { + lineMaterialWidth.push(width); + } + } + geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); + options.lineMaterial.linewidth = lineMaterialWidth[0]; + if (lineMaterialWidth.length > 1) { + // TODO CREATE material for each feature + console.warn('Too many differents stroke.width, only the first one will be used'); + } + } else { + const context = { globals, specifics: { properties: () => feature.geometries[0].properties, type: () => feature.type } }; + const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); + fillColorArray(colors, count, toColor(style.stroke.color)); + geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); + options.lineMaterial.linewidth = style.stroke.width; + } +} + +function setStyleofPoint(mesh) { + const feature = mesh.feature; + const options = mesh.options; + const ptsIn = feature.vertices; + const colors = new Uint8Array(ptsIn.length); + const pointMaterialSize = []; + const globals = { point: true }; + for (const geometry of feature.geometries) { + const context = { globals, specifics: { properties: () => geometry.properties, type: () => feature.type } }; + + const style = Style.merge(options.layer?.style, feature.style, context).drawingStylefromContext(context); + + const start = geometry.indices[0].offset; + const count = geometry.indices[0].count; + fillColorArray(colors, count, toColor(style.point.color), start); + const size = style.point.radius; + if (!pointMaterialSize.includes(size)) { + pointMaterialSize.push(size); + } + } + const geom = mesh.geometry; + geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true)); + options.pointMaterial.size = pointMaterialSize[0]; + if (pointMaterialSize.length > 1) { + // TODO CREATE material for each feature + console.warn('Too many differents point.radius, only the first one will be used'); + } +} + +function setStyle(meshes) { + if (!Array.isArray(meshes)) { meshes = [meshes]; } + meshes.forEach((mesh) => { + switch (mesh.feature.type) { + case FEATURE_TYPES.POINT: + setStyleofPoint(mesh); + break; + case FEATURE_TYPES.LINE: + setStyleOfLine(mesh); + break; + case FEATURE_TYPES.POLYGON: + if (mesh.options.layer?.style?.fill.extrusion_height) { + setStyleOfExtrudedPolygon(mesh); + } else { + setStyleOfPolygon(mesh); + } + break; + default: + } + }); +} + /** * Created Instanced object from mesh * @@ -614,6 +699,7 @@ function featureToMesh(feature, options) { } mesh.feature = feature; mesh.position.z = feature.altitude.min - options.GlobalZTrans; + mesh.options = options; if (options.layer) { mesh.layer = options.layer; @@ -626,6 +712,7 @@ function featureToMesh(feature, options) { * @module Feature2Mesh */ export default { + setStyle, /** * Return a function that converts [Features]{@link module:GeoJsonParser} to Meshes. Feature collection will be converted to a * a THREE.Group. @@ -685,6 +772,7 @@ export default { options.GlobalZTrans = collection.center.z; const meshes = features.map(feature => featureToMesh(feature, options)); + setStyle(meshes); const featureNode = new FeatureMesh(meshes, collection); return featureNode; diff --git a/src/Layer/FeatureGeometryLayer.js b/src/Layer/FeatureGeometryLayer.js index afa112963f..8d10a1630a 100644 --- a/src/Layer/FeatureGeometryLayer.js +++ b/src/Layer/FeatureGeometryLayer.js @@ -78,6 +78,12 @@ class FeatureGeometryLayer extends GeometryLayer { } return undefined; } + + redrawFeature(geometry) { + if (geometry) { + Feature2Mesh.setStyle(geometry.mesh); + } + } } export default FeatureGeometryLayer;