Skip to content

Commit e48877d

Browse files
Add IconLayer sizeBasis prop (#9736)
1 parent b74e383 commit e48877d

File tree

8 files changed

+77
-6
lines changed

8 files changed

+77
-6
lines changed

docs/api-reference/layers/icon-layer.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,19 @@ If you choose to use auto packing, this prop should be left empty.
346346

347347
Icon size multiplier.
348348

349+
350+
#### `sizeBasis` (string, optional) {#sizebasis}
351+
352+
- Default: 'height'
353+
354+
Determines which dimension the size controls when scaling the icon. Valid values:
355+
356+
'height': The icon size controls the height of the icon (default).
357+
358+
'width': The icon size controls the width of the icon.
359+
360+
This affects how the icon is scaled to maintain its aspect ratio based on the chosen size basis.
361+
349362
#### `sizeUnits` (string, optional) {#sizeunits}
350363

351364
* Default: `pixels`
@@ -436,7 +449,7 @@ Method called to retrieve the position of each object, returns `[lng, lat, z]`.
436449

437450
- Default: `1`
438451

439-
The height of each object, in units specified by `sizeUnits` (default pixels).
452+
The size of each object, in units specified by `sizeUnits` (default pixels). By default the size controls the height of the object, this can be changed with the sizeBasis property.
440453

441454
- If a number is provided, it is used as the size for all objects.
442455
- If a function is provided, it is called on each object to retrieve its size.

examples/layer-browser/src/examples/core-layers.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,16 @@ const ArcLayerExample = {
5858
const IconLayerExample = {
5959
layer: IconLayer,
6060
getData: () => dataSamples.points,
61+
propTypes: {
62+
sizeBasis: {
63+
type: 'category',
64+
value: ['height', 'width']
65+
}
66+
},
6167
props: {
6268
iconAtlas: 'data/icon-atlas.png',
6369
iconMapping: dataSamples.iconAtlas,
70+
sizeBasis: 'height',
6471
sizeScale: 24,
6572
getPosition: d => d.COORDINATES,
6673
getColor: d => [64, 64, 72],

modules/layers/src/icon-layer/icon-layer-uniforms.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const uniformBlock = `\
99
uniform iconUniforms {
1010
float sizeScale;
1111
vec2 iconsTextureDim;
12+
float sizeBasis;
1213
float sizeMinPixels;
1314
float sizeMaxPixels;
1415
bool billboard;
@@ -24,6 +25,7 @@ type IconBindingProps = {
2425
type IconUniformProps = {
2526
sizeScale: number;
2627
iconsTextureDim: [number, number];
28+
sizeBasis: number;
2729
sizeMinPixels: number;
2830
sizeMaxPixels: number;
2931
billboard: boolean;
@@ -40,6 +42,7 @@ export const iconUniforms = {
4042
uniformTypes: {
4143
sizeScale: 'f32',
4244
iconsTextureDim: 'vec2<f32>',
45+
sizeBasis: 'f32',
4346
sizeMinPixels: 'f32',
4447
sizeMaxPixels: 'f32',
4548
billboard: 'f32',

modules/layers/src/icon-layer/icon-layer-vertex.glsl.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ void main(void) {
4747
icon.sizeMinPixels, icon.sizeMaxPixels
4848
);
4949
50-
// scale icon height to match instanceSize
51-
float instanceScale = iconSize.y == 0.0 ? 0.0 : sizePixels / iconSize.y;
50+
// Choose correct constraint based on the 'sizeBasis' value (0.0 = width, 1.0 = height)
51+
float iconConstraint = icon.sizeBasis == 0.0 ? iconSize.x : iconSize.y;
52+
float instanceScale = iconConstraint == 0.0 ? 0.0 : sizePixels / iconConstraint;
5253
5354
// scale and rotate vertex in "pixel" value and convert back to fraction in clipspace
5455
vec2 pixelOffset = positions / 2.0 * iconSize + instanceOffsets;
@@ -62,7 +63,6 @@ void main(void) {
6263
vec3 offset = vec3(pixelOffset, 0.0);
6364
DECKGL_FILTER_SIZE(offset, geometry);
6465
gl_Position.xy += project_pixel_size_to_clipspace(offset.xy);
65-
6666
} else {
6767
vec3 offset_common = vec3(project_pixel_size(pixelOffset), 0.0);
6868
DECKGL_FILTER_SIZE(offset_common, geometry);

modules/layers/src/icon-layer/icon-layer.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ type _IconLayerProps<DataT> = {
4343
* @default 'pixels'
4444
*/
4545
sizeUnits?: Unit;
46+
/**
47+
* The dimension to scale the image
48+
*/
49+
sizeBasis: 'height' | 'width';
4650
/**
4751
* 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.
4852
*/
@@ -105,6 +109,7 @@ const defaultProps: DefaultProps<IconLayerProps> = {
105109
sizeScale: {type: 'number', value: 1, min: 0},
106110
billboard: true,
107111
sizeUnits: 'pixels',
112+
sizeBasis: 'height',
108113
sizeMinPixels: {type: 'number', min: 0, value: 0}, // min point radius in pixels
109114
sizeMaxPixels: {type: 'number', min: 0, value: Number.MAX_SAFE_INTEGER}, // max point radius in pixels
110115
alphaCutoff: {type: 'number', value: 0.05, min: 0, max: 1},
@@ -257,9 +262,9 @@ export default class IconLayer<DataT = any, ExtraPropsT extends {} = {}> extends
257262
}
258263

259264
draw({uniforms}): void {
260-
const {sizeScale, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff} = this.props;
265+
const {sizeScale, sizeBasis, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff} =
266+
this.props;
261267
const {iconManager} = this.state;
262-
263268
const iconsTexture = iconManager.getTexture();
264269
if (iconsTexture) {
265270
const model = this.state.model!;
@@ -268,6 +273,7 @@ export default class IconLayer<DataT = any, ExtraPropsT extends {} = {}> extends
268273
iconsTextureDim: [iconsTexture.width, iconsTexture.height],
269274
sizeUnits: UNIT[sizeUnits],
270275
sizeScale,
276+
sizeBasis: sizeBasis === 'height' ? 1.0 : 0.0,
271277
sizeMinPixels,
272278
sizeMaxPixels,
273279
billboard,

test/data/icons.png

416 Bytes
Loading
2.85 KB
Loading

test/render/test-cases/icon-layer.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,48 @@ export default [
3535
],
3636
goldenImage: './test/render/golden-images/icon-lnglat.png'
3737
},
38+
{
39+
name: 'icon-lnglat-rectangle',
40+
viewState: {
41+
longitude: -122.4269,
42+
latitude: 37.75,
43+
zoom: 15.6,
44+
pitch: 0,
45+
bearing: 0,
46+
padding: {
47+
top: 0,
48+
bottom: 0,
49+
left: 0,
50+
right: 0
51+
}
52+
},
53+
layers: [
54+
new IconLayer({
55+
id: 'icon-lnglat-multi',
56+
data: [
57+
{position: [-122.4269, 37.7515], icon: 'tall'},
58+
{position: [-122.4269, 37.7505], icon: 'wide'},
59+
{position: [-122.4269, 37.7495], icon: 'square'},
60+
{position: [-122.4269, 37.7485], icon: 'short'}
61+
],
62+
iconAtlas: './test/data/icons.png',
63+
iconMapping: {
64+
tall: {x: 0, y: 0, width: 40, height: 80, anchorY: 40},
65+
wide: {x: 40, y: 0, width: 80, height: 40, anchorY: 20},
66+
square: {x: 120, y: 0, width: 60, height: 60, anchorY: 30},
67+
short: {x: 180, y: 0, width: 60, height: 20, anchorY: 10}
68+
},
69+
sizeUnits: 'pixels',
70+
sizeScale: 1,
71+
sizeBasis: 'width',
72+
getPosition: d => d.position,
73+
getIcon: d => d.icon,
74+
getSize: 40, // target width in px
75+
opacity: 0.8
76+
})
77+
],
78+
goldenImage: './test/render/golden-images/icon-lnglat-rectangle.png'
79+
},
3880
{
3981
name: 'icon-lnglat-external-buffer',
4082
viewState: {

0 commit comments

Comments
 (0)