Rich text labels
This commit is contained in:
9
examples/rich-text-labels.html
Normal file
9
examples/rich-text-labels.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Rich Text Labels
|
||||||
|
shortdesc: Rich text labels.
|
||||||
|
docs: >
|
||||||
|
The labels in this map use different fonts to create clear context - an alphabetic sort key prefixing the state name in bold, and the population density in an extra line with a smaller font and italic.
|
||||||
|
tags: "vector, rich-text, labels"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
64
examples/rich-text-labels.js
Normal file
64
examples/rich-text-labels.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||||
|
import Map from '../src/ol/Map.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, Stroke, Style, Text} from '../src/ol/style.js';
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2,
|
||||||
|
extent: [-13882269, 2890586, -7456136, 6340207],
|
||||||
|
showFullExtent: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const labelStyle = new Style({
|
||||||
|
text: new Text({
|
||||||
|
font: '13px Calibri,sans-serif',
|
||||||
|
fill: new Fill({
|
||||||
|
color: '#000',
|
||||||
|
}),
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: '#fff',
|
||||||
|
width: 4,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const countryStyle = new Style({
|
||||||
|
fill: new Fill({
|
||||||
|
color: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
}),
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: '#319FD3',
|
||||||
|
width: 1,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const style = [countryStyle, labelStyle];
|
||||||
|
|
||||||
|
const vectorLayer = new VectorLayer({
|
||||||
|
background: 'white',
|
||||||
|
source: new VectorSource({
|
||||||
|
url: 'https://openlayers.org/data/vector/us-states.json',
|
||||||
|
format: new GeoJSON(),
|
||||||
|
}),
|
||||||
|
style: function (feature) {
|
||||||
|
labelStyle
|
||||||
|
.getText()
|
||||||
|
.setText([
|
||||||
|
feature.getId(),
|
||||||
|
'bold 13px Calibri,sans-serif',
|
||||||
|
` ${feature.get('name')}`,
|
||||||
|
'',
|
||||||
|
'\n',
|
||||||
|
'',
|
||||||
|
`${feature.get('density')} people/mi²`,
|
||||||
|
'italic 11px Calibri,sans-serif',
|
||||||
|
]);
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
@@ -2,8 +2,6 @@
|
|||||||
layout: example.html
|
layout: example.html
|
||||||
title: Vector Label Decluttering
|
title: Vector Label Decluttering
|
||||||
shortdesc: Label decluttering on polygons.
|
shortdesc: Label decluttering on polygons.
|
||||||
resources:
|
|
||||||
- https://cdn.polyfill.io/v2/polyfill.min.js?features=Set"
|
|
||||||
docs: >
|
docs: >
|
||||||
Decluttering is used to avoid overlapping labels. The `overflow: true` setting on the text style makes it so labels that do not fit within the bounds of a polygon are also considered for decluttering.
|
Decluttering is used to avoid overlapping labels. The `overflow: true` setting on the text style makes it so labels that do not fit within the bounds of a polygon are also considered for decluttering.
|
||||||
tags: "vector, decluttering, labels"
|
tags: "vector, decluttering, labels"
|
||||||
|
|||||||
@@ -367,21 +367,36 @@ export function measureAndCacheTextWidth(font, text, cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} font Font to use for measuring.
|
* @param {TextState} baseStyle Base style.
|
||||||
* @param {Array<string>} lines Lines to measure.
|
* @param {Array<string>} chunks Text chunks to measure.
|
||||||
* @param {Array<number>} widths Array will be populated with the widths of
|
* @return {{width: number, height: number, widths: Array<number>, heights: Array<number>, lineWidths: Array<number>}}} Text metrics.
|
||||||
* each line.
|
|
||||||
* @return {number} Width of the whole text.
|
|
||||||
*/
|
*/
|
||||||
export function measureTextWidths(font, lines, widths) {
|
export function getTextDimensions(baseStyle, chunks) {
|
||||||
const numLines = lines.length;
|
const widths = [];
|
||||||
|
const heights = [];
|
||||||
|
const lineWidths = [];
|
||||||
let width = 0;
|
let width = 0;
|
||||||
for (let i = 0; i < numLines; ++i) {
|
let lineWidth = 0;
|
||||||
const currentWidth = measureTextWidth(font, lines[i]);
|
let height = 0;
|
||||||
width = Math.max(width, currentWidth);
|
let lineHeight = 0;
|
||||||
widths.push(currentWidth);
|
for (let i = 0, ii = chunks.length; i <= ii; i += 2) {
|
||||||
|
const text = chunks[i];
|
||||||
|
if (text === '\n' || i === ii) {
|
||||||
|
width = Math.max(width, lineWidth);
|
||||||
|
lineWidths.push(lineWidth);
|
||||||
|
lineWidth = 0;
|
||||||
|
height += lineHeight;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
return width;
|
const font = chunks[i + 1] || baseStyle.font;
|
||||||
|
const currentWidth = measureTextWidth(font, text);
|
||||||
|
widths.push(currentWidth);
|
||||||
|
lineWidth += currentWidth;
|
||||||
|
const currentHeight = measureTextHeight(font);
|
||||||
|
heights.push(currentHeight);
|
||||||
|
lineHeight = Math.max(lineHeight, currentHeight);
|
||||||
|
}
|
||||||
|
return {width, height, widths, heights, lineWidths};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ import {
|
|||||||
defaultTextAlign,
|
defaultTextAlign,
|
||||||
defaultTextBaseline,
|
defaultTextBaseline,
|
||||||
drawImageOrLabel,
|
drawImageOrLabel,
|
||||||
|
getTextDimensions,
|
||||||
measureAndCacheTextWidth,
|
measureAndCacheTextWidth,
|
||||||
measureTextHeight,
|
|
||||||
measureTextWidths,
|
|
||||||
} from '../canvas.js';
|
} from '../canvas.js';
|
||||||
import {drawTextOnPath} from '../../geom/flat/textpath.js';
|
import {drawTextOnPath} from '../../geom/flat/textpath.js';
|
||||||
import {equals} from '../../array.js';
|
import {equals} from '../../array.js';
|
||||||
@@ -102,6 +101,20 @@ function horizontalTextAlign(text, align) {
|
|||||||
return TEXT_ALIGN[align];
|
return TEXT_ALIGN[align];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<string>} acc Accumulator.
|
||||||
|
* @param {string} line Line of text.
|
||||||
|
* @param {number} i Index
|
||||||
|
* @return {Array<string>} Accumulator.
|
||||||
|
*/
|
||||||
|
function createTextChunks(acc, line, i) {
|
||||||
|
if (i > 0) {
|
||||||
|
acc.push('\n', '');
|
||||||
|
}
|
||||||
|
acc.push(line, '');
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
class Executor {
|
class Executor {
|
||||||
/**
|
/**
|
||||||
* @param {number} resolution Resolution.
|
* @param {number} resolution Resolution.
|
||||||
@@ -206,7 +219,7 @@ class Executor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} text Text.
|
* @param {string|Array<string>} text Text.
|
||||||
* @param {string} textKey Text style key.
|
* @param {string} textKey Text style key.
|
||||||
* @param {string} fillKey Fill style key.
|
* @param {string} fillKey Fill style key.
|
||||||
* @param {string} strokeKey Stroke style key.
|
* @param {string} strokeKey Stroke style key.
|
||||||
@@ -225,19 +238,22 @@ class Executor {
|
|||||||
textState.scale[0] * pixelRatio,
|
textState.scale[0] * pixelRatio,
|
||||||
textState.scale[1] * pixelRatio,
|
textState.scale[1] * pixelRatio,
|
||||||
];
|
];
|
||||||
|
const textIsArray = Array.isArray(text);
|
||||||
const align = horizontalTextAlign(
|
const align = horizontalTextAlign(
|
||||||
text,
|
textIsArray ? text[0] : text,
|
||||||
textState.textAlign || defaultTextAlign
|
textState.textAlign || defaultTextAlign
|
||||||
);
|
);
|
||||||
const strokeWidth =
|
const strokeWidth =
|
||||||
strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
||||||
|
|
||||||
const lines = text.split('\n');
|
const chunks = textIsArray
|
||||||
const numLines = lines.length;
|
? text
|
||||||
const widths = [];
|
: text.split('\n').reduce(createTextChunks, []);
|
||||||
const width = measureTextWidths(textState.font, lines, widths);
|
|
||||||
const lineHeight = measureTextHeight(textState.font);
|
const {width, height, widths, heights, lineWidths} = getTextDimensions(
|
||||||
const height = lineHeight * numLines;
|
textState,
|
||||||
|
chunks
|
||||||
|
);
|
||||||
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
|
// make canvas 2 pixels wider to account for italic text width measurement errors
|
||||||
@@ -252,7 +268,6 @@ class Executor {
|
|||||||
if (scale[0] != 1 || scale[1] != 1) {
|
if (scale[0] != 1 || scale[1] != 1) {
|
||||||
contextInstructions.push('scale', scale);
|
contextInstructions.push('scale', scale);
|
||||||
}
|
}
|
||||||
contextInstructions.push('font', textState.font);
|
|
||||||
if (strokeKey) {
|
if (strokeKey) {
|
||||||
contextInstructions.push('strokeStyle', strokeState.strokeStyle);
|
contextInstructions.push('strokeStyle', strokeState.strokeStyle);
|
||||||
contextInstructions.push('lineWidth', strokeWidth);
|
contextInstructions.push('lineWidth', strokeWidth);
|
||||||
@@ -272,26 +287,52 @@ class Executor {
|
|||||||
contextInstructions.push('textBaseline', 'middle');
|
contextInstructions.push('textBaseline', 'middle');
|
||||||
contextInstructions.push('textAlign', 'center');
|
contextInstructions.push('textAlign', 'center');
|
||||||
const leftRight = 0.5 - align;
|
const leftRight = 0.5 - align;
|
||||||
const x = align * renderWidth + leftRight * strokeWidth;
|
let x = align * renderWidth + leftRight * strokeWidth;
|
||||||
let i;
|
const strokeInstructions = [];
|
||||||
if (strokeKey) {
|
const fillInstructions = [];
|
||||||
for (i = 0; i < numLines; ++i) {
|
let lineHeight = 0;
|
||||||
contextInstructions.push('strokeText', [
|
let lineOffset = 0;
|
||||||
lines[i],
|
let widthHeightIndex = 0;
|
||||||
x + leftRight * widths[i],
|
let lineWidthIndex = 0;
|
||||||
0.5 * (strokeWidth + lineHeight) + i * lineHeight,
|
let previousFont;
|
||||||
]);
|
for (let i = 0, ii = chunks.length; i < ii; i += 2) {
|
||||||
|
const text = chunks[i];
|
||||||
|
if (text === '\n') {
|
||||||
|
lineOffset += lineHeight;
|
||||||
|
lineHeight = 0;
|
||||||
|
x = align * renderWidth + leftRight * strokeWidth;
|
||||||
|
++lineWidthIndex;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
const font = chunks[i + 1] || textState.font;
|
||||||
|
if (font !== previousFont) {
|
||||||
|
if (strokeKey) {
|
||||||
|
strokeInstructions.push('font', font);
|
||||||
}
|
}
|
||||||
if (fillKey) {
|
if (fillKey) {
|
||||||
for (i = 0; i < numLines; ++i) {
|
fillInstructions.push('font', font);
|
||||||
contextInstructions.push('fillText', [
|
|
||||||
lines[i],
|
|
||||||
x + leftRight * widths[i],
|
|
||||||
0.5 * (strokeWidth + lineHeight) + i * lineHeight,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
previousFont = font;
|
||||||
}
|
}
|
||||||
|
lineHeight = Math.max(lineHeight, heights[widthHeightIndex]);
|
||||||
|
const fillStrokeArgs = [
|
||||||
|
text,
|
||||||
|
x +
|
||||||
|
leftRight * widths[widthHeightIndex] +
|
||||||
|
align * (widths[widthHeightIndex] - lineWidths[lineWidthIndex]),
|
||||||
|
0.5 * (strokeWidth + lineHeight) + lineOffset,
|
||||||
|
];
|
||||||
|
x += widths[widthHeightIndex];
|
||||||
|
if (strokeKey) {
|
||||||
|
strokeInstructions.push('strokeText', fillStrokeArgs);
|
||||||
|
}
|
||||||
|
if (fillKey) {
|
||||||
|
fillInstructions.push('fillText', fillStrokeArgs);
|
||||||
|
}
|
||||||
|
++widthHeightIndex;
|
||||||
|
}
|
||||||
|
Array.prototype.push.apply(contextInstructions, strokeInstructions);
|
||||||
|
Array.prototype.push.apply(contextInstructions, fillInstructions);
|
||||||
this.labels_[key] = label;
|
this.labels_[key] = label;
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
@@ -550,7 +591,7 @@ class Executor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} text The text to draw.
|
* @param {string|Array<string>} text The text to draw.
|
||||||
* @param {string} textKey The key of the text state.
|
* @param {string} textKey The key of the text state.
|
||||||
* @param {string} strokeKey The key for the stroke state.
|
* @param {string} strokeKey The key for the stroke state.
|
||||||
* @param {string} fillKey The key for the fill state.
|
* @param {string} fillKey The key for the fill state.
|
||||||
@@ -564,7 +605,7 @@ class Executor {
|
|||||||
const strokeState = this.strokeStates[strokeKey];
|
const strokeState = this.strokeStates[strokeKey];
|
||||||
const pixelRatio = this.pixelRatio;
|
const pixelRatio = this.pixelRatio;
|
||||||
const align = horizontalTextAlign(
|
const align = horizontalTextAlign(
|
||||||
text,
|
Array.isArray(text) ? text[0] : text,
|
||||||
textState.textAlign || defaultTextAlign
|
textState.textAlign || defaultTextAlign
|
||||||
);
|
);
|
||||||
const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
|
const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
|
||||||
|
|||||||
@@ -1146,7 +1146,12 @@ class CanvasImmediateRenderer extends VectorContext {
|
|||||||
? textTextBaseline
|
? textTextBaseline
|
||||||
: defaultTextBaseline,
|
: defaultTextBaseline,
|
||||||
};
|
};
|
||||||
this.text_ = textText !== undefined ? textText : '';
|
this.text_ =
|
||||||
|
textText !== undefined
|
||||||
|
? Array.isArray(textText)
|
||||||
|
? textText.reduce((acc, t, i) => (acc += i % 2 ? ' ' : t), '')
|
||||||
|
: textText
|
||||||
|
: '';
|
||||||
this.textOffsetX_ =
|
this.textOffsetX_ =
|
||||||
textOffsetX !== undefined ? this.pixelRatio_ * textOffsetX : 0;
|
textOffsetX !== undefined ? this.pixelRatio_ * textOffsetX : 0;
|
||||||
this.textOffsetY_ =
|
this.textOffsetY_ =
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {string}
|
* @type {string|Array<string>}
|
||||||
*/
|
*/
|
||||||
this.text_ = '';
|
this.text_ = '';
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ const DEFAULT_FILL_COLOR = '#333';
|
|||||||
* @property {number|import("../size.js").Size} [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|Array<string>} [text] Text content or rich text content. For plain text provide a string, which can
|
||||||
|
* contain line breaks (`\n`). For rich text provide an array of text/font tuples. A tuple consists of the text to
|
||||||
|
* render and the font to use (or `''` to use the text style's font). A line break has to be a separate tuple (i.e. `'\n', ''`).
|
||||||
|
* **Example:** `['foo', 'bold 10px sans-serif', ' bar', 'italic 10px sans-serif', ' baz', '']` will yield "**foo** *bar* baz".
|
||||||
|
* **Note:** Rich text is not supported for the immediate rendering API.
|
||||||
* @property {string} [textAlign] Text alignment. Possible values: 'left', 'right', 'center', 'end' or 'start'.
|
* @property {string} [textAlign] Text alignment. Possible values: 'left', 'right', 'center', 'end' or 'start'.
|
||||||
* Default is 'center' for `placement: 'point'`. For `placement: 'line'`, the default is to let the renderer choose a
|
* Default is 'center' for `placement: 'point'`. For `placement: 'line'`, the default is to let the renderer choose a
|
||||||
* placement where `maxAngle` is not exceeded.
|
* placement where `maxAngle` is not exceeded.
|
||||||
@@ -87,7 +91,7 @@ class Text {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {string|undefined}
|
* @type {string|Array<string>|undefined}
|
||||||
*/
|
*/
|
||||||
this.text_ = options.text;
|
this.text_ = options.text;
|
||||||
|
|
||||||
@@ -314,7 +318,7 @@ class Text {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text to be rendered.
|
* Get the text to be rendered.
|
||||||
* @return {string|undefined} Text.
|
* @return {string|Array<string>|undefined} Text.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
getText() {
|
getText() {
|
||||||
|
|||||||
BIN
test/rendering/cases/rich-text-style/expected.png
Normal file
BIN
test/rendering/cases/rich-text-style/expected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
129
test/rendering/cases/rich-text-style/main.js
Normal file
129
test/rendering/cases/rich-text-style/main.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import CircleStyle from '../../../../src/ol/style/Circle.js';
|
||||||
|
import Feature from '../../../../src/ol/Feature.js';
|
||||||
|
import Fill from '../../../../src/ol/style/Fill.js';
|
||||||
|
import Map from '../../../../src/ol/Map.js';
|
||||||
|
import Point from '../../../../src/ol/geom/Point.js';
|
||||||
|
import Stroke from '../../../../src/ol/style/Stroke.js';
|
||||||
|
import Style from '../../../../src/ol/style/Style.js';
|
||||||
|
import Text from '../../../../src/ol/style/Text.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 vectorSource = new VectorSource({
|
||||||
|
features: [
|
||||||
|
// inline right-bottom
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([-10, 50]),
|
||||||
|
text: [
|
||||||
|
'in',
|
||||||
|
'',
|
||||||
|
'line',
|
||||||
|
'italic 20px/1.5 Ubuntu',
|
||||||
|
'\n',
|
||||||
|
'',
|
||||||
|
'right-bottom',
|
||||||
|
'20px/1.2 Ubuntu',
|
||||||
|
],
|
||||||
|
textAlign: 'right',
|
||||||
|
textBaseline: 'bottom',
|
||||||
|
}),
|
||||||
|
// multi-line - center-middle
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([0, 0]),
|
||||||
|
text: ['multi-line', '', '\n', '', 'center-middle', 'italic 20px Ubuntu'],
|
||||||
|
textAlign: 'center',
|
||||||
|
textBaseline: 'middle',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// inline right-top
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([-10, -50]),
|
||||||
|
text: [
|
||||||
|
'in',
|
||||||
|
'',
|
||||||
|
'line',
|
||||||
|
'italic 20px/1.5 Ubuntu',
|
||||||
|
'\n',
|
||||||
|
'',
|
||||||
|
'right-top',
|
||||||
|
'28px/1 Ubuntu',
|
||||||
|
],
|
||||||
|
textAlign: 'right',
|
||||||
|
textBaseline: 'top',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// inline left-bottom
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([10, 50]),
|
||||||
|
text: [
|
||||||
|
'in',
|
||||||
|
'',
|
||||||
|
'line',
|
||||||
|
'italic 20px/1.5 Ubuntu',
|
||||||
|
'\n',
|
||||||
|
'',
|
||||||
|
'left-bottom',
|
||||||
|
'20px/1.2 Ubuntu',
|
||||||
|
],
|
||||||
|
textAlign: 'left',
|
||||||
|
textBaseline: 'bottom',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// inline left-top
|
||||||
|
new Feature({
|
||||||
|
geometry: new Point([10, -50]),
|
||||||
|
text: [
|
||||||
|
'in',
|
||||||
|
'',
|
||||||
|
'line',
|
||||||
|
'italic 20px/1.5 Ubuntu',
|
||||||
|
'\n',
|
||||||
|
'',
|
||||||
|
'left-top',
|
||||||
|
'28px/1 Ubuntu',
|
||||||
|
],
|
||||||
|
textAlign: 'left',
|
||||||
|
textBaseline: 'top',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
new Map({
|
||||||
|
pixelRatio: 1,
|
||||||
|
layers: [
|
||||||
|
new VectorLayer({
|
||||||
|
source: vectorSource,
|
||||||
|
style: function (feature) {
|
||||||
|
return new Style({
|
||||||
|
text: new Text({
|
||||||
|
text: feature.get('text'),
|
||||||
|
font: '24px Ubuntu',
|
||||||
|
textAlign: feature.get('textAlign'),
|
||||||
|
textBaseline: feature.get('textBaseline'),
|
||||||
|
fill: new Fill({
|
||||||
|
color: 'black',
|
||||||
|
}),
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: 'white',
|
||||||
|
}),
|
||||||
|
backgroundStroke: new Stroke({width: 1}),
|
||||||
|
}),
|
||||||
|
image: new CircleStyle({
|
||||||
|
radius: 10,
|
||||||
|
fill: new Fill({
|
||||||
|
color: 'cyan',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
resolution: 1,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
render({tolerance: 0.01});
|
||||||
Reference in New Issue
Block a user