From c28793ae0477f039dd51b71e90ee6311f4ead854 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Thu, 20 Feb 2020 13:31:41 +0000 Subject: [PATCH] Draw labels in a postrender function Position labels relative to the viewport when view is rotated --- src/ol/layer/Graticule.js | 170 ++++++++++++++++++++++++++------------ 1 file changed, 116 insertions(+), 54 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index b0e681926c..4096d42473 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -15,7 +15,7 @@ import { getTransform, transformExtent } from '../proj.js'; -import {getCenter, intersects, equals, getIntersection, isEmpty} from '../extent.js'; +import {getCenter, getHeight, getWidth, intersects, equals, getIntersection, isEmpty} from '../extent.js'; import {clamp} from '../math.js'; import Style from '../style/Style.js'; import Feature from '../Feature.js'; @@ -23,6 +23,8 @@ import {meridian, parallel} from '../geom/flat/geodesic.js'; import GeometryLayout from '../geom/GeometryLayout.js'; import Point from '../geom/Point.js'; import Collection from '../Collection.js'; +import {getVectorContext} from '../render.js'; +import EventType from '../render/EventType.js'; /** @@ -382,6 +384,8 @@ class Graticule extends VectorLayer { this.meridiansLabels_ = []; this.parallelsLabels_ = []; + + this.addEventListener(EventType.POSTRENDER, this.drawLabels_.bind(this)); } /** @@ -419,6 +423,7 @@ class Graticule extends VectorLayer { /** * @type {?import("../extent.js").Extent} + * @private */ this.loadedExtent_ = null; @@ -486,10 +491,10 @@ class Graticule extends VectorLayer { // first make sure we have enough features in the pool let featureCount = this.meridians_.length + this.parallels_.length; if (this.meridiansLabels_) { - featureCount += this.meridiansLabels_.length; + featureCount += this.meridians_.length; } if (this.parallelsLabels_) { - featureCount += this.parallelsLabels_.length; + featureCount += this.parallels_.length; } let feature; @@ -516,27 +521,6 @@ class Graticule extends VectorLayer { feature.setStyle(this.lineStyle_); featuresColl.push(feature); } - let labelData; - if (this.meridiansLabels_) { - for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) { - labelData = this.meridiansLabels_[i]; - feature = this.featurePool_[poolIndex++]; - feature.setGeometry(labelData.geom); - feature.setStyle(this.lonLabelStyle_); - feature.set('graticule_label', labelData.text); - featuresColl.push(feature); - } - } - if (this.parallelsLabels_) { - for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) { - labelData = this.parallelsLabels_[i]; - feature = this.featurePool_[poolIndex++]; - feature.setGeometry(labelData.geom); - feature.setStyle(this.latLabelStyle_); - feature.set('graticule_label', labelData.text); - featuresColl.push(feature); - } - } } /** @@ -553,11 +537,15 @@ class Graticule extends VectorLayer { const lineString = this.getMeridian_(lon, minLat, maxLat, squaredTolerance, index); if (intersects(lineString.getExtent(), extent)) { if (this.meridiansLabels_) { - const textPoint = this.getMeridianPoint_(lineString, extent, index); - this.meridiansLabels_[index] = { - geom: textPoint, - text: this.lonLabelFormatter_(lon) - }; + const text = this.lonLabelFormatter_(lon); + if (index in this.meridiansLabels_) { + this.meridiansLabels_[index].text = text; + } else { + this.meridiansLabels_[index] = { + geom: new Point([]), + text: text + }; + } } this.meridians_[index++] = lineString; } @@ -578,17 +566,83 @@ class Graticule extends VectorLayer { const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index); if (intersects(lineString.getExtent(), extent)) { if (this.parallelsLabels_) { - const textPoint = this.getParallelPoint_(lineString, extent, index); - this.parallelsLabels_[index] = { - geom: textPoint, - text: this.latLabelFormatter_(lat) - }; + const text = this.latLabelFormatter_(lat); + if (index in this.parallelsLabels_) { + this.parallelsLabels_[index].text = text; + } else { + this.parallelsLabels_[index] = { + geom: new Point([]), + text: text + }; + } } this.parallels_[index++] = lineString; } return index; } + /** + * @param {import("../render/Event.js").RenderEvent} event Render event. + * @private + */ + drawLabels_(event) { + const rotation = event.frameState.viewState.rotation; + const extent = event.frameState.extent; + let rotationCenter, rotationExtent; + if (rotation) { + rotationCenter = getCenter(extent); + const width = getWidth(extent); + const height = getHeight(extent); + const cr = Math.abs(Math.cos(rotation)); + const sr = Math.abs(Math.sin(rotation)); + const unrotatedWidth = (sr * height - cr * width) / (sr * sr - cr * cr); + const unrotatedHeight = (sr * width - cr * height) / (sr * sr - cr * cr); + rotationExtent = [ + rotationCenter[0] - unrotatedWidth / 2, rotationCenter[1] - unrotatedHeight / 2, + rotationCenter[0] + unrotatedWidth / 2, rotationCenter[1] + unrotatedHeight / 2 + ]; + } + + const vectorContext = getVectorContext(event); + let poolIndex = this.meridians_.length + this.parallels_.length; + let feature, index, l, textPoint; + + if (this.meridiansLabels_) { + for (index = 0, l = this.meridiansLabels_.length; index < l; ++index) { + const lineString = this.meridians_[index]; + if (!rotation) { + textPoint = this.getMeridianPoint_(lineString, this.renderedExtent_, index); + } else { + const clone = lineString.clone(); + clone.rotate(-rotation, rotationCenter); + textPoint = this.getMeridianPoint_(clone, rotationExtent, index); + textPoint.rotate(rotation, rotationCenter); + } + feature = this.featurePool_[poolIndex++]; + feature.setGeometry(textPoint); + feature.set('graticule_label', this.meridiansLabels_[index].text); + vectorContext.drawFeature(feature, this.lonLabelStyle_(feature)); + } + } + if (this.parallelsLabels_) { + for (index = 0, l = this.parallels_.length; index < l; ++index) { + const lineString = this.parallels_[index]; + if (!rotation) { + textPoint = this.getParallelPoint_(lineString, this.renderedExtent_, index); + } else { + const clone = lineString.clone(); + clone.rotate(-rotation, rotationCenter); + textPoint = this.getParallelPoint_(clone, rotationExtent, index); + textPoint.rotate(rotation, rotationCenter); + } + feature = this.featurePool_[poolIndex++]; + feature.setGeometry(textPoint); + feature.set('graticule_label', this.parallelsLabels_[index].text); + vectorContext.drawFeature(feature, this.latLabelStyle_(feature)); + } + } + } + /** * @param {import("../extent.js").Extent} extent Extent. * @param {import("../coordinate.js").Coordinate} center Center. @@ -745,19 +799,23 @@ class Graticule extends VectorLayer { */ getMeridianPoint_(lineString, extent, index) { const flatCoordinates = lineString.getFlatCoordinates(); - const clampedBottom = Math.max(extent[1], flatCoordinates[1]); - const clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]); + let bottom = 1; + let top = flatCoordinates.length - 1; + if (flatCoordinates[bottom] > flatCoordinates[top]) { + bottom = top; + top = 1; + } + const clampedBottom = Math.max(extent[1], flatCoordinates[bottom]); + const clampedTop = Math.min(extent[3], flatCoordinates[top]); const lat = clamp( extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_, clampedBottom, clampedTop); - const coordinate = [flatCoordinates[0], lat]; - let point; - if (index in this.meridiansLabels_) { - point = this.meridiansLabels_[index].geom; - point.setCoordinates(coordinate); - } else { - point = new Point(coordinate); - } + const coordinate0 = flatCoordinates[bottom - 1] + + (flatCoordinates[top - 1] - flatCoordinates[bottom - 1]) * (lat - flatCoordinates[bottom]) / + (flatCoordinates[top] - flatCoordinates[bottom]); + const coordinate = [coordinate0, lat]; + const point = this.meridiansLabels_[index].geom; + point.setCoordinates(coordinate); return point; } @@ -801,19 +859,23 @@ class Graticule extends VectorLayer { */ getParallelPoint_(lineString, extent, index) { const flatCoordinates = lineString.getFlatCoordinates(); - const clampedLeft = Math.max(extent[0], flatCoordinates[0]); - const clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]); + let left = 0; + let right = flatCoordinates.length - 2; + if (flatCoordinates[left] > flatCoordinates[right]) { + left = right; + right = 0; + } + const clampedLeft = Math.max(extent[0], flatCoordinates[left]); + const clampedRight = Math.min(extent[2], flatCoordinates[right]); const lon = clamp( extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_, clampedLeft, clampedRight); - const coordinate = [lon, flatCoordinates[1]]; - let point; - if (index in this.parallelsLabels_) { - point = this.parallelsLabels_[index].geom; - point.setCoordinates(coordinate); - } else { - point = new Point(coordinate); - } + const coordinate1 = flatCoordinates[left + 1] + + (flatCoordinates[right + 1] - flatCoordinates[left + 1]) * (lon - flatCoordinates[left]) / + (flatCoordinates[right] - flatCoordinates[left]); + const coordinate = [lon, coordinate1]; + const point = this.parallelsLabels_[index].geom; + point.setCoordinates(coordinate); return point; }