Merge pull request #10430 from Razi91/offset-RegularShape

Offset regular shape
This commit is contained in:
Andreas Hocevar
2020-01-15 11:26:41 +01:00
committed by GitHub
9 changed files with 108 additions and 17 deletions

View File

@@ -5,7 +5,9 @@ shortdesc: Example of some Regular Shape styles.
docs: >
This example shows how several regular shapes
or symbols (representing `x`, `cross`, `star`,
`triangle` and `square`) can be created.
`triangle`, `square` and `stacked`) can be created.
Style `stacked` represents possility to stack multiple shapes with offset
tags: "vector, symbol, regularshape, style, square, cross, star, triangle, x"
---
<div id="map" class="map"></div>

View File

@@ -59,18 +59,39 @@ const styles = {
radius2: 0,
angle: Math.PI / 4
})
})
}),
'stacked': [
new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 5,
angle: Math.PI / 4,
displacement: [0, 10]
})
}),
new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 10,
angle: Math.PI / 4
})
})
]
};
const styleKeys = ['x', 'cross', 'star', 'triangle', 'square'];
const styleKeys = ['x', 'cross', 'star', 'triangle', 'square', 'stacked'];
const count = 250;
const features = new Array(count);
const e = 4500000;
for (let i = 0; i < count; ++i) {
const coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
features[i] = new Feature(new Point(coordinates));
features[i].setStyle(styles[styleKeys[Math.floor(Math.random() * 5)]]);
features[i].setStyle(styles[styleKeys[Math.floor(Math.random() * 6)]]);
}
const source = new VectorSource({

View File

@@ -13,16 +13,17 @@ const vectorSource = new VectorSource();
function createFeatures(stroke, fill, offSet = [0, 0]) {
let feature;
feature = new Feature({
geometry: new Point([-15 + offSet[0], 15 + offSet[1]])
geometry: new Point([offSet[0], offSet[1]])
});
// square
// square with offset
feature.setStyle(new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 10,
angle: Math.PI / 4
angle: Math.PI / 4,
displacement: [-15, 15]
})
}));
vectorSource.addFeature(feature);

View File

@@ -10,6 +10,7 @@ import RegularShape from './RegularShape.js';
* @property {import("./Fill.js").default} [fill] Fill style.
* @property {number} radius Circle radius.
* @property {import("./Stroke.js").default} [stroke] Stroke style.
* @property {Array<number>} [displacement=[0,0]] displacement
*/
@@ -30,7 +31,8 @@ class CircleStyle extends RegularShape {
points: Infinity,
fill: options.fill,
radius: options.radius,
stroke: options.stroke
stroke: options.stroke,
displacement: options.displacement !== undefined ? options.displacement : [0, 0]
});
}
@@ -45,7 +47,8 @@ class CircleStyle extends RegularShape {
const style = new CircleStyle({
fill: this.getFill() ? this.getFill().clone() : undefined,
stroke: this.getStroke() ? this.getStroke().clone() : undefined,
radius: this.getRadius()
radius: this.getRadius(),
displacement: this.getDisplacement().slice()
});
style.setOpacity(this.getOpacity());
style.setScale(this.getScale());

View File

@@ -33,6 +33,7 @@ import ImageStyle from './Image.js';
* 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
* sub-rectangle to use from the original icon image.
* @property {Array<number>} [displacement=[0,0]] Displacement the icon
* @property {import("./IconOrigin.js").default} [offsetOrigin='top-left'] Origin of the offset: `bottom-left`, `bottom-right`,
* `top-left` or `top-right`.
* @property {number} [opacity=1] Opacity of the icon.
@@ -84,6 +85,7 @@ class Icon extends ImageStyle {
opacity: opacity,
rotation: rotation,
scale: scale,
displacement: options.displacement !== undefined ? options.displacement : [0, 0],
rotateWithView: rotateWithView
});
@@ -177,7 +179,6 @@ class Icon extends ImageStyle {
* @type {Array<number>}
*/
this.offset_ = options.offset !== undefined ? options.offset : [0, 0];
/**
* @private
* @type {import("./IconOrigin.js").default}
@@ -336,6 +337,7 @@ class Icon extends ImageStyle {
return this.origin_;
}
let offset = this.offset_;
const displacement = this.getDisplacement();
if (this.offsetOrigin_ != IconOrigin.TOP_LEFT) {
const size = this.getSize();
@@ -353,6 +355,8 @@ class Icon extends ImageStyle {
offset[1] = iconImageSize[1] - size[1] - offset[1];
}
}
offset[0] += displacement[0];
offset[1] += displacement[1];
this.origin_ = offset;
return this.origin_;
}

View File

@@ -10,6 +10,7 @@ import {abstract} from '../util.js';
* @property {boolean} rotateWithView
* @property {number} rotation
* @property {number} scale
* @property {Array<number>} displacement
*/
@@ -51,6 +52,12 @@ class ImageStyle {
*/
this.scale_ = options.scale;
/**
* @private
* @type {Array<number>}
*/
this.displacement_ = options.displacement;
}
/**
@@ -63,7 +70,8 @@ class ImageStyle {
opacity: this.getOpacity(),
scale: this.getScale(),
rotation: this.getRotation(),
rotateWithView: this.getRotateWithView()
rotateWithView: this.getRotateWithView(),
displacement: this.getDisplacement().slice()
});
}
@@ -103,6 +111,15 @@ class ImageStyle {
return this.scale_;
}
/**
* Get the displacement of the shape
* @return {Array<number>} Shape's center displacement
* @api
*/
getDisplacement() {
return this.displacement_;
}
/**
* Get the anchor point in pixels. The anchor determines the center point for the
* symbolizer.

View File

@@ -20,6 +20,7 @@ import ImageStyle from './Image.js';
* @property {number} [radius1] Outer radius of a star.
* @property {number} [radius2] Inner radius of a star.
* @property {number} [angle=0] Shape's angle in radians. A value of 0 will have one of the shape's point facing up.
* @property {Array<number>} [displacement=[0,0]] Displacement of the shape
* @property {import("./Stroke.js").default} [stroke] Stroke style.
* @property {number} [rotation=0] Rotation in radians (positive rotation clockwise).
* @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view.
@@ -61,7 +62,8 @@ class RegularShape extends ImageStyle {
opacity: 1,
rotateWithView: rotateWithView,
rotation: options.rotation !== undefined ? options.rotation : 0,
scale: 1
scale: 1,
displacement: options.displacement !== undefined ? options.displacement : [0, 0]
});
/**
@@ -160,7 +162,8 @@ class RegularShape extends ImageStyle {
angle: this.getAngle(),
stroke: this.getStroke() ? this.getStroke().clone() : undefined,
rotation: this.getRotation(),
rotateWithView: this.getRotateWithView()
rotateWithView: this.getRotateWithView(),
displacement: this.getDisplacement().slice()
});
style.setOpacity(this.getOpacity());
style.setScale(this.getScale());
@@ -353,12 +356,13 @@ class RegularShape extends ImageStyle {
// canvas.width and height are rounded to the closest integer
size = this.canvas_.width;
const imageSize = size;
const displacement = this.getDisplacement();
this.draw_(renderOptions, context, 0, 0);
this.createHitDetectionCanvas_(renderOptions);
this.anchor_ = [size / 2, size / 2];
this.anchor_ = [size / 2 - displacement[0], size / 2 + displacement[1]];
this.size_ = [size, size];
this.imageSize_ = [imageSize, imageSize];
}

View File

@@ -97,22 +97,26 @@ describe('ol.style.Icon', function() {
color: [1, 2, 3, 0.4],
src: src,
offset: [1, 2],
size: [10, 12]
size: [10, 12],
displacement: [5, 6]
});
const clone = original.clone();
expect(original.getAnchor()).not.to.be(clone.getAnchor());
expect(original.offset_).not.to.be(clone.offset_);
expect(original.getColor()).not.to.be(clone.getColor());
expect(original.getSize()).not.to.be(clone.getSize());
expect(original.getDisplacement()).not.to.be(clone.getDisplacement());
clone.anchor_[0] = 0;
clone.offset_[0] = 0;
clone.color_[0] = 0;
clone.size_[0] = 5;
clone.displacement_[0] = 10;
expect(original.anchor_).not.to.eql(clone.anchor_);
expect(original.offset_).not.to.eql(clone.offset_);
expect(original.color_).not.to.eql(clone.color_);
expect(original.size_).not.to.eql(clone.size_);
expect(original.displacement_).not.to.eql(clone.displacement_);
});
});
@@ -229,6 +233,18 @@ describe('ol.style.Icon', function() {
iconStyle.iconImage_.size_ = imageSize;
expect(iconStyle.getOrigin()).to.eql([92, 20]);
});
it('uses a top right offset origin + displacement', function() {
const iconStyle = new Icon({
src: 'test.png',
size: size,
offset: offset,
offsetOrigin: 'top-right',
displacement: [20, 10]
});
iconStyle.iconImage_.size_ = imageSize;
expect(iconStyle.getOrigin()).to.eql([92 + 20, 20 + 10]);
});
});
describe('#getImageSize', function() {

View File

@@ -82,6 +82,24 @@ describe('ol.style.RegularShape', function() {
expect(style.getHitDetectionImageSize()).to.eql([21, 21]);
});
it('sets default displacement [0, 0]', function() {
const style = new RegularShape({
radius: 5
});
expect(style.getDisplacement()).to.an('array');
expect(style.getDisplacement()[0]).to.eql(0);
expect(style.getDisplacement()[1]).to.eql(0);
});
it('can use offset', function() {
const style = new RegularShape({
radius: 5,
displacement: [10, 20]
});
expect(style.getDisplacement()).to.an('array');
expect(style.getDisplacement()[0]).to.eql(10);
expect(style.getDisplacement()[1]).to.eql(20);
});
});
describe('#clone', function() {
@@ -108,7 +126,8 @@ describe('ol.style.RegularShape', function() {
color: '#319FD3'
}),
rotation: 2,
rotateWithView: true
rotateWithView: true,
displacement: [10, 20]
});
original.setOpacity(0.5);
original.setScale(1.5);
@@ -123,6 +142,8 @@ describe('ol.style.RegularShape', function() {
expect(original.getRotateWithView()).to.eql(clone.getRotateWithView());
expect(original.getScale()).to.eql(clone.getScale());
expect(original.getStroke().getColor()).to.eql(clone.getStroke().getColor());
expect(original.getDisplacement()[0]).to.eql(clone.getDisplacement()[0]);
expect(original.getDisplacement()[1]).to.eql(clone.getDisplacement()[1]);
});
it('the clone does not reference the same objects as the original', function() {
@@ -132,11 +153,13 @@ describe('ol.style.RegularShape', function() {
}),
stroke: new Stroke({
color: '#319FD3'
})
}),
displacement: [0, 5]
});
const clone = original.clone();
expect(original.getFill()).to.not.be(clone.getFill());
expect(original.getStroke()).to.not.be(clone.getStroke());
expect(original.getDisplacement()).to.not.be(clone.getDisplacement());
clone.getFill().setColor('#012345');
clone.getStroke().setColor('#012345');