Merge pull request #11037 from mike-000/patch-11
Allow icon and text styles to be scaled in two dimensions
|
After Width: | Height: | Size: 427 B |
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Icon Scale
|
||||||
|
shortdesc: Example of scaling icons and labels.
|
||||||
|
docs: >
|
||||||
|
Icons and labels can be scaled in both dimensions if required. A negative value will flip the image
|
||||||
|
or text around its anchor point (reversed text is <b>not</b> suitable for line placement). A newline
|
||||||
|
character inserted in label text is interpreted in a <b>vector layer</b>, but will not be shown in
|
||||||
|
a <b>vector context</b>.
|
||||||
|
tags: "vector, style, icon, label, scale"
|
||||||
|
resources:
|
||||||
|
- https://code.jquery.com/jquery-2.2.3.min.js
|
||||||
|
- https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css
|
||||||
|
- https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js
|
||||||
|
---
|
||||||
|
<div id="map" class="map"><div id="popup"></div></div>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import Feature from '../src/ol/Feature.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import Overlay from '../src/ol/Overlay.js';
|
||||||
|
import Point from '../src/ol/geom/Point.js';
|
||||||
|
import TileJSON from '../src/ol/source/TileJSON.js';
|
||||||
|
import VectorSource from '../src/ol/source/Vector.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import {Icon, Style, Text} from '../src/ol/style.js';
|
||||||
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
|
import {fromLonLat} from '../src/ol/proj.js';
|
||||||
|
import {getVectorContext} from '../src/ol/render.js';
|
||||||
|
|
||||||
|
const rasterLayer = new TileLayer({
|
||||||
|
source: new TileJSON({
|
||||||
|
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
|
||||||
|
crossOrigin: '',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconFeature = new Feature({
|
||||||
|
geometry: new Point(fromLonLat([0, -10])),
|
||||||
|
name: 'Fish.1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const feature1 = new Feature({
|
||||||
|
geometry: new Point(fromLonLat([0, -10])),
|
||||||
|
name: 'Fish.1 Island',
|
||||||
|
});
|
||||||
|
|
||||||
|
const feature2 = new Feature({
|
||||||
|
geometry: new Point(fromLonLat([-30, 10])),
|
||||||
|
name: 'Fish.2 Island',
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconStyle = new Style({
|
||||||
|
image: new Icon({
|
||||||
|
anchor: [0.5, 0.9],
|
||||||
|
src: 'data/fish.png',
|
||||||
|
crossOrigin: '',
|
||||||
|
scale: [0, 0],
|
||||||
|
rotation: Math.PI / 4,
|
||||||
|
}),
|
||||||
|
text: new Text({
|
||||||
|
text: 'FISH\nTEXT',
|
||||||
|
scale: [0, 0],
|
||||||
|
rotation: Math.PI / 4,
|
||||||
|
textAlign: 'center',
|
||||||
|
textBaseline: 'top',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
let j = 45;
|
||||||
|
|
||||||
|
iconFeature.setStyle(function () {
|
||||||
|
const x = Math.sin((i * Math.PI) / 180) * 3;
|
||||||
|
const y = Math.sin((j * Math.PI) / 180) * 4;
|
||||||
|
iconStyle.getImage().setScale([x, y]);
|
||||||
|
iconStyle.getText().setScale([x, y]);
|
||||||
|
return iconStyle;
|
||||||
|
});
|
||||||
|
|
||||||
|
rasterLayer.on('postrender', function (event) {
|
||||||
|
const vectorContext = getVectorContext(event);
|
||||||
|
const x = Math.cos((i * Math.PI) / 180) * 3;
|
||||||
|
const y = Math.cos((j * Math.PI) / 180) * 4;
|
||||||
|
iconStyle.getImage().setScale([x, y]);
|
||||||
|
iconStyle.getText().setScale([x, y]);
|
||||||
|
vectorContext.drawFeature(feature2, iconStyle);
|
||||||
|
});
|
||||||
|
|
||||||
|
const vectorSource = new VectorSource({
|
||||||
|
features: [iconFeature, feature1, feature2],
|
||||||
|
});
|
||||||
|
|
||||||
|
const vectorLayer = new VectorLayer({
|
||||||
|
source: vectorSource,
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
layers: [rasterLayer, vectorLayer],
|
||||||
|
target: document.getElementById('map'),
|
||||||
|
view: new View({
|
||||||
|
center: fromLonLat([-15, 0]),
|
||||||
|
zoom: 3,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
i = (i + 4) % 360;
|
||||||
|
j = (j + 5) % 360;
|
||||||
|
vectorSource.changed();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const element = document.getElementById('popup');
|
||||||
|
|
||||||
|
const popup = new Overlay({
|
||||||
|
element: element,
|
||||||
|
positioning: 'bottom-center',
|
||||||
|
stopEvent: false,
|
||||||
|
offset: [0, -50],
|
||||||
|
});
|
||||||
|
map.addOverlay(popup);
|
||||||
|
|
||||||
|
// display popup on click
|
||||||
|
map.on('click', function (evt) {
|
||||||
|
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
|
||||||
|
return feature;
|
||||||
|
});
|
||||||
|
$(element).popover('destroy');
|
||||||
|
if (feature) {
|
||||||
|
const coordinates = feature.getGeometry().getCoordinates();
|
||||||
|
popup.setPosition(coordinates);
|
||||||
|
$(element).popover({
|
||||||
|
placement: 'top',
|
||||||
|
html: true,
|
||||||
|
animation: false,
|
||||||
|
content: feature.get('name'),
|
||||||
|
});
|
||||||
|
$(element).popover('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// change mouse cursor when over marker
|
||||||
|
map.on('pointermove', function (e) {
|
||||||
|
if (e.dragging) {
|
||||||
|
$(element).popover('destroy');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pixel = map.getEventPixel(e.originalEvent);
|
||||||
|
const hit = map.hasFeatureAtPixel(pixel);
|
||||||
|
map.getTarget().style.cursor = hit ? 'pointer' : '';
|
||||||
|
});
|
||||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,117 @@
|
|||||||
|
import Feature from '../../../src/ol/Feature.js';
|
||||||
|
import Icon from '../../../src/ol/style/Icon.js';
|
||||||
|
import Map from '../../../src/ol/Map.js';
|
||||||
|
import Point from '../../../src/ol/geom/Point.js';
|
||||||
|
import Style from '../../../src/ol/style/Style.js';
|
||||||
|
import VectorLayer from '../../../src/ol/layer/Vector.js';
|
||||||
|
import VectorSource from '../../../src/ol/source/Vector.js';
|
||||||
|
import View from '../../../src/ol/View.js';
|
||||||
|
|
||||||
|
const features = [];
|
||||||
|
for (let i = 0; i < 2; ++i) {
|
||||||
|
const x = i * 5;
|
||||||
|
features.push(
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 2, 2]),
|
||||||
|
scale: [1.5, 1],
|
||||||
|
anchor: [1, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 3, 2]),
|
||||||
|
scale: [1.5, 1],
|
||||||
|
anchor: [0.5, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 4, 2]),
|
||||||
|
scale: [1.5, 1],
|
||||||
|
anchor: [0, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 2, 4]),
|
||||||
|
scale: [-1, 1],
|
||||||
|
anchor: [0, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 3, 4]),
|
||||||
|
scale: [-1, 1],
|
||||||
|
anchor: [0.5, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 4, 4]),
|
||||||
|
scale: [-1, 1],
|
||||||
|
anchor: [1, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 2, 6]),
|
||||||
|
scale: [1, -1],
|
||||||
|
anchor: [0.5, 1],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 3, 6]),
|
||||||
|
scale: [1, -1],
|
||||||
|
anchor: [0.5, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 4, 6]),
|
||||||
|
scale: [1, -1],
|
||||||
|
anchor: [0.5, 0],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 2, 8]),
|
||||||
|
scale: [1, 1.5],
|
||||||
|
anchor: [0.5, 0],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 3, 8]),
|
||||||
|
scale: [1, 1.5],
|
||||||
|
anchor: [0.5, 0.5],
|
||||||
|
rotated: i,
|
||||||
|
}),
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([x + 4, 8]),
|
||||||
|
scale: [1, 1.5],
|
||||||
|
anchor: [0.5, 1],
|
||||||
|
rotated: i,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const vectorSource = new VectorSource({
|
||||||
|
features: features,
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = new Style({
|
||||||
|
image: new Icon({
|
||||||
|
src: '/data/fish.png',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const vectorLayer = new VectorLayer({
|
||||||
|
source: vectorSource,
|
||||||
|
style: function (feature) {
|
||||||
|
style.getImage().setScale(feature.get('scale'));
|
||||||
|
style.getImage().setAnchor(feature.get('anchor'));
|
||||||
|
style.getImage().setRotation((feature.get('rotated') * Math.PI) / 4);
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
pixelRatio: 1,
|
||||||
|
layers: [vectorLayer],
|
||||||
|
target: 'map',
|
||||||
|
view: new View(),
|
||||||
|
});
|
||||||
|
map.getView().fit([0, 0, 11, 11]);
|
||||||
|
|
||||||
|
render();
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@@ -59,7 +59,7 @@ feature1.setStyle(
|
|||||||
vectorSource.addFeature(feature1);
|
vectorSource.addFeature(feature1);
|
||||||
|
|
||||||
const lineString2 = lineString1.clone();
|
const lineString2 = lineString1.clone();
|
||||||
lineString2.translate(0, 30);
|
lineString2.translate(0, 20);
|
||||||
const feature2 = new Feature({geometry: lineString2});
|
const feature2 = new Feature({geometry: lineString2});
|
||||||
feature2.setStyle(
|
feature2.setStyle(
|
||||||
new Style({
|
new Style({
|
||||||
@@ -123,7 +123,7 @@ feature4.setStyle(
|
|||||||
vectorSource.addFeature(feature4);
|
vectorSource.addFeature(feature4);
|
||||||
|
|
||||||
const lineString5 = lineString4.clone();
|
const lineString5 = lineString4.clone();
|
||||||
lineString5.translate(0, 30);
|
lineString5.translate(0, 20);
|
||||||
const feature5 = new Feature({geometry: lineString5});
|
const feature5 = new Feature({geometry: lineString5});
|
||||||
feature5.setStyle(
|
feature5.setStyle(
|
||||||
new Style({
|
new Style({
|
||||||
@@ -141,7 +141,7 @@ feature5.setStyle(
|
|||||||
vectorSource.addFeature(feature5);
|
vectorSource.addFeature(feature5);
|
||||||
|
|
||||||
const lineString6 = lineString5.clone();
|
const lineString6 = lineString5.clone();
|
||||||
lineString6.translate(0, 30);
|
lineString6.translate(0, 20);
|
||||||
const feature6 = new Feature({geometry: lineString6});
|
const feature6 = new Feature({geometry: lineString6});
|
||||||
feature6.setStyle(
|
feature6.setStyle(
|
||||||
new Style({
|
new Style({
|
||||||
@@ -160,6 +160,28 @@ feature6.setStyle(
|
|||||||
);
|
);
|
||||||
vectorSource.addFeature(feature6);
|
vectorSource.addFeature(feature6);
|
||||||
|
|
||||||
|
const lineString7 = lineString6.clone();
|
||||||
|
lineString7.translate(0, 30);
|
||||||
|
const feature7 = new Feature({geometry: lineString7});
|
||||||
|
feature7.setStyle(
|
||||||
|
new Style({
|
||||||
|
stroke: new Stroke({color: 'blue'}),
|
||||||
|
text: new Text({
|
||||||
|
text: 'Reflection',
|
||||||
|
font: 'normal 400 12px/1 Ubuntu',
|
||||||
|
scale: [2, -1],
|
||||||
|
textBaseline: 'bottom',
|
||||||
|
textAlign: 'right',
|
||||||
|
placement: 'line',
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: '#FFFF00',
|
||||||
|
width: 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
vectorSource.addFeature(feature7);
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
pixelRatio: 1,
|
pixelRatio: 1,
|
||||||
layers: [
|
layers: [
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 8.6 KiB |
@@ -110,15 +110,23 @@ feature.setStyle(
|
|||||||
);
|
);
|
||||||
vectorSource.addFeature(feature);
|
vectorSource.addFeature(feature);
|
||||||
|
|
||||||
// background and padding
|
// two dimensional scale
|
||||||
feature = new Feature({
|
feature = new Feature({
|
||||||
geometry: new Point([-10, 0]),
|
geometry: new Point([100, 20]),
|
||||||
});
|
});
|
||||||
feature.setStyle(
|
feature.setStyle(
|
||||||
new Style({
|
new Style({
|
||||||
text: new Text({
|
text: new Text({
|
||||||
text: 'hello',
|
text: 'mirror',
|
||||||
font: '12px Ubuntu',
|
font: '12px Ubuntu',
|
||||||
|
scale: [-1, 2],
|
||||||
|
rotateWithView: true,
|
||||||
|
fill: new Fill({
|
||||||
|
color: 'red',
|
||||||
|
}),
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: '#000',
|
||||||
|
}),
|
||||||
padding: [1, 2, 3, 5],
|
padding: [1, 2, 3, 5],
|
||||||
backgroundFill: new Fill({
|
backgroundFill: new Fill({
|
||||||
color: 'rgba(55, 55, 55, 0.25)',
|
color: 'rgba(55, 55, 55, 0.25)',
|
||||||
|
|||||||
|
After Width: | Height: | Size: 427 B |
@@ -915,11 +915,11 @@ function createNameStyleFunction(foundStyle, name) {
|
|||||||
imageSize = DEFAULT_IMAGE_STYLE_SIZE;
|
imageSize = DEFAULT_IMAGE_STYLE_SIZE;
|
||||||
}
|
}
|
||||||
if (imageSize.length == 2) {
|
if (imageSize.length == 2) {
|
||||||
const imageScale = imageStyle.getScale();
|
const imageScale = imageStyle.getScaleArray();
|
||||||
// Offset the label to be centered to the right of the icon,
|
// Offset the label to be centered to the right of the icon,
|
||||||
// if there is one.
|
// if there is one.
|
||||||
textOffset[0] = (imageScale * imageSize[0]) / 2;
|
textOffset[0] = (imageScale[0] * imageSize[0]) / 2;
|
||||||
textOffset[1] = (-imageScale * imageSize[1]) / 2;
|
textOffset[1] = (-imageScale[1] * imageSize[1]) / 2;
|
||||||
textAlign = 'left';
|
textAlign = 'left';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ import {toString} from '../transform.js';
|
|||||||
* @property {boolean} [overflow]
|
* @property {boolean} [overflow]
|
||||||
* @property {import("../style/Fill.js").default} [backgroundFill]
|
* @property {import("../style/Fill.js").default} [backgroundFill]
|
||||||
* @property {import("../style/Stroke.js").default} [backgroundStroke]
|
* @property {import("../style/Stroke.js").default} [backgroundStroke]
|
||||||
* @property {number} [scale]
|
* @property {import("../size.js").Size} [scale]
|
||||||
* @property {Array<number>} [padding]
|
* @property {Array<number>} [padding]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ export function rotateAtOffset(context, rotation, offsetX, offsetY) {
|
|||||||
* @param {number} h Height.
|
* @param {number} h Height.
|
||||||
* @param {number} x X.
|
* @param {number} x X.
|
||||||
* @param {number} y Y.
|
* @param {number} y Y.
|
||||||
* @param {number} scale Scale.
|
* @param {import("../size.js").Size} scale Scale.
|
||||||
*/
|
*/
|
||||||
export function drawImageOrLabel(
|
export function drawImageOrLabel(
|
||||||
context,
|
context,
|
||||||
@@ -437,10 +437,25 @@ export function drawImageOrLabel(
|
|||||||
if (/** @type {*} */ (labelOrImage).contextInstructions) {
|
if (/** @type {*} */ (labelOrImage).contextInstructions) {
|
||||||
// label
|
// label
|
||||||
context.translate(x, y);
|
context.translate(x, y);
|
||||||
context.scale(scale, scale);
|
context.scale(scale[0], scale[1]);
|
||||||
executeLabelInstructions(/** @type {Label} */ (labelOrImage), context);
|
executeLabelInstructions(/** @type {Label} */ (labelOrImage), context);
|
||||||
|
} else if (scale[0] < 0 || scale[1] < 0) {
|
||||||
|
// flipped image
|
||||||
|
context.translate(x, y);
|
||||||
|
context.scale(scale[0], scale[1]);
|
||||||
|
context.drawImage(
|
||||||
|
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (labelOrImage),
|
||||||
|
originX,
|
||||||
|
originY,
|
||||||
|
w,
|
||||||
|
h,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
w,
|
||||||
|
h
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// image
|
// if image not flipped translate and scale can be avoided
|
||||||
context.drawImage(
|
context.drawImage(
|
||||||
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (labelOrImage),
|
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (labelOrImage),
|
||||||
originX,
|
originX,
|
||||||
@@ -449,8 +464,8 @@ export function drawImageOrLabel(
|
|||||||
h,
|
h,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
w * scale,
|
w * scale[0],
|
||||||
h * scale
|
h * scale[1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,10 @@ class Executor {
|
|||||||
const fillState = fillKey ? this.fillStates[fillKey] : null;
|
const fillState = fillKey ? this.fillStates[fillKey] : null;
|
||||||
const textState = this.textStates[textKey];
|
const textState = this.textStates[textKey];
|
||||||
const pixelRatio = this.pixelRatio;
|
const pixelRatio = this.pixelRatio;
|
||||||
const scale = textState.scale * pixelRatio;
|
const scale = [
|
||||||
|
textState.scale[0] * pixelRatio,
|
||||||
|
textState.scale[1] * pixelRatio,
|
||||||
|
];
|
||||||
const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign];
|
const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign];
|
||||||
const strokeWidth =
|
const strokeWidth =
|
||||||
strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
||||||
@@ -207,15 +210,17 @@ class Executor {
|
|||||||
const height = lineHeight * numLines;
|
const height = lineHeight * numLines;
|
||||||
const renderWidth = width + strokeWidth;
|
const renderWidth = width + strokeWidth;
|
||||||
const contextInstructions = [];
|
const contextInstructions = [];
|
||||||
|
// make canvas 2 pixels wider to account for italic text width measurement errors
|
||||||
|
const w = (renderWidth + 2) * scale[0];
|
||||||
|
const h = (height + strokeWidth) * scale[1];
|
||||||
/** @type {import("../canvas.js").Label} */
|
/** @type {import("../canvas.js").Label} */
|
||||||
const label = {
|
const label = {
|
||||||
// make canvas 2 pixels wider to account for italic text width measurement errors
|
width: w < 0 ? Math.floor(w) : Math.ceil(w),
|
||||||
width: Math.ceil((renderWidth + 2) * scale),
|
height: h < 0 ? Math.floor(h) : Math.ceil(h),
|
||||||
height: Math.ceil((height + strokeWidth) * scale),
|
|
||||||
contextInstructions: contextInstructions,
|
contextInstructions: contextInstructions,
|
||||||
};
|
};
|
||||||
if (scale != 1) {
|
if (scale[0] != 1 || scale[1] != 1) {
|
||||||
contextInstructions.push('scale', [scale, scale]);
|
contextInstructions.push('scale', scale);
|
||||||
}
|
}
|
||||||
contextInstructions.push('font', textState.font);
|
contextInstructions.push('font', textState.font);
|
||||||
if (strokeKey) {
|
if (strokeKey) {
|
||||||
@@ -317,7 +322,7 @@ class Executor {
|
|||||||
* @param {number} originX Origin X.
|
* @param {number} originX Origin X.
|
||||||
* @param {number} originY Origin Y.
|
* @param {number} originY Origin Y.
|
||||||
* @param {number} rotation Rotation.
|
* @param {number} rotation Rotation.
|
||||||
* @param {number} scale Scale.
|
* @param {import("../../size.js").Size} scale Scale.
|
||||||
* @param {boolean} snapToPixel Snap to pixel.
|
* @param {boolean} snapToPixel Snap to pixel.
|
||||||
* @param {number} width Width.
|
* @param {number} width Width.
|
||||||
* @param {Array<number>} padding Padding.
|
* @param {Array<number>} padding Padding.
|
||||||
@@ -347,8 +352,8 @@ class Executor {
|
|||||||
strokeInstruction
|
strokeInstruction
|
||||||
) {
|
) {
|
||||||
const fillStroke = fillInstruction || strokeInstruction;
|
const fillStroke = fillInstruction || strokeInstruction;
|
||||||
anchorX *= scale;
|
anchorX *= scale[0];
|
||||||
anchorY *= scale;
|
anchorY *= scale[1];
|
||||||
x -= anchorX;
|
x -= anchorX;
|
||||||
y -= anchorY;
|
y -= anchorY;
|
||||||
|
|
||||||
@@ -360,8 +365,8 @@ class Executor {
|
|||||||
height + originY > imageOrLabel.height
|
height + originY > imageOrLabel.height
|
||||||
? imageOrLabel.height - originY
|
? imageOrLabel.height - originY
|
||||||
: height;
|
: height;
|
||||||
const boxW = padding[3] + w * scale + padding[1];
|
const boxW = padding[3] + w * scale[0] + padding[1];
|
||||||
const boxH = padding[0] + h * scale + padding[2];
|
const boxH = padding[0] + h * scale[1] + padding[2];
|
||||||
const boxX = x - padding[3];
|
const boxX = x - padding[3];
|
||||||
const boxY = y - padding[0];
|
const boxY = y - padding[0];
|
||||||
|
|
||||||
@@ -415,7 +420,7 @@ class Executor {
|
|||||||
);
|
);
|
||||||
const canvas = context.canvas;
|
const canvas = context.canvas;
|
||||||
const strokePadding = strokeInstruction
|
const strokePadding = strokeInstruction
|
||||||
? (strokeInstruction[2] * scale) / 2
|
? (strokeInstruction[2] * scale[0]) / 2
|
||||||
: 0;
|
: 0;
|
||||||
const renderBuffer = this.renderBuffer_;
|
const renderBuffer = this.renderBuffer_;
|
||||||
const intersects =
|
const intersects =
|
||||||
@@ -612,7 +617,7 @@ class Executor {
|
|||||||
strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
||||||
|
|
||||||
// Remove the 2 pixels we added in createLabel() for the anchor
|
// Remove the 2 pixels we added in createLabel() for the anchor
|
||||||
const width = label.width / pixelRatio - 2 * textState.scale;
|
const width = label.width / pixelRatio - 2 * textState.scale[0];
|
||||||
const anchorX = align * width + 2 * (0.5 - align) * strokeWidth;
|
const anchorX = align * width + 2 * (0.5 - align) * strokeWidth;
|
||||||
const anchorY =
|
const anchorY =
|
||||||
(baseline * label.height) / pixelRatio +
|
(baseline * label.height) / pixelRatio +
|
||||||
@@ -791,7 +796,7 @@ class Executor {
|
|||||||
const originY = /** @type {number} */ (instruction[10]);
|
const originY = /** @type {number} */ (instruction[10]);
|
||||||
const rotateWithView = /** @type {boolean} */ (instruction[11]);
|
const rotateWithView = /** @type {boolean} */ (instruction[11]);
|
||||||
let rotation = /** @type {number} */ (instruction[12]);
|
let rotation = /** @type {number} */ (instruction[12]);
|
||||||
const scale = /** @type {number} */ (instruction[13]);
|
const scale = /** @type {import("../../size.js").Size} */ (instruction[13]);
|
||||||
let width = /** @type {number} */ (instruction[14]);
|
let width = /** @type {number} */ (instruction[14]);
|
||||||
|
|
||||||
if (!image && instruction.length >= 19) {
|
if (!image && instruction.length >= 19) {
|
||||||
@@ -914,11 +919,17 @@ class Executor {
|
|||||||
const strokeWidth = /** @type {number} */ (instruction[11]);
|
const strokeWidth = /** @type {number} */ (instruction[11]);
|
||||||
text = /** @type {string} */ (instruction[12]);
|
text = /** @type {string} */ (instruction[12]);
|
||||||
textKey = /** @type {string} */ (instruction[13]);
|
textKey = /** @type {string} */ (instruction[13]);
|
||||||
const pixelRatioScale = /** @type {number} */ (instruction[14]);
|
const pixelRatioScale = [
|
||||||
|
/** @type {number} */ (instruction[14]),
|
||||||
|
/** @type {number} */ (instruction[14]),
|
||||||
|
];
|
||||||
|
|
||||||
const textState = this.textStates[textKey];
|
const textState = this.textStates[textKey];
|
||||||
const font = textState.font;
|
const font = textState.font;
|
||||||
const textScale = textState.scale * measurePixelRatio;
|
const textScale = [
|
||||||
|
textState.scale[0] * measurePixelRatio,
|
||||||
|
textState.scale[1] * measurePixelRatio,
|
||||||
|
];
|
||||||
|
|
||||||
let cachedWidths;
|
let cachedWidths;
|
||||||
if (font in this.widths_) {
|
if (font in this.widths_) {
|
||||||
@@ -930,7 +941,8 @@ class Executor {
|
|||||||
|
|
||||||
const pathLength = lineStringLength(pixelCoordinates, begin, end, 2);
|
const pathLength = lineStringLength(pixelCoordinates, begin, end, 2);
|
||||||
const textLength =
|
const textLength =
|
||||||
textScale * measureAndCacheTextWidth(font, text, cachedWidths);
|
Math.abs(textScale[0]) *
|
||||||
|
measureAndCacheTextWidth(font, text, cachedWidths);
|
||||||
if (overflow || textLength <= pathLength) {
|
if (overflow || textLength <= pathLength) {
|
||||||
const textAlign = this.textStates[textKey].textAlign;
|
const textAlign = this.textStates[textKey].textAlign;
|
||||||
const startM = (pathLength - textLength) * TEXT_ALIGN[textAlign];
|
const startM = (pathLength - textLength) * TEXT_ALIGN[textAlign];
|
||||||
@@ -942,7 +954,7 @@ class Executor {
|
|||||||
text,
|
text,
|
||||||
startM,
|
startM,
|
||||||
maxAngle,
|
maxAngle,
|
||||||
textScale,
|
Math.abs(textScale[0]),
|
||||||
measureAndCacheTextWidth,
|
measureAndCacheTextWidth,
|
||||||
font,
|
font,
|
||||||
cachedWidths
|
cachedWidths
|
||||||
@@ -958,7 +970,8 @@ class Executor {
|
|||||||
anchorX = /** @type {number} */ (part[2]) + strokeWidth;
|
anchorX = /** @type {number} */ (part[2]) + strokeWidth;
|
||||||
anchorY =
|
anchorY =
|
||||||
baseline * label.height +
|
baseline * label.height +
|
||||||
(0.5 - baseline) * 2 * strokeWidth -
|
((0.5 - baseline) * 2 * strokeWidth * textScale[1]) /
|
||||||
|
textScale[0] -
|
||||||
offsetY;
|
offsetY;
|
||||||
rendered =
|
rendered =
|
||||||
this.replayImageOrLabel_(
|
this.replayImageOrLabel_(
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {number|undefined}
|
* @type {import("../../size.js").Size|undefined}
|
||||||
*/
|
*/
|
||||||
this.scale_ = undefined;
|
this.scale_ = undefined;
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.originY_,
|
this.originY_,
|
||||||
this.rotateWithView_,
|
this.rotateWithView_,
|
||||||
this.rotation_,
|
this.rotation_,
|
||||||
this.scale_ * this.pixelRatio,
|
[this.scale_[0] * this.pixelRatio, this.scale_[1] * this.pixelRatio],
|
||||||
this.width_,
|
this.width_,
|
||||||
]);
|
]);
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
@@ -202,7 +202,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.originY_,
|
this.originY_,
|
||||||
this.rotateWithView_,
|
this.rotateWithView_,
|
||||||
this.rotation_,
|
this.rotation_,
|
||||||
this.scale_ * this.pixelRatio,
|
[this.scale_[0] * this.pixelRatio, this.scale_[1] * this.pixelRatio],
|
||||||
this.width_,
|
this.width_,
|
||||||
]);
|
]);
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
@@ -268,7 +268,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.originY_ = origin[1];
|
this.originY_ = origin[1];
|
||||||
this.rotateWithView_ = imageStyle.getRotateWithView();
|
this.rotateWithView_ = imageStyle.getRotateWithView();
|
||||||
this.rotation_ = imageStyle.getRotation();
|
this.rotation_ = imageStyle.getRotation();
|
||||||
this.scale_ = imageStyle.getScale();
|
this.scale_ = imageStyle.getScaleArray();
|
||||||
this.width_ = size[0];
|
this.width_ = size[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,9 +188,9 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {number}
|
* @type {import("../../size.js").Size}
|
||||||
*/
|
*/
|
||||||
this.imageScale_ = 0;
|
this.imageScale_ = [0, 0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -230,9 +230,9 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {number}
|
* @type {import("../../size.js").Size}
|
||||||
*/
|
*/
|
||||||
this.textScale_ = 0;
|
this.textScale_ = [0, 0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -297,35 +297,51 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
for (let i = 0, ii = pixelCoordinates.length; i < ii; i += 2) {
|
for (let i = 0, ii = pixelCoordinates.length; i < ii; i += 2) {
|
||||||
const x = pixelCoordinates[i] - this.imageAnchorX_;
|
const x = pixelCoordinates[i] - this.imageAnchorX_;
|
||||||
const y = pixelCoordinates[i + 1] - this.imageAnchorY_;
|
const y = pixelCoordinates[i + 1] - this.imageAnchorY_;
|
||||||
if (rotation !== 0 || this.imageScale_ != 1) {
|
if (
|
||||||
|
rotation !== 0 ||
|
||||||
|
this.imageScale_[0] != 1 ||
|
||||||
|
this.imageScale_[1] != 1
|
||||||
|
) {
|
||||||
const centerX = x + this.imageAnchorX_;
|
const centerX = x + this.imageAnchorX_;
|
||||||
const centerY = y + this.imageAnchorY_;
|
const centerY = y + this.imageAnchorY_;
|
||||||
composeTransform(
|
composeTransform(
|
||||||
localTransform,
|
localTransform,
|
||||||
centerX,
|
centerX,
|
||||||
centerY,
|
centerY,
|
||||||
this.imageScale_,
|
1,
|
||||||
this.imageScale_,
|
1,
|
||||||
rotation,
|
rotation,
|
||||||
-centerX,
|
-centerX,
|
||||||
-centerY
|
-centerY
|
||||||
);
|
);
|
||||||
context.setTransform.apply(context, localTransform);
|
context.setTransform.apply(context, localTransform);
|
||||||
|
context.translate(centerX, centerY);
|
||||||
|
context.scale(this.imageScale_[0], this.imageScale_[1]);
|
||||||
|
context.drawImage(
|
||||||
|
this.image_,
|
||||||
|
this.imageOriginX_,
|
||||||
|
this.imageOriginY_,
|
||||||
|
this.imageWidth_,
|
||||||
|
this.imageHeight_,
|
||||||
|
-this.imageAnchorX_,
|
||||||
|
-this.imageAnchorY_,
|
||||||
|
this.imageWidth_,
|
||||||
|
this.imageHeight_
|
||||||
|
);
|
||||||
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
} else {
|
||||||
|
context.drawImage(
|
||||||
|
this.image_,
|
||||||
|
this.imageOriginX_,
|
||||||
|
this.imageOriginY_,
|
||||||
|
this.imageWidth_,
|
||||||
|
this.imageHeight_,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
this.imageWidth_,
|
||||||
|
this.imageHeight_
|
||||||
|
);
|
||||||
}
|
}
|
||||||
context.drawImage(
|
|
||||||
this.image_,
|
|
||||||
this.imageOriginX_,
|
|
||||||
this.imageOriginY_,
|
|
||||||
this.imageWidth_,
|
|
||||||
this.imageHeight_,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
this.imageWidth_,
|
|
||||||
this.imageHeight_
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (rotation !== 0 || this.imageScale_ != 1) {
|
|
||||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
||||||
}
|
}
|
||||||
if (this.imageOpacity_ != 1) {
|
if (this.imageOpacity_ != 1) {
|
||||||
context.globalAlpha = alpha;
|
context.globalAlpha = alpha;
|
||||||
@@ -366,28 +382,39 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
for (; offset < end; offset += stride) {
|
for (; offset < end; offset += stride) {
|
||||||
const x = pixelCoordinates[offset] + this.textOffsetX_;
|
const x = pixelCoordinates[offset] + this.textOffsetX_;
|
||||||
const y = pixelCoordinates[offset + 1] + this.textOffsetY_;
|
const y = pixelCoordinates[offset + 1] + this.textOffsetY_;
|
||||||
if (rotation !== 0 || this.textScale_ != 1) {
|
if (
|
||||||
|
rotation !== 0 ||
|
||||||
|
this.textScale_[0] != 1 ||
|
||||||
|
this.textScale_[1] != 1
|
||||||
|
) {
|
||||||
const localTransform = composeTransform(
|
const localTransform = composeTransform(
|
||||||
this.tmpLocalTransform_,
|
this.tmpLocalTransform_,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
this.textScale_,
|
1,
|
||||||
this.textScale_,
|
1,
|
||||||
rotation,
|
rotation,
|
||||||
-x,
|
-x,
|
||||||
-y
|
-y
|
||||||
);
|
);
|
||||||
context.setTransform.apply(context, localTransform);
|
context.setTransform.apply(context, localTransform);
|
||||||
|
context.translate(x, y);
|
||||||
|
context.scale(this.textScale_[0], this.textScale_[1]);
|
||||||
|
if (this.textStrokeState_) {
|
||||||
|
context.strokeText(this.text_, 0, 0);
|
||||||
|
}
|
||||||
|
if (this.textFillState_) {
|
||||||
|
context.fillText(this.text_, 0, 0);
|
||||||
|
}
|
||||||
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
} else {
|
||||||
|
if (this.textStrokeState_) {
|
||||||
|
context.strokeText(this.text_, x, y);
|
||||||
|
}
|
||||||
|
if (this.textFillState_) {
|
||||||
|
context.fillText(this.text_, x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.textStrokeState_) {
|
|
||||||
context.strokeText(this.text_, x, y);
|
|
||||||
}
|
|
||||||
if (this.textFillState_) {
|
|
||||||
context.fillText(this.text_, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rotation !== 0 || this.textScale_ != 1) {
|
|
||||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,22 +1021,30 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
if (!imageStyle) {
|
if (!imageStyle) {
|
||||||
this.image_ = null;
|
this.image_ = null;
|
||||||
} else {
|
} else {
|
||||||
const imageAnchor = imageStyle.getAnchor();
|
|
||||||
// FIXME pixel ratio
|
|
||||||
const imageImage = imageStyle.getImage(1);
|
|
||||||
const imageOrigin = imageStyle.getOrigin();
|
|
||||||
const imageSize = imageStyle.getSize();
|
const imageSize = imageStyle.getSize();
|
||||||
this.imageAnchorX_ = imageAnchor[0];
|
if (!imageSize) {
|
||||||
this.imageAnchorY_ = imageAnchor[1];
|
this.image_ = null;
|
||||||
this.imageHeight_ = imageSize[1];
|
} else {
|
||||||
this.image_ = imageImage;
|
const imageAnchor = imageStyle.getAnchor();
|
||||||
this.imageOpacity_ = imageStyle.getOpacity();
|
// FIXME pixel ratio
|
||||||
this.imageOriginX_ = imageOrigin[0];
|
const imageImage = imageStyle.getImage(1);
|
||||||
this.imageOriginY_ = imageOrigin[1];
|
const imageOrigin = imageStyle.getOrigin();
|
||||||
this.imageRotateWithView_ = imageStyle.getRotateWithView();
|
const imageScale = imageStyle.getScaleArray();
|
||||||
this.imageRotation_ = imageStyle.getRotation();
|
this.imageAnchorX_ = imageAnchor[0];
|
||||||
this.imageScale_ = imageStyle.getScale() * this.pixelRatio_;
|
this.imageAnchorY_ = imageAnchor[1];
|
||||||
this.imageWidth_ = imageSize[0];
|
this.imageHeight_ = imageSize[1];
|
||||||
|
this.image_ = imageImage;
|
||||||
|
this.imageOpacity_ = imageStyle.getOpacity();
|
||||||
|
this.imageOriginX_ = imageOrigin[0];
|
||||||
|
this.imageOriginY_ = imageOrigin[1];
|
||||||
|
this.imageRotateWithView_ = imageStyle.getRotateWithView();
|
||||||
|
this.imageRotation_ = imageStyle.getRotation();
|
||||||
|
this.imageScale_ = [
|
||||||
|
this.pixelRatio_ * imageScale[0],
|
||||||
|
this.pixelRatio_ * imageScale[1],
|
||||||
|
];
|
||||||
|
this.imageWidth_ = imageSize[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1078,7 +1113,7 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
const textOffsetY = textStyle.getOffsetY();
|
const textOffsetY = textStyle.getOffsetY();
|
||||||
const textRotateWithView = textStyle.getRotateWithView();
|
const textRotateWithView = textStyle.getRotateWithView();
|
||||||
const textRotation = textStyle.getRotation();
|
const textRotation = textStyle.getRotation();
|
||||||
const textScale = textStyle.getScale();
|
const textScale = textStyle.getScaleArray();
|
||||||
const textText = textStyle.getText();
|
const textText = textStyle.getText();
|
||||||
const textTextAlign = textStyle.getTextAlign();
|
const textTextAlign = textStyle.getTextAlign();
|
||||||
const textTextBaseline = textStyle.getTextBaseline();
|
const textTextBaseline = textStyle.getTextBaseline();
|
||||||
@@ -1099,8 +1134,10 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
this.textRotateWithView_ =
|
this.textRotateWithView_ =
|
||||||
textRotateWithView !== undefined ? textRotateWithView : false;
|
textRotateWithView !== undefined ? textRotateWithView : false;
|
||||||
this.textRotation_ = textRotation !== undefined ? textRotation : 0;
|
this.textRotation_ = textRotation !== undefined ? textRotation : 0;
|
||||||
this.textScale_ =
|
this.textScale_ = [
|
||||||
this.pixelRatio_ * (textScale !== undefined ? textScale : 1);
|
this.pixelRatio_ * textScale[0],
|
||||||
|
this.pixelRatio_ * textScale[1],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,6 +303,27 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
|
|
||||||
this.beginGeometry(geometry, feature);
|
this.beginGeometry(geometry, feature);
|
||||||
|
|
||||||
|
// adjust padding for negative scale
|
||||||
|
let padding = textState.padding;
|
||||||
|
if (
|
||||||
|
padding != defaultPadding &&
|
||||||
|
(textState.scale[0] < 0 || textState.scale[1] < 0)
|
||||||
|
) {
|
||||||
|
let p0 = textState.padding[0];
|
||||||
|
let p1 = textState.padding[1];
|
||||||
|
let p2 = textState.padding[2];
|
||||||
|
let p3 = textState.padding[3];
|
||||||
|
if (textState.scale[0] < 0) {
|
||||||
|
p1 = -p1;
|
||||||
|
p3 = -p3;
|
||||||
|
}
|
||||||
|
if (textState.scale[1] < 0) {
|
||||||
|
p0 = -p0;
|
||||||
|
p2 = -p2;
|
||||||
|
}
|
||||||
|
padding = [p0, p1, p2, p3];
|
||||||
|
}
|
||||||
|
|
||||||
// The image is unknown at this stage so we pass null; it will be computed at render time.
|
// The image is unknown at this stage so we pass null; it will be computed at render time.
|
||||||
// For clarity, we pass NaN for offsetX, offsetY, width and height, which will be computed at
|
// For clarity, we pass NaN for offsetX, offsetY, width and height, which will be computed at
|
||||||
// render time.
|
// render time.
|
||||||
@@ -321,11 +342,11 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
0,
|
0,
|
||||||
this.textRotateWithView_,
|
this.textRotateWithView_,
|
||||||
this.textRotation_,
|
this.textRotation_,
|
||||||
1,
|
[1, 1],
|
||||||
NaN,
|
NaN,
|
||||||
textState.padding == defaultPadding
|
padding == defaultPadding
|
||||||
? defaultPadding
|
? defaultPadding
|
||||||
: textState.padding.map(function (p) {
|
: padding.map(function (p) {
|
||||||
return p * pixelRatio;
|
return p * pixelRatio;
|
||||||
}),
|
}),
|
||||||
!!textState.backgroundFill,
|
!!textState.backgroundFill,
|
||||||
@@ -338,6 +359,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
this.textOffsetY_,
|
this.textOffsetY_,
|
||||||
geometryWidths,
|
geometryWidths,
|
||||||
]);
|
]);
|
||||||
|
const scale = 1 / pixelRatio;
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
CanvasInstruction.DRAW_IMAGE,
|
CanvasInstruction.DRAW_IMAGE,
|
||||||
begin,
|
begin,
|
||||||
@@ -352,9 +374,9 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
0,
|
0,
|
||||||
this.textRotateWithView_,
|
this.textRotateWithView_,
|
||||||
this.textRotation_,
|
this.textRotation_,
|
||||||
1 / this.pixelRatio,
|
[scale, scale],
|
||||||
NaN,
|
NaN,
|
||||||
textState.padding,
|
padding,
|
||||||
!!textState.backgroundFill,
|
!!textState.backgroundFill,
|
||||||
!!textState.backgroundStroke,
|
!!textState.backgroundStroke,
|
||||||
this.text_,
|
this.text_,
|
||||||
@@ -431,9 +453,8 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
|
|
||||||
const offsetY = this.textOffsetY_ * pixelRatio;
|
const offsetY = this.textOffsetY_ * pixelRatio;
|
||||||
const text = this.text_;
|
const text = this.text_;
|
||||||
const textScale = textState.scale;
|
|
||||||
const strokeWidth = strokeState
|
const strokeWidth = strokeState
|
||||||
? (strokeState.lineWidth * textScale) / 2
|
? (strokeState.lineWidth * Math.abs(textState.scale[0])) / 2
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
this.instructions.push([
|
this.instructions.push([
|
||||||
@@ -529,7 +550,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
textState = this.textState_;
|
textState = this.textState_;
|
||||||
const font = textStyle.getFont() || defaultFont;
|
const font = textStyle.getFont() || defaultFont;
|
||||||
registerFont(font);
|
registerFont(font);
|
||||||
const textScale = textStyle.getScale();
|
const textScale = textStyle.getScaleArray();
|
||||||
textState.overflow = textStyle.getOverflow();
|
textState.overflow = textStyle.getOverflow();
|
||||||
textState.font = font;
|
textState.font = font;
|
||||||
textState.maxAngle = textStyle.getMaxAngle();
|
textState.maxAngle = textStyle.getMaxAngle();
|
||||||
@@ -540,7 +561,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
textState.backgroundFill = textStyle.getBackgroundFill();
|
textState.backgroundFill = textStyle.getBackgroundFill();
|
||||||
textState.backgroundStroke = textStyle.getBackgroundStroke();
|
textState.backgroundStroke = textStyle.getBackgroundStroke();
|
||||||
textState.padding = textStyle.getPadding() || defaultPadding;
|
textState.padding = textStyle.getPadding() || defaultPadding;
|
||||||
textState.scale = textScale === undefined ? 1 : textScale;
|
textState.scale = textScale === undefined ? [1, 1] : textScale;
|
||||||
|
|
||||||
const textOffsetX = textStyle.getOffsetX();
|
const textOffsetX = textStyle.getOffsetX();
|
||||||
const textOffsetY = textStyle.getOffsetY();
|
const textOffsetY = textStyle.getOffsetY();
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {getUid} from '../util.js';
|
|||||||
* @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.
|
||||||
* @property {number} [scale=1] Scale.
|
* @property {number|import("../size.js").Size} [scale=1] Scale.
|
||||||
* @property {boolean} [rotateWithView=false] Whether to rotate the icon with the view.
|
* @property {boolean} [rotateWithView=false] Whether to rotate the icon with the view.
|
||||||
* @property {number} [rotation=0] Rotation in radians (positive rotation clockwise).
|
* @property {number} [rotation=0] Rotation in radians (positive rotation clockwise).
|
||||||
* @property {import("../size.js").Size} [size] Icon size in pixel. Can be used together with `offset` to define the
|
* @property {import("../size.js").Size} [size] Icon size in pixel. Can be used together with `offset` to define the
|
||||||
@@ -69,7 +69,7 @@ class Icon extends ImageStyle {
|
|||||||
const rotation = options.rotation !== undefined ? options.rotation : 0;
|
const rotation = options.rotation !== undefined ? options.rotation : 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number|import("../size.js").Size}
|
||||||
*/
|
*/
|
||||||
const scale = options.scale !== undefined ? options.scale : 1;
|
const scale = options.scale !== undefined ? options.scale : 1;
|
||||||
|
|
||||||
@@ -215,6 +215,7 @@ class Icon extends ImageStyle {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
|
const scale = this.getScale();
|
||||||
return new Icon({
|
return new Icon({
|
||||||
anchor: this.anchor_.slice(),
|
anchor: this.anchor_.slice(),
|
||||||
anchorOrigin: this.anchorOrigin_,
|
anchorOrigin: this.anchorOrigin_,
|
||||||
@@ -230,7 +231,7 @@ class Icon extends ImageStyle {
|
|||||||
offsetOrigin: this.offsetOrigin_,
|
offsetOrigin: this.offsetOrigin_,
|
||||||
size: this.size_ !== null ? this.size_.slice() : undefined,
|
size: this.size_ !== null ? this.size_.slice() : undefined,
|
||||||
opacity: this.getOpacity(),
|
opacity: this.getOpacity(),
|
||||||
scale: this.getScale(),
|
scale: Array.isArray(scale) ? scale.slice() : scale,
|
||||||
rotation: this.getRotation(),
|
rotation: this.getRotation(),
|
||||||
rotateWithView: this.getRotateWithView(),
|
rotateWithView: this.getRotateWithView(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
* @module ol/style/Image
|
* @module ol/style/Image
|
||||||
*/
|
*/
|
||||||
import {abstract} from '../util.js';
|
import {abstract} from '../util.js';
|
||||||
|
import {toSize} from '../size.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Options
|
* @typedef {Object} Options
|
||||||
* @property {number} opacity
|
* @property {number} opacity
|
||||||
* @property {boolean} rotateWithView
|
* @property {boolean} rotateWithView
|
||||||
* @property {number} rotation
|
* @property {number} rotation
|
||||||
* @property {number} scale
|
* @property {number|import("../size.js").Size} scale
|
||||||
* @property {Array<number>} displacement
|
* @property {Array<number>} displacement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -45,10 +46,16 @@ class ImageStyle {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {number}
|
* @type {number|import("../size.js").Size}
|
||||||
*/
|
*/
|
||||||
this.scale_ = options.scale;
|
this.scale_ = options.scale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {import("../size.js").Size}
|
||||||
|
*/
|
||||||
|
this.scaleArray_ = toSize(options.scale);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {Array<number>}
|
* @type {Array<number>}
|
||||||
@@ -62,9 +69,10 @@ class ImageStyle {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
|
const scale = this.getScale();
|
||||||
return new ImageStyle({
|
return new ImageStyle({
|
||||||
opacity: this.getOpacity(),
|
opacity: this.getOpacity(),
|
||||||
scale: this.getScale(),
|
scale: Array.isArray(scale) ? scale.slice() : scale,
|
||||||
rotation: this.getRotation(),
|
rotation: this.getRotation(),
|
||||||
rotateWithView: this.getRotateWithView(),
|
rotateWithView: this.getRotateWithView(),
|
||||||
displacement: this.getDisplacement().slice(),
|
displacement: this.getDisplacement().slice(),
|
||||||
@@ -100,13 +108,21 @@ class ImageStyle {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the symbolizer scale.
|
* Get the symbolizer scale.
|
||||||
* @return {number} Scale.
|
* @return {number|import("../size.js").Size} Scale.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
getScale() {
|
getScale() {
|
||||||
return this.scale_;
|
return this.scale_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the symbolizer scale array.
|
||||||
|
* @return {import("../size.js").Size} Scale array.
|
||||||
|
*/
|
||||||
|
getScaleArray() {
|
||||||
|
return this.scaleArray_;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the displacement of the shape
|
* Get the displacement of the shape
|
||||||
* @return {Array<number>} Shape's center displacement
|
* @return {Array<number>} Shape's center displacement
|
||||||
@@ -219,11 +235,12 @@ class ImageStyle {
|
|||||||
/**
|
/**
|
||||||
* Set the scale.
|
* Set the scale.
|
||||||
*
|
*
|
||||||
* @param {number} scale Scale.
|
* @param {number|import("../size.js").Size} scale Scale.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
setScale(scale) {
|
setScale(scale) {
|
||||||
this.scale_ = scale;
|
this.scale_ = scale;
|
||||||
|
this.scaleArray_ = toSize(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import Fill from './Fill.js';
|
import Fill from './Fill.js';
|
||||||
import TextPlacement from './TextPlacement.js';
|
import TextPlacement from './TextPlacement.js';
|
||||||
|
import {toSize} from '../size.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default fill color to use if no fill was set at construction time; a
|
* The default fill color to use if no fill was set at construction time; a
|
||||||
@@ -23,7 +24,7 @@ const DEFAULT_FILL_COLOR = '#333';
|
|||||||
* @property {boolean} [overflow=false] For polygon labels or when `placement` is set to `'line'`, allow text to exceed
|
* @property {boolean} [overflow=false] For polygon labels or when `placement` is set to `'line'`, allow text to exceed
|
||||||
* the width of the polygon at the label position or the length of the path that it follows.
|
* the width of the polygon at the label position or the length of the path that it follows.
|
||||||
* @property {import("./TextPlacement.js").default|string} [placement='point'] Text placement.
|
* @property {import("./TextPlacement.js").default|string} [placement='point'] Text placement.
|
||||||
* @property {number} [scale] Scale.
|
* @property {number|import("../size.js").Size} [scale] Scale.
|
||||||
* @property {boolean} [rotateWithView=false] Whether to rotate the text with the view.
|
* @property {boolean} [rotateWithView=false] Whether to rotate the text with the view.
|
||||||
* @property {number} [rotation=0] Rotation in radians (positive rotation clockwise).
|
* @property {number} [rotation=0] Rotation in radians (positive rotation clockwise).
|
||||||
* @property {string} [text] Text content.
|
* @property {string} [text] Text content.
|
||||||
@@ -74,10 +75,16 @@ class Text {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {number|undefined}
|
* @type {number|import("../size.js").Size|undefined}
|
||||||
*/
|
*/
|
||||||
this.scale_ = options.scale;
|
this.scale_ = options.scale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {import("../size.js").Size}
|
||||||
|
*/
|
||||||
|
this.scaleArray_ = toSize(options.scale !== undefined ? options.scale : 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {string|undefined}
|
* @type {string|undefined}
|
||||||
@@ -172,6 +179,7 @@ class Text {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
|
const scale = this.getScale();
|
||||||
return new Text({
|
return new Text({
|
||||||
font: this.getFont(),
|
font: this.getFont(),
|
||||||
placement: this.getPlacement(),
|
placement: this.getPlacement(),
|
||||||
@@ -179,7 +187,7 @@ class Text {
|
|||||||
overflow: this.getOverflow(),
|
overflow: this.getOverflow(),
|
||||||
rotation: this.getRotation(),
|
rotation: this.getRotation(),
|
||||||
rotateWithView: this.getRotateWithView(),
|
rotateWithView: this.getRotateWithView(),
|
||||||
scale: this.getScale(),
|
scale: Array.isArray(scale) ? scale.slice() : scale,
|
||||||
text: this.getText(),
|
text: this.getText(),
|
||||||
textAlign: this.getTextAlign(),
|
textAlign: this.getTextAlign(),
|
||||||
textBaseline: this.getTextBaseline(),
|
textBaseline: this.getTextBaseline(),
|
||||||
@@ -280,13 +288,21 @@ class Text {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text scale.
|
* Get the text scale.
|
||||||
* @return {number|undefined} Scale.
|
* @return {number|import("../size.js").Size|undefined} Scale.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
getScale() {
|
getScale() {
|
||||||
return this.scale_;
|
return this.scale_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the symbolizer scale array.
|
||||||
|
* @return {import("../size.js").Size} Scale array.
|
||||||
|
*/
|
||||||
|
getScaleArray() {
|
||||||
|
return this.scaleArray_;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the stroke style for the text.
|
* Get the stroke style for the text.
|
||||||
* @return {import("./Stroke.js").default} Stroke style.
|
* @return {import("./Stroke.js").default} Stroke style.
|
||||||
@@ -443,11 +459,12 @@ class Text {
|
|||||||
/**
|
/**
|
||||||
* Set the scale.
|
* Set the scale.
|
||||||
*
|
*
|
||||||
* @param {number|undefined} scale Scale.
|
* @param {number|import("../size.js").Size|undefined} scale Scale.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
setScale(scale) {
|
setScale(scale) {
|
||||||
this.scale_ = scale;
|
this.scale_ = scale;
|
||||||
|
this.scaleArray_ = toSize(scale !== undefined ? scale : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||