import {TextLayerDemo} from '@site/src/doc-demos/layers';
The TextLayer
renders text labels at given coordinates.
TextLayer is a CompositeLayer that wraps around the IconLayer. It automatically creates an atlas texture from the specified font settings and characterSet
.
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
import {Deck} from '@deck.gl/core';
import {TextLayer} from '@deck.gl/layers';
const layer = new TextLayer({
id: 'TextLayer',
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-stations.json',
getPosition: d => d.coordinates,
getText: d => d.name,
getAlignmentBaseline: 'center',
getColor: [255, 128, 0],
getSize: 16,
getTextAnchor: 'middle',
pickable: true
});
new Deck({
initialViewState: {
longitude: -122.4,
latitude: 37.74,
zoom: 11
},
controller: true,
getTooltip: ({object}) => object && object.name,
layers: [layer]
});
import {Deck, PickingInfo} from '@deck.gl/core';
import {TextLayer} from '@deck.gl/layers';
type BartStation = {
name: string;
coordinates: [longitude: number, latitude: number];
};
const layer = new TextLayer<BartStation>({
id: 'TextLayer',
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-stations.json',
getPosition: (d: BartStation) => d.coordinates,
getText: (d: BartStation) => d.name,
getAlignmentBaseline: 'center',
getColor: [255, 128, 0],
getSize: 16,
getTextAnchor: 'middle',
pickable: true
});
new Deck({
initialViewState: {
longitude: -122.4,
latitude: 37.74,
zoom: 11
},
controller: true,
getTooltip: ({object}: PickingInfo<BartStation>) => object && object.name,
layers: [layer]
});
import React from 'react';
import DeckGL from '@deck.gl/react';
import {} from '@deck.gl/layers';
import type {PickingInfo} from '@deck.gl/core';
type BartStation = {
name: string;
coordinates: [longitude: number, latitude: number];
};
function App() {
const layer = new TextLayer<BartStation>({
id: 'TextLayer',
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-stations.json',
getPosition: (d: BartStation) => d.coordinates,
getText: (d: BartStation) => d.name,
getAlignmentBaseline: 'center',
getColor: [255, 128, 0],
getSize: 16,
getTextAnchor: 'middle',
pickable: true
});
return <DeckGL
initialViewState={{
longitude: -122.4,
latitude: 37.74,
zoom: 11
}}
controller
getTooltip={({object}: PickingInfo<BartStation>) => object && object.name}
layers={[layer]}
/>;
}
To install the dependencies from NPM:
npm install deck.gl
# or
npm install @deck.gl/core @deck.gl/layers
import {TextLayer} from '@deck.gl/layers';
import type {TextLayerProps} from '@deck.gl/layers';
new TextLayer<DataT>(...props: TextLayerProps<DataT>[]);
To use pre-bundled scripts:
<script src="https://unpkg.com/deck.gl@^9.0.0/dist.min.js"></script>
<!-- or -->
<script src="https://unpkg.com/@deck.gl/core@^9.0.0/dist.min.js"></script>
<script src="https://unpkg.com/@deck.gl/layers@^9.0.0/dist.min.js"></script>
new deck.TextLayer({});
Inherits from all Base Layer and CompositeLayer properties.
- Default: 1
Text size multiplier.
- Default:
pixels
The units of the size, one of 'meters'
, 'common'
, and 'pixels'
. See unit system.
- Default:
0
The minimum size in pixels. When using non-pixel sizeUnits
, this prop can be used to prevent the icon from getting too small when zoomed out.
- Default:
Number.MAX_SAFE_INTEGER
The maximum size in pixels. When using non-pixel sizeUnits
, this prop can be used to prevent the icon from getting too big when zoomed in.
- Default:
true
If true
, the text always faces camera. Otherwise the text faces up (z).
- Default
false
Whether to render background for the text blocks.
- Default
[0, 0, 0, 0]
The padding of the background, an array of either 2 or 4 numbers.
- If an array of 2 is supplied, it is interpreted as
[padding_x, padding_y]
in pixels. - If an array of 4 is supplied, it is interpreted as
[padding_left, padding_top, padding_right, padding_bottom]
in pixels.
- Default:
'Monaco, monospace'
Specifies a prioritized list of one or more font family names and/or generic family names. Follow the specs for CSS font-family.
See the remarks section below for tips on using web fonts.
- Default: ASCII characters 32-128
Specifies a list of characters to include in the font.
- If set to
'auto'
, automatically detects the characters used in the data. This option has a performance overhead and may cause the layer to take longer to load if the data is very large. - If set to an array or set of characters, the generated font will be limited to these characters. If you already know all the characters that are needed (e.g. numbers, latin alphabet), using this option provides better performance. If a character outside of the specified range is referenced by
getText
, a warning will be logged to the JavaScript console.
Note that there is a limit to the number of unique characters supported by a single layer. The maximum number subjects to fontSettings.fontSize
and the MAX_TEXTURE_SIZE of the device/browser.
- Default:
normal
.
css font-weight
.
- Default:
1.0
.
A unitless number that will be multiplied with the current font size to set the line height.
Advance options for fine tuning the appearance and performance of the generated shared fontAtlas
.
Options:
fontSize
(number): Font size in pixels. Default is64
. This option is only applied for generatingfontAtlas
, it does not impact the size of displayed text labels. LargerfontSize
will give you a sharper look when rendering text labels with very large font sizes. But largerfontSize
requires more time and space to generate thefontAtlas
.buffer
(number): Whitespace buffer around each side of the character. Default is4
. In general, biggerfontSize
requires biggerbuffer
. Increasebuffer
will add more space between each character when layoutcharacterSet
infontAtlas
. This option could be tuned to provide sufficient space for drawing each character and avoiding overlapping of neighboring characters.sdf
(boolean): Flag to enable / disablesdf
. Default isfalse
.sdf
(Signed Distance Fields) will provide a sharper look when rendering with very large or small font sizes.TextLayer
integrates withTinySDF
which implements thesdf
algorithm.radius
(number): How many pixels around the glyph shape to use for encoding distance. Default is12
. Bigger radius yields higher quality outcome. Only applies whensdf: true
.cutoff
(number): How much of the radius (relative) is used for the inside part the glyph. Default is0.25
. Biggercutoff
makes character thinner. Smallercutoff
makes character look thicker. Only applies whensdf: true
.smoothing
(number): How much smoothing to apply to the text edges. Default0.1
. Only applies whensdf: true
.
- Default:
break-word
Available options are break-all
and break-word
. A valid maxWidth
has to be provided to use wordBreak
.
- Default:
-1
A unitless number that will be multiplied with the current text size to set the width limit of a string. If specified, when the text is longer than the width limit, it will be wrapped into multiple lines using the strategy of wordBreak
.
For example, maxWidth: 10.0
used with getSize: 12
is roughly the equivalent of max-width: 120px
in CSS.
- Default:
0
Width of outline around the text, relative to the font size. Only effective if fontSettings.sdf
is true
.
- Default:
[0, 0, 0, 255]
Color of outline around the text, in [r, g, b, [a]]
. Each channel is a number between 0-255 and a
is 255 if not supplied.
getText
(Accessor<string>, optional) {#gettext}
- Default:
x => x.text
Method called to retrieve the content of each text label.
getPosition
(Accessor<Position>, optional) {#getposition}
- Default:
x => x.position
Method called to retrieve the location of each text label.
getSize
(Accessor<number>, optional) {#getsize}
- Default:
32
The font size of each text label, in units specified by sizeUnits
(default pixels).
- If a number is provided, it is used as the size for all objects.
- If a function is provided, it is called on each object to retrieve its size.
getColor
(Accessor<Color>, optional) {#getcolor}
- Default:
[0, 0, 0, 255]
The rgba color is in the format of [r, g, b, [a]]
. Each channel is a number between 0-255 and a
is 255 if not supplied.
- If an array is provided, it is used as the color for all objects.
- If a function is provided, it is called on each object to retrieve its color.
getAngle
(Accessor<number>, optional) {#getangle}
- Default:
0
The rotating angle of each text label, in degrees.
- If a number is provided, it is used as the angle for all objects.
- If a function is provided, it is called on each object to retrieve its angle.
getTextAnchor
(Accessor<string>, optional) {#gettextanchor}
- Default:
'middle'
The text anchor. Available options include 'start'
, 'middle'
and 'end'
.
- If a string is provided, it is used as the text anchor for all objects.
- If a function is provided, it is called on each object to retrieve its text anchor.
getAlignmentBaseline
(Accessor<string>, optional) {#getalignmentbaseline}
- Default:
'center'
The alignment baseline. Available options include 'top'
, 'center'
and 'bottom'
.
- If a string is provided, it is used as the alignment baseline for all objects.
- If a function is provided, it is called on each object to retrieve its alignment baseline.
getPixelOffset
(Accessor<number[2]>, optional) {#getpixeloffset}
- Default:
[0, 0]
Screen space offset relative to the coordinates
in pixel unit.
- If an array is provided, it is used as the offset for all objects.
- If a function is provided, it is called on each object to retrieve its offset.
getBackgroundColor
(Accessor<Color>, optional) {#getbackgroundcolor}
- Default:
[255, 255, 255, 255]
The background color. Only effective if background: true
.
The rgba color is in the format of [r, g, b, [a]]
. Each channel is a number between 0-255 and a
is 255 if not supplied.
- If an array is provided, it is used as the background color for all objects.
- If a function is provided, it is called on each object to retrieve its background color.
getBorderColor
(Accessor<Color>, optional) {#getbordercolor}
- Default:
[0, 0, 0, 255]
The border color of the background. Only effective if background: true
.
The rgba color is in the format of [r, g, b, [a]]
. Each channel is a number between 0-255 and a
is 255 if not supplied.
- If an array is provided, it is used as the border color for all objects.
- If a function is provided, it is called on each object to retrieve its border color.
getBorderWidth
(Accessor<number>, optional) {#getborderwidth}
- Default:
0
The border thickness of each text label, in pixels. Only effective if background: true
.
- If a number is provided, it is used as the border thickness for all objects.
- If a function is provided, it is called on each object to retrieve its border thickness.
The TextLayer renders the following sublayers:
characters
- anIconLayer
rendering all the characters.background
- the background for each text block, ifbackground: true
.
This section is about the special requirements when supplying attributes directly to a TextLayer
.
Because each text string has a different number of characters, when data.attributes.getText
is supplied, the layer also requires an array data.startIndices
that describes the character index at the start of each text object. For example, if there are 3 text objects of 2, 3, and 4 characters each, startIndices
should be [0, 2, 5, 9]
.
Additionally, all other attributes (getColor
, getWidth
, etc.), if supplied, must contain the same layout (number of characters) as the getText
buffer.
Example use case:
const TEXT_DATA = [
{
text: 'Hello',
position: [-122.4, 37.7],
color: [255, 0, 0]
},
{
text: 'World',
position: [-122.5, 37.8],
color: [0, 0, 255]
},
// ...
];
new TextLayer({
data: TEXT_DATA,
getText: d => d.text,
getPosition: d => d.position,
getColor: d => d.color
})
The equivalent binary attributes would be:
// USE BINARY
// Flatten the text by converting to unicode value
// Non-Latin characters may require Uint16Array
// [72, 101, 108, 108, 111, ...]
const texts = new Uint8Array(TEXT_DATA.map(d => Array.from(d.text).map(char => char.charCodeAt(0))).flat());
// The position attribute must supply one position for each character
// [-122.4, 37.7, -122.4, 37.7, -122.4, 37.7, ...]
const positions = new Float64Array(TEXT_DATA.map(d => Array.from(d.text).map(_ => d.position)).flat(2));
// The color attribute must supply one color for each character
// [255, 0, 0, 255, 0, 0, 255, 0, 0, ...]
const colors = new Uint8Array(TEXT_DATA.map(d => Array.from(d.text).map(_ => d.color)).flat(2));
// The "layout" that tells TextLayer where each string starts
const startIndices = new Uint16Array(TEXT_DATA.reduce((acc, d) => {
const lastIndex = acc[acc.length - 1];
acc.push(lastIndex + d.text.length);
return acc;
}, [0]));
new TextLayer({
data: {
length: TEXT_DATA.length,
startIndices: startIndices, // this is required to render the texts correctly!
attributes: {
getText: {value: texts},
getPosition: {value: positions, size: 2},
getColor: {value: colors, size: 3}
}
}
})
To use background: true
with binary data, the background attributes must be supplied separately via data.attributes.background
. Each attribute is packed with one vertex per object.
data.attributes.background
may contain the following keys:
getPosition
: corresponds to thegetPosition
accessorgetAngle
: corresponds to thegetAngle
accessorgetSize
: corresponds to thegetSize
accessorgetPixelOffset
: corresponds to thegetPixelOffset
accessorgetFillColor
: corresponds to thegetBackgroundColor
accessorgetLineColor
: corresponds to thegetBorderColor
accessorgetLineWidth
: corresponds to thegetBorderWidth
accessor
Following the above example, additional attributes are required to render the background:
// The background position attribute supplies one position for each text block
const backgroundPositions = new Float64Array(TEXT_DATA.map(d => d.position).flat());
// The background color attribute supplies one color for each text block
const backgroundColors = new Uint8Array(TEXT_DATA.map(d => d.bgColor).flat());
new TextLayer({
data: {
length: TEXT_DATA.length,
startIndices: startIndices, // this is required to render the texts correctly!
attributes: {
getText: {value: texts},
getPosition: {value: positions, size: 2},
getColor: {value: colors, size: 3},
background: {
getPosition: {value: backgroundPosition, size: 2},
getFillColor: {value: backgroundColors, size: 3}
}
}
},
background: true
})
The TextLayer
creates a font texture when it is first added with the fillText API. If the font specified by fontFamily
is not loaded at this point, it will fall back to using the default font just like regular CSS behavior. The loading sequence may become an issue when a web font is used, due to lazy loading.
One way to force a web font to load before the script execution is to preload the font resource:
<link rel="preload" href="https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2" as="font" crossorigin="anonymous" type="font/woff2" />
<style>
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2) format('woff2');
}
</style>
Another way is to use the FontFace API to load a web font before adding the TextLayer
:
import {Deck} from '@deck.gl/core';
import {TextLayer} from '@deck.gl/layers';
const deckInstance = new Deck({...});
renderLayers();
async function renderLayers() {
const font = new FontFace('Material Icons', 'url(https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2)');
// wait for font to be loaded
await font.load();
// add font to document
document.fonts.add(font);
// add TextLayer
const textLayer = new TextLayer({
fontFamily: 'Material Icons',
// ...
});
deckInstance.setProps({
layers: [textLayer]
});
}
import React, {useState, useEffect} from 'react';
import DeckGL from '@deck.gl/react';
import {TextLayer} from '@deck.gl/layers';
function App() {
const [fontLoaded, setFontLoaded] = useState<boolean>(false);
useEffect(() => {
(async () => {
const font = new FontFace('Material Icons', 'url(https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2)');
// wait for font to be loaded
await font.load();
// add font to document
document.fonts.add(font);
setFontLoaded(true);
})();
}, []);
const textLayer = fontLoaded && new TextLayer({
fontFamily: 'Material Icons',
// ...
});
return <DeckGL
// ...
layers={[textLayer]}
/>;
}
The TextLayer has full support for Unicode characters. To reference a Unicode character in JavaScript you can either use a string literal ('日本語'
, '©'
) or escaped code point ('\u{1F436}'
).
At the moment this layer doesn't render multi-color emojis.
To conserve memory, DeckGL caches the most 3 used fontAtlas
by default. Creating a fontAtlas
is a CPU intesive operation specially if fontSettings.sdf
is set to true
.
If you are using much more than 3 fonts, you might experience perforamnce hits because DeckGL constantly tries to evict the least most used fontAtlas
from cache and recreate them when needed.
To mitigate the potential performance degradation, you can override the fontAtlas
default cache limit by setting TextLayer.fontAtlasCacheLimit
value:
import {TextLayer} from '@deck.gl/layers';
TextLayer.fontAtlasCacheLimit = 10;
// ... rest of the application
It is recommended to set fontAtlasCacheLimit
once in your application since it recreates the cache which removes existing cached fontAtlas
.