Merge pull request #12918 from mike-000/setDisplacement-2

Add setDisplacement method to ol/style/Image and subclasses
This commit is contained in:
Tim Schaub
2021-10-26 17:05:48 -06:00
committed by GitHub
8 changed files with 165 additions and 52 deletions

File diff suppressed because one or more lines are too long

13
examples/wind-arrows.html Normal file
View File

@@ -0,0 +1,13 @@
---
layout: example.html
title: Wind Arrows
shortdesc: Example of Wind Arrows styled using Regular Shapes.
docs: >
This example shows wind arrows styled using regular shapes for the
arrow shaft and head. The shaft is scaled based on the wind speed
and a corresponding displacement is used to position the unscaled
arrow head at the end of the scaled shaft.
The weather data is provided by <a href="https://openweathermap.org/current">OpenWeather</a>.
tags: "vector, symbol, regularshape, style, arrow"
---
<div id="map" class="map"></div>

83
examples/wind-arrows.js Normal file
View File

@@ -0,0 +1,83 @@
import Feature from '../src/ol/Feature.js';
import Map from '../src/ol/Map.js';
import OSM from '../src/ol/source/OSM.js';
import Point from '../src/ol/geom/Point.js';
import TileLayer from '../src/ol/layer/Tile.js';
import VectorLayer from '../src/ol/layer/Vector.js';
import VectorSource from '../src/ol/source/Vector.js';
import View from '../src/ol/View.js';
import {Fill, RegularShape, Stroke, Style} from '../src/ol/style.js';
import {fromLonLat} from '../src/ol/proj.js';
const shaft = new RegularShape({
points: 2,
radius: 5,
stroke: new Stroke({
width: 2,
color: 'black',
}),
rotateWithView: true,
});
const head = new RegularShape({
points: 3,
radius: 5,
fill: new Fill({
color: 'black',
}),
rotateWithView: true,
});
const styles = [new Style({image: shaft}), new Style({image: head})];
const source = new VectorSource({
attributions:
'Weather data by <a href="https://openweathermap.org/current">OpenWeather</a>',
});
const map = new Map({
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
source: source,
style: function (feature) {
const wind = feature.get('wind');
// rotate arrow away from wind origin
const angle = ((wind.deg - 180) * Math.PI) / 180;
const scale = wind.speed / 10;
shaft.setScale([1, scale]);
shaft.setRotation(angle);
head.setDisplacement([
0,
head.getRadius() / 2 + shaft.getRadius() * scale,
]);
head.setRotation(angle);
return styles;
},
}),
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2,
}),
});
fetch('data/openweather/weather.json')
.then(function (response) {
return response.json();
})
.then(function (data) {
const features = [];
data.list.forEach(function (report) {
const feature = new Feature(
new Point(fromLonLat([report.coord.lon, report.coord.lat]))
);
feature.setProperties(report);
features.push(feature);
});
source.addFeatures(features);
map.getView().fit(source.getExtent());
});

View File

@@ -32,7 +32,7 @@ import {getUid} from '../util.js';
* to provide the size of the image, with the `imgSize` option. * to provide the size of the image, with the `imgSize` option.
* @property {Array<number>} [offset=[0, 0]] Offset, which, together with the size and the offset origin, define the * @property {Array<number>} [offset=[0, 0]] Offset, which, together with the size and the offset origin, define the
* sub-rectangle to use from the original icon image. * sub-rectangle to use from the original icon image.
* @property {Array<number>} [displacement=[0,0]] Displacement the icon * @property {Array<number>} [displacement=[0,0]] Displacement of the icon.
* @property {import("./IconOrigin.js").default} [offsetOrigin='top-left'] Origin of the offset: `bottom-left`, `bottom-right`, * @property {import("./IconOrigin.js").default} [offsetOrigin='top-left'] Origin of the offset: `bottom-left`, `bottom-right`,
* `top-left` or `top-right`. * `top-left` or `top-right`.
* @property {number} [opacity=1] Opacity of the icon. * @property {number} [opacity=1] Opacity of the icon.
@@ -244,53 +244,50 @@ class Icon extends ImageStyle {
* @api * @api
*/ */
getAnchor() { getAnchor() {
if (this.normalizedAnchor_) { let anchor = this.normalizedAnchor_;
return this.normalizedAnchor_; if (!anchor) {
} anchor = this.anchor_;
let anchor = this.anchor_; const size = this.getSize();
const size = this.getSize(); if (
if ( this.anchorXUnits_ == IconAnchorUnits.FRACTION ||
this.anchorXUnits_ == IconAnchorUnits.FRACTION || this.anchorYUnits_ == IconAnchorUnits.FRACTION
this.anchorYUnits_ == IconAnchorUnits.FRACTION ) {
) { if (!size) {
if (!size) { return null;
return null; }
}
anchor = this.anchor_.slice();
if (this.anchorXUnits_ == IconAnchorUnits.FRACTION) {
anchor[0] *= size[0];
}
if (this.anchorYUnits_ == IconAnchorUnits.FRACTION) {
anchor[1] *= size[1];
}
}
if (this.anchorOrigin_ != IconOrigin.TOP_LEFT) {
if (!size) {
return null;
}
if (anchor === this.anchor_) {
anchor = this.anchor_.slice(); anchor = this.anchor_.slice();
if (this.anchorXUnits_ == IconAnchorUnits.FRACTION) {
anchor[0] *= size[0];
}
if (this.anchorYUnits_ == IconAnchorUnits.FRACTION) {
anchor[1] *= size[1];
}
} }
if (
this.anchorOrigin_ == IconOrigin.TOP_RIGHT || if (this.anchorOrigin_ != IconOrigin.TOP_LEFT) {
this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT if (!size) {
) { return null;
anchor[0] = -anchor[0] + size[0]; }
} if (anchor === this.anchor_) {
if ( anchor = this.anchor_.slice();
this.anchorOrigin_ == IconOrigin.BOTTOM_LEFT || }
this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT if (
) { this.anchorOrigin_ == IconOrigin.TOP_RIGHT ||
anchor[1] = -anchor[1] + size[1]; this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT
) {
anchor[0] = -anchor[0] + size[0];
}
if (
this.anchorOrigin_ == IconOrigin.BOTTOM_LEFT ||
this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT
) {
anchor[1] = -anchor[1] + size[1];
}
} }
this.normalizedAnchor_ = anchor;
} }
const displacement = this.getDisplacement(); const displacement = this.getDisplacement();
anchor[0] -= displacement[0]; return [anchor[0] - displacement[0], anchor[1] + displacement[1]];
anchor[1] += displacement[1];
this.normalizedAnchor_ = anchor;
return this.normalizedAnchor_;
} }
/** /**

View File

@@ -203,6 +203,16 @@ class ImageStyle {
return abstract(); return abstract();
} }
/**
* Set the displacement.
*
* @param {Array<number>} displacement Displacement.
* @api
*/
setDisplacement(displacement) {
this.displacement_ = displacement;
}
/** /**
* Set the opacity. * Set the opacity.
* *

View File

@@ -126,12 +126,6 @@ class RegularShape extends ImageStyle {
*/ */
this.stroke_ = options.stroke !== undefined ? options.stroke : null; this.stroke_ = options.stroke !== undefined ? options.stroke : null;
/**
* @private
* @type {Array<number>}
*/
this.anchor_ = null;
/** /**
* @private * @private
* @type {import("../size.js").Size} * @type {import("../size.js").Size}
@@ -177,7 +171,12 @@ class RegularShape extends ImageStyle {
* @api * @api
*/ */
getAnchor() { getAnchor() {
return this.anchor_; const size = this.size_;
if (!size) {
return null;
}
const displacement = this.getDisplacement();
return [size[0] / 2 - displacement[0], size[1] / 2 + displacement[1]];
} }
/** /**
@@ -467,9 +466,7 @@ class RegularShape extends ImageStyle {
render() { render() {
this.renderOptions_ = this.createRenderOptions(); this.renderOptions_ = this.createRenderOptions();
const size = this.renderOptions_.size; const size = this.renderOptions_.size;
const displacement = this.getDisplacement();
this.canvas_ = {}; this.canvas_ = {};
this.anchor_ = [size / 2 - displacement[0], size / 2 + displacement[1]];
this.size_ = [size, size]; this.size_ = [size, size];
} }

View File

@@ -198,6 +198,11 @@ describe('ol.style.Icon', function () {
size[0] / 2 - 20, size[0] / 2 - 20,
size[1] / 2 + 10, size[1] / 2 + 10,
]); ]);
iconStyle.setDisplacement([10, 20]);
expect(iconStyle.getAnchor()).to.eql([
size[0] / 2 - 10,
size[1] / 2 + 20,
]);
}); });
}); });

View File

@@ -87,6 +87,7 @@ describe('ol.style.RegularShape', function () {
expect(style.getDisplacement()).to.an('array'); expect(style.getDisplacement()).to.an('array');
expect(style.getDisplacement()[0]).to.eql(0); expect(style.getDisplacement()[0]).to.eql(0);
expect(style.getDisplacement()[1]).to.eql(0); expect(style.getDisplacement()[1]).to.eql(0);
expect(style.getAnchor()).to.eql([5, 5]);
}); });
it('will use the larger radius to calculate the size', function () { it('will use the larger radius to calculate the size', function () {
let style = new RegularShape({ let style = new RegularShape({
@@ -109,6 +110,12 @@ describe('ol.style.RegularShape', function () {
expect(style.getDisplacement()).to.an('array'); expect(style.getDisplacement()).to.an('array');
expect(style.getDisplacement()[0]).to.eql(10); expect(style.getDisplacement()[0]).to.eql(10);
expect(style.getDisplacement()[1]).to.eql(20); expect(style.getDisplacement()[1]).to.eql(20);
expect(style.getAnchor()).to.eql([-5, 25]);
style.setDisplacement([20, 10]);
expect(style.getDisplacement()).to.an('array');
expect(style.getDisplacement()[0]).to.eql(20);
expect(style.getDisplacement()[1]).to.eql(10);
expect(style.getAnchor()).to.eql([-15, 15]);
}); });
}); });