Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs/api-reference/layers/icon-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,19 @@ If you choose to use auto packing, this prop should be left empty.

Icon size multiplier.


#### `sizeBasis` (string, optional) {#sizebasis}

- Default: 'height'

Determines which dimension the size controls when scaling the icon. Valid values:

'height': The icon size controls the height of the icon (default).

'width': The icon size controls the width of the icon.

This affects how the icon is scaled to maintain its aspect ratio based on the chosen size basis.

#### `sizeUnits` (string, optional) {#sizeunits}

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

- Default: `1`

The height of each object, in units specified by `sizeUnits` (default pixels).
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.

- 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.
Expand Down
7 changes: 7 additions & 0 deletions examples/layer-browser/src/examples/core-layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,16 @@ const ArcLayerExample = {
const IconLayerExample = {
layer: IconLayer,
getData: () => dataSamples.points,
propTypes: {
sizeBasis: {
type: 'category',
value: ['height', 'width']
}
},
props: {
iconAtlas: 'data/icon-atlas.png',
iconMapping: dataSamples.iconAtlas,
sizeBasis: 'height',
sizeScale: 24,
getPosition: d => d.COORDINATES,
getColor: d => [64, 64, 72],
Expand Down
3 changes: 3 additions & 0 deletions modules/layers/src/icon-layer/icon-layer-uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const uniformBlock = `\
uniform iconUniforms {
float sizeScale;
vec2 iconsTextureDim;
float sizeBasis;
float sizeMinPixels;
float sizeMaxPixels;
bool billboard;
Expand All @@ -24,6 +25,7 @@ type IconBindingProps = {
type IconUniformProps = {
sizeScale: number;
iconsTextureDim: [number, number];
sizeBasis: number;
sizeMinPixels: number;
sizeMaxPixels: number;
billboard: boolean;
Expand All @@ -40,6 +42,7 @@ export const iconUniforms = {
uniformTypes: {
sizeScale: 'f32',
iconsTextureDim: 'vec2<f32>',
sizeBasis: 'f32',
sizeMinPixels: 'f32',
sizeMaxPixels: 'f32',
billboard: 'f32',
Expand Down
6 changes: 3 additions & 3 deletions modules/layers/src/icon-layer/icon-layer-vertex.glsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ void main(void) {
icon.sizeMinPixels, icon.sizeMaxPixels
);

// scale icon height to match instanceSize
float instanceScale = iconSize.y == 0.0 ? 0.0 : sizePixels / iconSize.y;
// Choose correct constraint based on the 'sizeBasis' value (0.0 = width, 1.0 = height)
float iconConstraint = icon.sizeBasis == 0.0 ? iconSize.x : iconSize.y;
float instanceScale = iconConstraint == 0.0 ? 0.0 : sizePixels / iconConstraint;

// scale and rotate vertex in "pixel" value and convert back to fraction in clipspace
vec2 pixelOffset = positions / 2.0 * iconSize + instanceOffsets;
Expand All @@ -62,7 +63,6 @@ void main(void) {
vec3 offset = vec3(pixelOffset, 0.0);
DECKGL_FILTER_SIZE(offset, geometry);
gl_Position.xy += project_pixel_size_to_clipspace(offset.xy);

} else {
vec3 offset_common = vec3(project_pixel_size(pixelOffset), 0.0);
DECKGL_FILTER_SIZE(offset_common, geometry);
Expand Down
10 changes: 8 additions & 2 deletions modules/layers/src/icon-layer/icon-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ type _IconLayerProps<DataT> = {
* @default 'pixels'
*/
sizeUnits?: Unit;
/**
* The dimension to scale the image
*/
sizeBasis: 'height' | 'width';
/**
* 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.
*/
Expand Down Expand Up @@ -105,6 +109,7 @@ const defaultProps: DefaultProps<IconLayerProps> = {
sizeScale: {type: 'number', value: 1, min: 0},
billboard: true,
sizeUnits: 'pixels',
sizeBasis: 'height',
sizeMinPixels: {type: 'number', min: 0, value: 0}, // min point radius in pixels
sizeMaxPixels: {type: 'number', min: 0, value: Number.MAX_SAFE_INTEGER}, // max point radius in pixels
alphaCutoff: {type: 'number', value: 0.05, min: 0, max: 1},
Expand Down Expand Up @@ -257,9 +262,9 @@ export default class IconLayer<DataT = any, ExtraPropsT extends {} = {}> extends
}

draw({uniforms}): void {
const {sizeScale, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff} = this.props;
const {sizeScale, sizeBasis, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff} =
this.props;
const {iconManager} = this.state;

const iconsTexture = iconManager.getTexture();
if (iconsTexture) {
const model = this.state.model!;
Expand All @@ -268,6 +273,7 @@ export default class IconLayer<DataT = any, ExtraPropsT extends {} = {}> extends
iconsTextureDim: [iconsTexture.width, iconsTexture.height],
sizeUnits: UNIT[sizeUnits],
sizeScale,
sizeBasis: sizeBasis === 'height' ? 1.0 : 0.0,
sizeMinPixels,
sizeMaxPixels,
billboard,
Expand Down
Binary file added test/data/icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions test/render/test-cases/icon-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,48 @@ export default [
],
goldenImage: './test/render/golden-images/icon-lnglat.png'
},
{
name: 'icon-lnglat-rectangle',
viewState: {
longitude: -122.4269,
latitude: 37.75,
zoom: 15.6,
pitch: 0,
bearing: 0,
padding: {
top: 0,
bottom: 0,
left: 0,
right: 0
}
},
layers: [
new IconLayer({
id: 'icon-lnglat-multi',
data: [
{position: [-122.4269, 37.7515], icon: 'tall'},
{position: [-122.4269, 37.7505], icon: 'wide'},
{position: [-122.4269, 37.7495], icon: 'square'},
{position: [-122.4269, 37.7485], icon: 'short'}
],
iconAtlas: './test/data/icons.png',
iconMapping: {
tall: {x: 0, y: 0, width: 40, height: 80, anchorY: 40},
wide: {x: 40, y: 0, width: 80, height: 40, anchorY: 20},
square: {x: 120, y: 0, width: 60, height: 60, anchorY: 30},
short: {x: 180, y: 0, width: 60, height: 20, anchorY: 10}
},
sizeUnits: 'pixels',
sizeScale: 1,
sizeBasis: 'width',
getPosition: d => d.position,
getIcon: d => d.icon,
getSize: 40, // target width in px
opacity: 0.8
})
],
goldenImage: './test/render/golden-images/icon-lnglat-rectangle.png'
},
{
name: 'icon-lnglat-external-buffer',
viewState: {
Expand Down
Loading