diff --git a/docs/config.json b/docs/config.json index d024eef942..5ad8155d11 100644 --- a/docs/config.json +++ b/docs/config.json @@ -15,6 +15,7 @@ "FeatureBuildingOptions", "Style", "StyleOptions", + "StyleContext", "Label" ], diff --git a/src/Converter/Feature2Mesh.js b/src/Converter/Feature2Mesh.js index 2f9a97bdce..8a66e700bd 100644 --- a/src/Converter/Feature2Mesh.js +++ b/src/Converter/Feature2Mesh.js @@ -7,63 +7,10 @@ import Extent from 'Core/Geographic/Extent'; import Crs from 'Core/Geographic/Crs'; import OrientationUtils from 'Utils/OrientationUtils'; import Coordinates from 'Core/Geographic/Coordinates'; +import { StyleContext } from 'Core/Style'; const coord = new Coordinates('EPSG:4326', 0, 0, 0); - -/** - * @class - * @classdesc FeatureContext is a class to store all informations - * about context to generate the style of each FeatureGeometry. - * - * @property {Coordinates} worldCoord @private Coordinates of the FeatureGeometry in world system. - * @property {Coordinates} localCoordinates @private Are the coordinates systeme origin local or global. - * @property {boolean} isProjected @private Are the coordinates already been projected. - * @property {FeatureGeometry} geometry @private - * @property {Object} globals - * @property {Object} collection - * @property {Coordinates} coordinates - */ -export class FeatureContext { - #worldCoord = new Coordinates('EPSG:4326', 0, 0, 0); - #localCoordinates = new Coordinates('EPSG:4326', 0, 0, 0); - #isProjected = true; - #geometry = {}; - - constructor() { - this.globals = {}; - } - - setGeometry(g) { - this.#geometry = g; - } - - setCollection(c) { - this.collection = c; - this.#localCoordinates.setCrs(c.crs); - } - - setLocalCoordinatesFromArray(vertices, offset) { - this.#isProjected = false; - return this.#localCoordinates.setFromArray(vertices, offset); - } - - get properties() { - return this.#geometry.properties; - } - - get coordinates() { - if (!this.#isProjected) { - this.#isProjected = true; - this.#worldCoord.copy(this.#localCoordinates).applyMatrix4(this.collection.matrixWorld); - if (this.#localCoordinates.crs == 'EPSG:4978') { - return this.#worldCoord.as('EPSG:4326', this.#worldCoord); - } - } - return this.#worldCoord; - } -} - -const context = new FeatureContext(); +const context = new StyleContext(); const dim_ref = new THREE.Vector2(); const dim = new THREE.Vector2(); diff --git a/src/Converter/Feature2Texture.js b/src/Converter/Feature2Texture.js index afcd645c2c..77dcaa0d65 100644 --- a/src/Converter/Feature2Texture.js +++ b/src/Converter/Feature2Texture.js @@ -2,10 +2,9 @@ import * as THREE from 'three'; import { FEATURE_TYPES } from 'Core/Feature'; import Extent from 'Core/Geographic/Extent'; import Coordinates from 'Core/Geographic/Coordinates'; -import Style from 'Core/Style'; -import { FeatureContext } from 'Converter/Feature2Mesh'; +import Style, { StyleContext } from 'Core/Style'; -const context = new FeatureContext(); +const context = new StyleContext(); /** * Draw polygon (contour, line edge and fill) based on feature vertices into canvas @@ -77,12 +76,6 @@ const coord = new Coordinates('EPSG:4326', 0, 0, 0); function drawFeature(ctx, feature, extent, style, invCtxScale) { const extentDim = extent.planarDimensions(); const scaleRadius = extentDim.x / ctx.canvas.width; - context.globals = { - fill: true, - stroke: true, - point: true, - zoom: extent.zoom, - }; for (const geometry of feature.geometries) { if (Extent.intersectsExtent(geometry.extent, extent)) { @@ -175,6 +168,13 @@ export default { // to scale line width and radius circle const invCtxScale = Math.abs(1 / scale.x); + context.globals = { + fill: true, + stroke: true, + point: true, + zoom: extent.zoom, + }; + // Draw the canvas for (const feature of collection.features) { drawFeature(ctx, feature, featureExtent, feature.style || style, invCtxScale); diff --git a/src/Core/Style.js b/src/Core/Style.js index 3b6b1801df..8856e58ede 100644 --- a/src/Core/Style.js +++ b/src/Core/Style.js @@ -4,6 +4,7 @@ import Fetcher from 'Provider/Fetcher'; import * as mapbox from '@mapbox/mapbox-gl-style-spec'; import { Color } from 'three'; import { deltaE } from 'Renderer/Color'; +import Coordinates from 'Core/Geographic/Coordinates'; import itowns_stroke_single_before from './StyleChunk/itowns_stroke_single_before.css'; @@ -160,6 +161,66 @@ function defineStyleProperty(style, category, name, value, defaultValue) { style[category][name] = value; } +/** + * @class + * @classdesc StyleContext stores metadata of one FeatureGeometry that are needed for its style computation: + * type of feature and what is needed (fill, stroke or draw a point, etc.) as well as where to get its + * properties and its coordinates (for base_altitude). + * + * @property {Object} globals Style type (fill, stroke, point, text and or icon) to consider, it also + * contains the current zoom. + * @property {Object} collection The FeatureCollection to which the FeatureGeometry is attached + * @property {Coordinates} coordinates The coordinates (in world space) of the last vertex (x, y, z) set with + * setLocalCoordinatesFromArray(). + * private properties: + * @property {Coordinates} worldCoord @private Coordinates object to store coordinates in world space. + * @property {Coordinates} localCoordinates @private Coordinates object to store coordinates in local space. + * @property {boolean} worldCoordsComputed @private Have the world coordinates already been computed + * from the local coordinates? + * @property {FeatureGeometry} geometry @private The FeatureGeometry to compute the style. + */ +export class StyleContext { + #worldCoord = new Coordinates('EPSG:4326', 0, 0, 0); + #localCoordinates = new Coordinates('EPSG:4326', 0, 0, 0); + #worldCoordsComputed = true; + #geometry = {}; + /** + * @constructor + */ + constructor() { + this.globals = {}; + } + + setGeometry(g) { + this.#geometry = g; + } + + setCollection(c) { + this.collection = c; + this.#localCoordinates.setCrs(c.crs); + } + + setLocalCoordinatesFromArray(vertices, offset) { + this.#worldCoordsComputed = false; + return this.#localCoordinates.setFromArray(vertices, offset); + } + + get properties() { + return this.#geometry.properties; + } + + get coordinates() { + if (!this.#worldCoordsComputed) { + this.#worldCoordsComputed = true; + this.#worldCoord.copy(this.#localCoordinates).applyMatrix4(this.collection.matrixWorld); + if (this.#localCoordinates.crs == 'EPSG:4978') { + return this.#worldCoord.as('EPSG:4326', this.#worldCoord); + } + } + return this.#worldCoord; + } +} + /** * @typedef {Object} StyleOptions * @memberof StyleOptions diff --git a/src/Layer/LabelLayer.js b/src/Layer/LabelLayer.js index 6c389babe9..d2c55ef813 100644 --- a/src/Layer/LabelLayer.js +++ b/src/Layer/LabelLayer.js @@ -6,11 +6,10 @@ import Coordinates from 'Core/Geographic/Coordinates'; import Extent from 'Core/Geographic/Extent'; import Label from 'Core/Label'; import { FEATURE_TYPES } from 'Core/Feature'; -import { readExpression } from 'Core/Style'; +import { readExpression, StyleContext } from 'Core/Style'; import { ScreenGrid } from 'Renderer/Label2DRenderer'; -import { FeatureContext } from 'Converter/Feature2Mesh'; -const context = new FeatureContext(); +const context = new StyleContext(); const coord = new Coordinates('EPSG:4326', 0, 0, 0);