From a9acdf8fb4e8baacfbce52255a646cf255edff38 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Tue, 30 Apr 2024 14:04:15 -0400 Subject: [PATCH 01/10] docs(carto): Clean up CARTO fetchMap example (#8857) --- docs/api-reference/carto/fetch-map.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/api-reference/carto/fetch-map.md b/docs/api-reference/carto/fetch-map.md index 559489cc6be..34845b6f7ef 100644 --- a/docs/api-reference/carto/fetch-map.md +++ b/docs/api-reference/carto/fetch-map.md @@ -24,15 +24,12 @@ fetchMap({cartoMapId}).then(map => new Deck(map)); ### Integration with CARTO basemaps ```js -import mapboxgl from 'mapbox-gl'; - fetchMap({cartoMapId}).then(({initialViewState, mapStyle, layers}) => { - // Add Mapbox GL for the basemap. It's not a requirement if you don't need a basemap. - const MAP_STYLE = `https://basemaps.cartocdn.com/gl/${mapStyle.styleType}-gl-style/style.json`; const deckgl = new deck.DeckGL({ container: 'container', controller: true, - mapStyle: MAP_STYLE, + // (Optional) Include a basemap. + mapStyle: `https://basemaps.cartocdn.com/gl/${mapStyle.styleType}-gl-style/style.json`, initialViewState, layers }); From 44e971d6411df44bee387a81a28001069d3b75c1 Mon Sep 17 00:00:00 2001 From: atmorling Date: Fri, 3 May 2024 00:21:56 +0200 Subject: [PATCH 02/10] fix(widgets): export @deck.gl/widgets/stylesheet.css (#8863) * remove container from zoom-widget docs * add export for @deck.gl/widgets stylesheet --- docs/api-reference/widgets/zoom-widget.md | 6 ------ modules/widgets/package.json | 4 ++++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/api-reference/widgets/zoom-widget.md b/docs/api-reference/widgets/zoom-widget.md index 21d066fcae0..4f7d52749c3 100644 --- a/docs/api-reference/widgets/zoom-widget.md +++ b/docs/api-reference/widgets/zoom-widget.md @@ -28,12 +28,6 @@ Default: `'vertical'` Widget button orientation. Valid options are `vertical` or `horizontal`. -#### `container` (HTMLElement, optional) {#container} - -Default: `undefined` - -A [compatible DOM element](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen#Compatible_elements) which should be made full screen. By default, the map container element will be made full screen. - #### `zoomInLabel` (string, optional) {#zoominlabel} Tooltip message displayed while hovering a mouse over the zoom in button. diff --git a/modules/widgets/package.json b/modules/widgets/package.json index 5cdb19d57c3..5d02dbcb1c6 100644 --- a/modules/widgets/package.json +++ b/modules/widgets/package.json @@ -25,6 +25,10 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" + }, + "./stylesheet.css": { + "import": "./dist/stylesheet.css", + "style": "./dist/stylesheet.css" } }, "files": [ From 05e1b012f317d557f1f5da182af311ab7f96e448 Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Sun, 5 May 2024 17:31:26 -0700 Subject: [PATCH 03/10] feat(layers): Simplify ArcLayer (#8859) --- .../src/arc-layer/arc-layer-vertex.glsl.ts | 15 ++++-- modules/layers/src/arc-layer/arc-layer.ts | 49 +++++++------------ 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/modules/layers/src/arc-layer/arc-layer-vertex.glsl.ts b/modules/layers/src/arc-layer/arc-layer-vertex.glsl.ts index a01c41fb017..f666ea49e30 100644 --- a/modules/layers/src/arc-layer/arc-layer-vertex.glsl.ts +++ b/modules/layers/src/arc-layer/arc-layer-vertex.glsl.ts @@ -22,7 +22,6 @@ export default `\ #version 300 es #define SHADER_NAME arc-layer-vertex-shader -in vec3 positions; in vec4 instanceSourceColors; in vec4 instanceTargetColors; in vec3 instanceSourcePositions; @@ -137,7 +136,15 @@ void main(void) { geometry.worldPosition = instanceSourcePositions; geometry.worldPositionAlt = instanceTargetPositions; - float segmentIndex = positions.x; + /* + * --(i, -1)-----------_(i+1, -1)-- + * | _,-" | + * o _,-" o + * | _,-" | + * --(i, 1)"-------------(i+1, 1)-- + */ + float segmentIndex = float(gl_VertexID / 2); + float segmentSide = mod(float(gl_VertexID), 2.) == 0. ? -1. : 1.; float segmentRatio = getSegmentRatio(segmentIndex); float prevSegmentRatio = getSegmentRatio(max(0.0, segmentIndex - 1.0)); float nextSegmentRatio = getSegmentRatio(min(numSegments - 1.0, segmentIndex + 1.0)); @@ -147,7 +154,7 @@ void main(void) { float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0)); isValid = 1.0; - uv = vec2(segmentRatio, positions.y); + uv = vec2(segmentRatio, segmentSide); geometry.uv = uv; geometry.pickingColor = instancePickingColors; @@ -244,7 +251,7 @@ void main(void) { // extrude vec3 offset = vec3( - getExtrusionOffset((next.xy - curr.xy) * indexDir, positions.y, widthPixels), + getExtrusionOffset((next.xy - curr.xy) * indexDir, segmentSide, widthPixels), 0.0); DECKGL_FILTER_SIZE(offset, geometry); DECKGL_FILTER_GL_POSITION(curr, geometry); diff --git a/modules/layers/src/arc-layer/arc-layer.ts b/modules/layers/src/arc-layer/arc-layer.ts index 3d21382c9c8..358c6940556 100644 --- a/modules/layers/src/arc-layer/arc-layer.ts +++ b/modules/layers/src/arc-layer/arc-layer.ts @@ -34,7 +34,6 @@ import { DefaultProps } from '@deck.gl/core'; -import {Geometry} from '@luma.gl/engine'; import {Model} from '@luma.gl/engine'; import vs from './arc-layer-vertex.glsl'; @@ -227,11 +226,10 @@ export default class ArcLayer extends /* eslint-enable max-len */ } - updateState(opts: UpdateParameters): void { - super.updateState(opts); - const {props, oldProps, changeFlags} = opts; - // Re-generate model if geometry changed - if (changeFlags.extensionsChanged || props.numSegments !== oldProps.numSegments) { + updateState(params: UpdateParameters): void { + super.updateState(params); + + if (params.changeFlags.extensionsChanged) { this.state.model?.destroy(); this.state.model = this._getModel(); this.getAttributeManager()!.invalidateAll(); @@ -239,12 +237,20 @@ export default class ArcLayer extends } draw({uniforms}) { - const {widthUnits, widthScale, widthMinPixels, widthMaxPixels, greatCircle, wrapLongitude} = - this.props; + const { + widthUnits, + widthScale, + widthMinPixels, + widthMaxPixels, + greatCircle, + wrapLongitude, + numSegments + } = this.props; const model = this.state.model!; model.setUniforms(uniforms); model.setUniforms({ + numSegments, greatCircle, widthUnits: UNIT[widthUnits], widthScale, @@ -252,38 +258,17 @@ export default class ArcLayer extends widthMaxPixels, useShortestPath: wrapLongitude }); + model.setVertexCount(numSegments * 2); model.draw(this.context.renderPass); } protected _getModel(): Model { - const {numSegments} = this.props; - let positions: number[] = []; - /* - * (0, -1)-------------_(1, -1) - * | _,-" | - * o _,-" o - * | _,-" | - * (0, 1)"-------------(1, 1) - */ - for (let i = 0; i < numSegments; i++) { - positions = positions.concat([i, 1, 0, i, -1, 0]); - } - - const model = new Model(this.context.device, { + return new Model(this.context.device, { ...this.getShaders(), id: this.props.id, bufferLayout: this.getAttributeManager()!.getBufferLayouts(), - geometry: new Geometry({ - topology: 'triangle-strip', - attributes: { - positions: {size: 3, value: new Float32Array(positions)} - } - }), + topology: 'triangle-strip', isInstanced: true }); - - model.setUniforms({numSegments}); - - return model; } } From fd3aa4c16d4b73e23c87ff46385bfd88c7312f3e Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Sun, 5 May 2024 17:32:17 -0700 Subject: [PATCH 04/10] Remove WebGL-only triangle-fan topology (#8860) --- modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts | 4 ++-- .../aggregation-layers/src/heatmap-layer/triangle-layer.ts | 2 +- .../text-layer/text-background-layer/text-background-layer.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts b/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts index 928d9c7baa8..fad958c9031 100644 --- a/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts +++ b/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts @@ -486,8 +486,8 @@ export default class HeatmapLayer< const viewportCorners = [ viewport.unproject([0, 0]), viewport.unproject([viewport.width, 0]), - viewport.unproject([viewport.width, viewport.height]), - viewport.unproject([0, viewport.height]) + viewport.unproject([0, viewport.height]), + viewport.unproject([viewport.width, viewport.height]) ].map(p => p.map(Math.fround)); // #1: get world bounds for current viewport extends diff --git a/modules/aggregation-layers/src/heatmap-layer/triangle-layer.ts b/modules/aggregation-layers/src/heatmap-layer/triangle-layer.ts index cbeebfb7a0f..d1da42ed894 100644 --- a/modules/aggregation-layers/src/heatmap-layer/triangle-layer.ts +++ b/modules/aggregation-layers/src/heatmap-layer/triangle-layer.ts @@ -65,7 +65,7 @@ export default class TriangleLayer extends Layer<_TriangleLayerProps> { {name: 'positions', format: 'float32x3'}, {name: 'texCoords', format: 'float32x2'} ], - topology: 'triangle-fan-webgl', + topology: 'triangle-strip', vertexCount }); } diff --git a/modules/layers/src/text-layer/text-background-layer/text-background-layer.ts b/modules/layers/src/text-layer/text-background-layer/text-background-layer.ts index be607b2681b..5f407bed1f9 100644 --- a/modules/layers/src/text-layer/text-background-layer/text-background-layer.ts +++ b/modules/layers/src/text-layer/text-background-layer/text-background-layer.ts @@ -159,14 +159,14 @@ export default class TextBackgroundLayer Date: Sun, 5 May 2024 17:38:54 -0700 Subject: [PATCH 05/10] feat(core): Explicitly set stepMode in Attribute layout (#8858) --- docs/api-reference/core/attribute-manager.md | 46 +++++---- docs/upgrade-guide.md | 1 + .../src/lib/attribute/attribute-manager.ts | 56 +++++------ modules/core/src/lib/attribute/attribute.ts | 19 +++- modules/core/src/lib/attribute/data-column.ts | 4 +- modules/core/src/lib/layer.ts | 2 +- .../src/brushing/brushing-extension.ts | 11 +-- .../extensions/src/brushing/shader-module.ts | 8 -- .../collision-filter-extension.ts | 7 +- .../src/collision-filter/shader-module.ts | 8 -- .../src/data-filter/data-filter-extension.ts | 22 +---- .../src/data-filter/shader-module.ts | 47 ++-------- .../src/fill-style/fill-style-extension.ts | 33 ++----- .../src/fill-style/shader-module.ts | 22 ++--- .../solid-polygon-layer-vertex-main.glsl.ts | 11 ++- .../solid-polygon-layer-vertex-side.glsl.ts | 34 +++---- .../solid-polygon-layer-vertex-top.glsl.ts | 6 -- .../solid-polygon-layer.ts | 49 +++------- .../lib/attribute/attribute-manager.spec.ts | 94 ++++++++++++++++++- .../core/lib/attribute/attribute.spec.ts | 18 ++-- 20 files changed, 234 insertions(+), 264 deletions(-) diff --git a/docs/api-reference/core/attribute-manager.md b/docs/api-reference/core/attribute-manager.md index f2ee23e6a8a..b6e17d5667c 100644 --- a/docs/api-reference/core/attribute-manager.md +++ b/docs/api-reference/core/attribute-manager.md @@ -52,7 +52,7 @@ the attributes that should be auto-calculated. ```js attributeManager.add({ positions: {size: 2, accessor: 'getPosition', update: calculatePositions}, - colors: {size: 4, type: GL.UNSIGNED_BYTE, accessor: 'getColor', update: calculateColors} + colors: {size: 4, type: 'unorm8', accessor: 'getColor', update: calculateColors} }); ``` @@ -61,13 +61,10 @@ Takes a single parameter as a map of attribute descriptor objects: * keys are attribute names * values are objects with attribute definitions: - luma.gl [accessor parameters](https://luma.gl/docs/api-reference-legacy/classes/accessor): - * `type` (string, optional) - data type of the attribute, see "Remarks" section below. + * `type` (string, optional) - data type of the attribute, see "Remarks" section below. Default `'float32'`. * `size` (number) - number of elements per vertex - * `normalized` (boolean) - default `false` - * `integer` (boolean) - WebGL2 only, default `false` - * `divisor` (boolean, optional) - `1` if this is an instanced attribute - (a.k.a. divisor). Default to `0`. - deck.gl attribute configurations: + * `stepMode` (string, optional) - One of `'vertex'`, `'instance'` and `'dynamic'`. If set to `'dynamic'`, will be resolved to `'instance'` when this attribute is applied to an instanced model, and `'vertex'` otherwise. Default `'vertex'`. * `isIndexed` (boolean, optional) - if this is an index attribute (a.k.a. indices). Default to `false`. * `accessor` (string | string[] | Function) - accessor name(s) that will @@ -85,12 +82,10 @@ Takes a single parameter as a map of attribute descriptor objects: * `size` (number) - number of elements per vertex * `vertexOffset` (number) - offset of the attribute by vertex (stride). Default `0`. * `elementOffset` (number) - offset of the attribute by element. default `0`. - * `divisor` (boolean, optional) - `1` if this is an instanced attribute - (a.k.a. divisor). Default to `0`. #### `addInstanced` {#addinstanced} -Shorthand for `add()` in which all attributes `instanced` field are set to `true`. +Shorthand for `add()` in which all attributes `stepMode` field are set to `'instance'`. #### `remove` {#remove} @@ -152,6 +147,16 @@ Notes: * Any preallocated buffers in "buffers" matching registered attribute names will be used. No update will happen in this case. * Calls onUpdateStart and onUpdateEnd log callbacks before and after. +#### `getBufferLayouts` + +Returns WebGPU-style buffer layout descriptors. + +Parameters: + +* `modelInfo` (object) - a luma.gl `Model` or a similarly shaped object + + `isInstanced` (boolean) - used to resolve `stepMode: 'dynamic'` + + ## Remarks ### Attribute Type @@ -160,15 +165,20 @@ The following `type` values are supported for attribute definitions: | type | value array type | notes | | ---- | ---------------- | ----- | -| `GL.FLOAT` | `Float32Array` | | -| `GL.DOUBLE` | `Float64Array` | Because 64-bit floats are not supported by WebGL, the value is converted to an interleaved `Float32Array` before uploading to the GPU. It is exposed to the vertex shader as two attributes, `` and `64Low`, the sum of which is the 64-bit value. | -| `GL.BYTE` | `Int8Array` | | -| `GL.SHORT` | `Int16Array` | | -| `GL.INT` | `Int32Array` | | -| `GL.UNSIGNED_BYTE` | `Uint8ClampedArray` | | -| `GL.UNSIGNED_SHORT` | `Uint16Array` | | -| `GL.UNSIGNED_INT` | `Uint32Array` | | - +| `float32` | `Float32Array` | | +| `float64` | `Float64Array` | Because 64-bit floats are not supported by WebGL, the value is converted to an interleaved `Float32Array` before uploading to the GPU. It is exposed to the vertex shader as two attributes, `` and `64Low`, the sum of which is the 64-bit value. | +| `sint8` | `Int8Array` | | +| `snorm8` | `Int8Array` | Normalized | +| `uint8` | `Uint8ClampedArray` | | +| `unorm8` | `Uint8ClampedArray` | Normalized | +| `sint16` | `Int16Array` | | +| `snorm16` | `Int16Array` | Normalized | +| `uint16` | `Uint16Array` | | +| `unorm16` | `Uint16Array` | Normalized | +| `sint32` | `Int32Array` | | +| `uint32` | `Uint32Array` | | + + ## Source [modules/core/src/lib/attribute-manager.ts](https://github.com/visgl/deck.gl/blob/master/modules/core/src/lib/attribute/attribute-manager.ts) diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index 5963fead561..1685ac1655c 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -52,6 +52,7 @@ class MyLayer { While the 9.0 release of deck.gl does not yet support WebGPU, our goal is to enable WebGPU soon in a 9.x release. A number of changes will be required to deck.gl curtom layers: - deck.gl now uses uniform buffers instead of global uniforms. It is not yet required to use uniform buffers but it will be necessary if you would like to run deck.gl on WebGPU in future releases. +- When defining an attribute, `type` is now a WebGPU-style [string format](https://luma.gl/docs/api-guide/gpu/gpu-attributes#vertexformat) instead of GL constant, and `divisor` is replaced by `stepMode`. See [AttributeManager.add](./api-reference/core/attribute-manager.md#add) - WebGL draw modes `GL.TRIANGLE_FAN` and `GL.LINE_LOOP` are not supported on WebGPU. Select a different topology when creating geometries. - The luma picking module now [uses uniform buffers](https://github.com/visgl/luma.gl/blob/master/modules/shadertools/src/modules/engine/picking/picking.ts#L34-L50). To access picking state in shaders use `picking.isActive` rather than `picking_isActive` diff --git a/modules/core/src/lib/attribute/attribute-manager.ts b/modules/core/src/lib/attribute/attribute-manager.ts index e1c68a66e5b..f9faa4741dc 100644 --- a/modules/core/src/lib/attribute/attribute-manager.ts +++ b/modules/core/src/lib/attribute/attribute-manager.ts @@ -138,7 +138,7 @@ export default class AttributeManager { // Adds attributes addInstanced(attributes: {[id: string]: AttributeOptions}) { - this._add(attributes, {instanced: 1}); + this._add(attributes, {stepMode: 'instance'}); } /** @@ -308,50 +308,44 @@ export default class AttributeManager { return changedAttributes; } + /** Generate WebGPU-style buffer layout descriptors from all attributes */ getBufferLayouts( - attributes?: {[id: string]: Attribute}, - excludeAttributes: Record = {} - ): BufferLayout[] { - if (!attributes) { - attributes = this.getAttributes(); - } - const bufferMaps: BufferLayout[] = []; - for (const attributeName in attributes) { - if (!excludeAttributes[attributeName]) { - bufferMaps.push(attributes[attributeName].getBufferLayout()); - } + /** A luma.gl Model-shaped object that supplies additional hint to attribute resolution */ + modelInfo?: { + /** Whether the model is instanced */ + isInstanced?: boolean; } - return bufferMaps; + ): BufferLayout[] { + return Object.values(this.getAttributes()).map(attribute => + attribute.getBufferLayout(modelInfo) + ); } // PRIVATE METHODS - // Used to register an attribute - private _add(attributes: {[id: string]: AttributeOptions}, extraProps: any = {}) { + /** Register new attributes */ + private _add( + /** A map from attribute name to attribute descriptors */ + attributes: {[id: string]: AttributeOptions}, + /** Additional attribute settings to pass to all attributes */ + overrideOptions?: Partial + ) { for (const attributeName in attributes) { const attribute = attributes[attributeName]; + const props: AttributeOptions = { + ...attribute, + id: attributeName, + size: (attribute.isIndexed && 1) || attribute.size || 1, + ...overrideOptions + }; + // Initialize the attribute descriptor, with WebGL and metadata fields - this.attributes[attributeName] = this._createAttribute(attributeName, attribute, extraProps); + this.attributes[attributeName] = new Attribute(this.device, props); } this._mapUpdateTriggersToAttributes(); } - /* eslint-enable max-statements */ - - private _createAttribute(name: string, attribute: AttributeOptions, extraProps: any) { - // For expected default values see: - // https://github.com/visgl/luma.gl/blob/1affe21352e289eeaccee2a876865138858a765c/modules/webgl/src/classes/accessor.js#L5-L13 - // and https://deck.gl/docs/api-reference/core/attribute-manager#add - const props: AttributeOptions = { - ...attribute, - id: name, - size: (attribute.isIndexed && 1) || attribute.size || 1, - divisor: extraProps.instanced ? 1 : attribute.divisor || 0 - }; - - return new Attribute(this.device, props); - } // build updateTrigger name to attribute name mapping private _mapUpdateTriggersToAttributes() { diff --git a/modules/core/src/lib/attribute/attribute.ts b/modules/core/src/lib/attribute/attribute.ts index 9199cd42a5e..2ce352d79e4 100644 --- a/modules/core/src/lib/attribute/attribute.ts +++ b/modules/core/src/lib/attribute/attribute.ts @@ -43,6 +43,7 @@ export type Updater = ( export type AttributeOptions = DataColumnOptions<{ transition?: boolean | Partial; + stepMode?: 'vertex' | 'instance' | 'dynamic'; noAlloc?: boolean; update?: Updater; accessor?: Accessor | string | string[]; @@ -363,19 +364,31 @@ export default class Attribute extends DataColumn { return result; } - getBufferLayout( + protected _getBufferLayout( attributeName: string = this.id, options: Partial | null = null ): BufferLayout { diff --git a/modules/core/src/lib/layer.ts b/modules/core/src/lib/layer.ts index 3273b2e719f..0f5658608c7 100644 --- a/modules/core/src/lib/layer.ts +++ b/modules/core/src/lib/layer.ts @@ -771,7 +771,7 @@ export default abstract class Layer extends Component< if (bufferLayoutChanged) { // AttributeManager is always defined when this method is called const attributeManager = this.getAttributeManager()!; - model.setBufferLayout(attributeManager.getBufferLayouts()); + model.setBufferLayout(attributeManager.getBufferLayouts(model)); // All attributes must be reset after buffer layout change changedAttributes = attributeManager.getAttributes(); } diff --git a/modules/extensions/src/brushing/brushing-extension.ts b/modules/extensions/src/brushing/brushing-extension.ts index 33a8b50189d..6ebca94c86b 100644 --- a/modules/extensions/src/brushing/brushing-extension.ts +++ b/modules/extensions/src/brushing/brushing-extension.ts @@ -69,15 +69,8 @@ export default class BrushingExtension extends LayerExtension { attributeManager.add({ brushingTargets: { size: 2, - accessor: 'getBrushingTarget', - shaderAttributes: { - brushingTargets: { - divisor: 0 - }, - instanceBrushingTargets: { - divisor: 1 - } - } + stepMode: 'dynamic', + accessor: 'getBrushingTarget' } }); } diff --git a/modules/extensions/src/brushing/shader-module.ts b/modules/extensions/src/brushing/shader-module.ts index 1519bf13970..2e7368c7510 100644 --- a/modules/extensions/src/brushing/shader-module.ts +++ b/modules/extensions/src/brushing/shader-module.ts @@ -37,11 +37,7 @@ const vs = glsl` uniform vec2 brushing_mousePos; uniform float brushing_radius; - #ifdef NON_INSTANCED_MODEL in vec2 brushingTargets; - #else - in vec2 instanceBrushingTargets; - #endif out float brushing_isVisible; @@ -89,11 +85,7 @@ const inject = { } else if (brushing_target == 1) { brushingTarget = geometry.worldPositionAlt.xy; } else { - #ifdef NON_INSTANCED_MODEL brushingTarget = brushingTargets; - #else - brushingTarget = instanceBrushingTargets; - #endif } bool visible; if (brushing_target == 3) { diff --git a/modules/extensions/src/collision-filter/collision-filter-extension.ts b/modules/extensions/src/collision-filter/collision-filter-extension.ts index 344136e4f22..9ab37d29435 100644 --- a/modules/extensions/src/collision-filter/collision-filter-extension.ts +++ b/modules/extensions/src/collision-filter/collision-filter-extension.ts @@ -68,11 +68,8 @@ export default class CollisionFilterExtension extends LayerExtension { attributeManager!.add({ collisionPriorities: { size: 1, - accessor: 'getCollisionPriority', - shaderAttributes: { - collisionPriorities: {divisor: 0}, - instanceCollisionPriorities: {divisor: 1} - } + stepMode: 'dynamic', + accessor: 'getCollisionPriority' } }); } diff --git a/modules/extensions/src/collision-filter/shader-module.ts b/modules/extensions/src/collision-filter/shader-module.ts index 337c73a01b6..eead389c0a3 100644 --- a/modules/extensions/src/collision-filter/shader-module.ts +++ b/modules/extensions/src/collision-filter/shader-module.ts @@ -4,11 +4,7 @@ import {project} from '@deck.gl/core'; import {glsl} from '../utils/syntax-tags'; const vs = glsl` -#ifdef NON_INSTANCED_MODEL in float collisionPriorities; -#else -in float instanceCollisionPriorities; -#endif uniform sampler2D collision_texture; uniform bool collision_sort; @@ -60,11 +56,7 @@ const inject = { `, 'vs:DECKGL_FILTER_GL_POSITION': glsl` if (collision_sort) { - #ifdef NON_INSTANCED_MODEL float collisionPriority = collisionPriorities; - #else - float collisionPriority = instanceCollisionPriorities; - #endif position.z = -0.001 * collisionPriority * position.w; // Support range -1000 -> 1000 } diff --git a/modules/extensions/src/data-filter/data-filter-extension.ts b/modules/extensions/src/data-filter/data-filter-extension.ts index ab6369a1b04..0591273724a 100644 --- a/modules/extensions/src/data-filter/data-filter-extension.ts +++ b/modules/extensions/src/data-filter/data-filter-extension.ts @@ -167,15 +167,8 @@ export default class DataFilterExtension extends LayerExtension< filterValues: { size: filterSize, type: fp64 ? 'float64' : 'float32', - accessor: 'getFilterValue', - shaderAttributes: { - filterValues: { - divisor: 0 - }, - instanceFilterValues: { - divisor: 1 - } - } + stepMode: 'dynamic', + accessor: 'getFilterValue' } }); } @@ -184,19 +177,12 @@ export default class DataFilterExtension extends LayerExtension< attributeManager.add({ filterCategoryValues: { size: categorySize, + stepMode: 'dynamic', accessor: 'getFilterCategory', transform: categorySize === 1 ? d => extension._getCategoryKey.call(this, d, 0) - : d => d.map((x, i) => extension._getCategoryKey.call(this, x, i)), - shaderAttributes: { - filterCategoryValues: { - divisor: 0 - }, - instanceFilterCategoryValues: { - divisor: 1 - } - } + : d => d.map((x, i) => extension._getCategoryKey.call(this, x, i)) } }); } diff --git a/modules/extensions/src/data-filter/shader-module.ts b/modules/extensions/src/data-filter/shader-module.ts index e9008dce44d..7cb5333bb10 100644 --- a/modules/extensions/src/data-filter/shader-module.ts +++ b/modules/extensions/src/data-filter/shader-module.ts @@ -25,20 +25,6 @@ export type Defines = { * Enable 64-bit precision in numeric filter. */ DATAFILTER_DOUBLE?: boolean; - - // Defines derived in shader - /** - * Numeric filter attribute - */ - DATAFILTER_ATTRIB?: 'filterValues' | 'instanceFilterValues'; - /** - * Numeric filter attribute (low bits). Only used when `DATAFILTER_DOUBLE = true` - */ - DATAFILTER_ATTRIB_64LOW?: 'filterValues64Low' | 'instanceFilterValues64Low'; - /** - * Category filter attribute - */ - DATACATEGORY_ATTRIB?: 'filterCategoryValues' | 'instanceFilterCategoryValues'; }; const vs = glsl` @@ -53,17 +39,9 @@ uniform ivec4 filter_categoryBitMask; uniform DATAFILTER_TYPE filter_softMax; uniform DATAFILTER_TYPE filter_max; - #ifdef NON_INSTANCED_MODEL - #define DATAFILTER_ATTRIB filterValues - #define DATAFILTER_ATTRIB_64LOW filterValues64Low - #else - #define DATAFILTER_ATTRIB instanceFilterValues - #define DATAFILTER_ATTRIB_64LOW instanceFilterValues64Low - #endif - - in DATAFILTER_TYPE DATAFILTER_ATTRIB; + in DATAFILTER_TYPE filterValues; #ifdef DATAFILTER_DOUBLE - in DATAFILTER_TYPE DATAFILTER_ATTRIB_64LOW; + in DATAFILTER_TYPE filterValues64Low; uniform DATAFILTER_TYPE filter_min64High; uniform DATAFILTER_TYPE filter_max64High; @@ -72,12 +50,7 @@ uniform ivec4 filter_categoryBitMask; #ifdef DATACATEGORY_TYPE - #ifdef NON_INSTANCED_MODEL - #define DATACATEGORY_ATTRIB filterCategoryValues - #else - #define DATACATEGORY_ATTRIB instanceFilterCategoryValues - #endif - in DATACATEGORY_TYPE DATACATEGORY_ATTRIB; + in DATACATEGORY_TYPE filterCategoryValues; #endif out float dataFilter_value; @@ -119,7 +92,7 @@ float dataFilter_reduceValue(vec4 value) { } #endif -#ifdef DATACATEGORY_ATTRIB +#ifdef DATACATEGORY_TYPE void dataFilter_setCategoryValue(DATACATEGORY_TYPE category) { #if DATACATEGORY_CHANNELS == 1 // One 128-bit mask int dataFilter_masks = filter_categoryBitMask[int(category / 32.0)]; @@ -223,19 +196,19 @@ const inject = { 'vs:#main-start': glsl` dataFilter_value = 1.0; if (filter_enabled) { - #ifdef DATAFILTER_ATTRIB + #ifdef DATAFILTER_TYPE #ifdef DATAFILTER_DOUBLE dataFilter_setValue( - DATAFILTER_ATTRIB - filter_min64High + DATAFILTER_ATTRIB_64LOW, - DATAFILTER_ATTRIB - filter_max64High + DATAFILTER_ATTRIB_64LOW + filterValues - filter_min64High + filterValues64Low, + filterValues - filter_max64High + filterValues64Low ); #else - dataFilter_setValue(DATAFILTER_ATTRIB, DATAFILTER_ATTRIB); + dataFilter_setValue(filterValues, filterValues); #endif #endif - #ifdef DATACATEGORY_ATTRIB - dataFilter_setCategoryValue(DATACATEGORY_ATTRIB); + #ifdef DATACATEGORY_TYPE + dataFilter_setCategoryValue(filterCategoryValues); #endif } `, diff --git a/modules/extensions/src/fill-style/fill-style-extension.ts b/modules/extensions/src/fill-style/fill-style-extension.ts index 2a1e4ae411b..179e23b1c87 100644 --- a/modules/extensions/src/fill-style/fill-style-extension.ts +++ b/modules/extensions/src/fill-style/fill-style-extension.ts @@ -110,41 +110,20 @@ export default class FillStyleExtension extends LayerExtension vertexPositions: { size: 3, type: 'float64', + stepMode: 'dynamic', fp64: this.use64bitPositions(), transition: ATTRIBUTE_TRANSITION, accessor: 'getPolygon', @@ -219,68 +220,47 @@ export default class SolidPolygonLayer update: this.calculatePositions, noAlloc, shaderAttributes: { - instancePositions: { - vertexOffset: 0, - divisor: 1 - }, - instanceNextPositions: { - vertexOffset: 1, - divisor: 1 + nextVertexPositions: { + vertexOffset: 1 } } }, instanceVertexValid: { size: 1, type: 'uint16', - divisor: 1, + stepMode: 'instance', // eslint-disable-next-line @typescript-eslint/unbound-method update: this.calculateVertexValid, noAlloc }, elevations: { size: 1, + stepMode: 'dynamic', transition: ATTRIBUTE_TRANSITION, - accessor: 'getElevation', - shaderAttributes: { - instanceElevations: { - divisor: 1 - } - } + accessor: 'getElevation' }, fillColors: { size: this.props.colorFormat.length, type: 'unorm8', + stepMode: 'dynamic', transition: ATTRIBUTE_TRANSITION, accessor: 'getFillColor', - defaultValue: DEFAULT_COLOR, - shaderAttributes: { - instanceFillColors: { - divisor: 1 - } - } + defaultValue: DEFAULT_COLOR }, lineColors: { size: this.props.colorFormat.length, type: 'unorm8', + stepMode: 'dynamic', transition: ATTRIBUTE_TRANSITION, accessor: 'getLineColor', - defaultValue: DEFAULT_COLOR, - shaderAttributes: { - instanceLineColors: { - divisor: 1 - } - } + defaultValue: DEFAULT_COLOR }, pickingColors: { size: 4, type: 'uint8', + stepMode: 'dynamic', accessor: (object, {index, target: value}) => - this.encodePickingColor(object && object.__source ? object.__source.index : index, value), - shaderAttributes: { - instancePickingColors: { - divisor: 1 - } - } + this.encodePickingColor(object && object.__source ? object.__source.index : index, value) } }); /* eslint-enable max-len */ @@ -412,11 +392,10 @@ export default class SolidPolygonLayer let sideModel; let wireframeModel; - const bufferLayout = this.getAttributeManager()!.getBufferLayouts(); - if (filled) { const shaders = this.getShaders('top'); shaders.defines.NON_INSTANCED_MODEL = 1; + const bufferLayout = this.getAttributeManager()!.getBufferLayouts({isInstanced: false}); topModel = new Model(this.context.device, { ...shaders, @@ -433,6 +412,8 @@ export default class SolidPolygonLayer }); } if (extruded) { + const bufferLayout = this.getAttributeManager()!.getBufferLayouts({isInstanced: true}); + sideModel = new Model(this.context.device, { ...this.getShaders('side'), id: `${id}-side`, diff --git a/test/modules/core/lib/attribute/attribute-manager.spec.ts b/test/modules/core/lib/attribute/attribute-manager.spec.ts index 4ddcb247ecb..92b151bf617 100644 --- a/test/modules/core/lib/attribute/attribute-manager.spec.ts +++ b/test/modules/core/lib/attribute/attribute-manager.spec.ts @@ -70,10 +70,11 @@ test('AttributeManager.add', t => { {positions: ['positions'], getPosition: ['positions']}, 'AttributeManager.add - build update triggers mapping' ); + attributeManager.addInstanced({instancePositions: {size: 2, accessor: 'getPosition', update}}); t.equals( - attributeManager.getAttributes()['positions'].settings.divisor, - 0, - 'AttributeManager.add creates attribute with default divisor of 0' + attributeManager.getAttributes()['instancePositions'].settings.stepMode, + 'instance', + 'AttributeManager.addInstanced creates attribute with stepMode:instance' ); t.end(); }); @@ -370,3 +371,90 @@ test('AttributeManager.invalidate', t => { t.end(); }); + +test('AttributeManager.getBufferLayouts', t => { + const attributeManager = new AttributeManager(device); + attributeManager.add({ + // indexed attribute + indices: {size: 1, isIndexed: true, update}, + // non-instanced attribute + colors: {size: 4, type: 'unorm8', stepMode: 'vertex', accessor: 'getColor'}, + // instanced attribute + instanceColors: {size: 4, type: 'unorm8', stepMode: 'instance', accessor: 'getColor'}, + // dynamically assigned stepMode + positions: {size: 3, type: 'float64', fp64: true, stepMode: 'dynamic', accessor: 'getPosition'} + }); + + t.deepEqual( + attributeManager.getBufferLayouts(), + [ + { + name: 'indices', + byteStride: 4, + attributes: [ + { + attribute: 'indices', + format: 'uint32', + byteOffset: 0 + } + ], + stepMode: 'vertex' + }, + { + name: 'colors', + byteStride: 4, + attributes: [ + { + attribute: 'colors', + format: 'unorm8x4', + byteOffset: 0 + } + ], + stepMode: 'vertex' + }, + { + name: 'instanceColors', + byteStride: 4, + attributes: [ + { + attribute: 'instanceColors', + format: 'unorm8x4', + byteOffset: 0 + } + ], + stepMode: 'instance' + }, + { + name: 'positions', + byteStride: 24, + attributes: [ + { + attribute: 'positions', + format: 'float32x3', + byteOffset: 0 + }, + { + attribute: 'positions64Low', + format: 'float32x3', + byteOffset: 12 + } + ], + stepMode: 'instance' + } + ], + 'getBufferLayouts()' + ); + + t.is( + attributeManager.getBufferLayouts({isInstanced: false})[3].stepMode, + 'vertex', + 'dynamic attribute.stepMode in nonInstancedModel' + ); + t.is( + attributeManager.getBufferLayouts({isInstanced: true})[3].stepMode, + 'instance', + 'dynamic attribute.stepMode in instancedModel' + ); + + t.end(); +}); diff --git a/test/modules/core/lib/attribute/attribute.spec.ts b/test/modules/core/lib/attribute/attribute.spec.ts index 9006a87ddf5..dc4a97043c8 100644 --- a/test/modules/core/lib/attribute/attribute.spec.ts +++ b/test/modules/core/lib/attribute/attribute.spec.ts @@ -253,13 +253,10 @@ test('Attribute#shaderAttributes', t => { id: 'positions', update, size: 3, + stepMode: 'instance', shaderAttributes: { - instancePositions: { - divisor: 1 - }, - instanceNextPositions: { - vertexOffset: 1, - divisor: 1 + nextPositions: { + vertexOffset: 1 } } }); @@ -271,15 +268,12 @@ test('Attribute#shaderAttributes', t => { t.is(attributeLayout.format, 'float32x3', 'Attribute position has correct format'); t.is(attributeLayout.byteOffset, 0, 'Attribute position has correct offset'); attributeLayout = bufferLayout.attributes[1]; - t.is(attributeLayout.format, 'float32x3', 'Attribute instancePositions has correct format'); - t.is(attributeLayout.byteOffset, 0, 'Attribute instancePositions has correct offset'); - attributeLayout = bufferLayout.attributes[2]; - t.is(attributeLayout.format, 'float32x3', 'Attribute instanceNextPositions has correct format'); - t.is(attributeLayout.byteOffset, 12, 'Attribute instanceNextPositions has correct offset'); + t.is(attributeLayout.format, 'float32x3', 'Attribute nextPositions has correct format'); + t.is(attributeLayout.byteOffset, 12, 'Attribute nextPositions has correct offset'); t.deepEquals( attribute.getValue(), - {positions: buffer1, instancePositions: buffer1, instanceNextPositions: buffer1}, + {positions: buffer1, nextPositions: buffer1}, 'Attribute has buffer' ); From 1015b043111db13b9d91d9d8eb7c21833e7c3cc1 Mon Sep 17 00:00:00 2001 From: Anton Ahatov Date: Mon, 6 May 2024 01:40:05 +0100 Subject: [PATCH 06/10] Update using-with-maplibre.md (#8868) --- docs/developer-guide/base-maps/using-with-maplibre.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developer-guide/base-maps/using-with-maplibre.md b/docs/developer-guide/base-maps/using-with-maplibre.md index 0c19f16041e..489cf4ed882 100644 --- a/docs/developer-guide/base-maps/using-with-maplibre.md +++ b/docs/developer-guide/base-maps/using-with-maplibre.md @@ -96,7 +96,7 @@ function DeckGLOverlay(props: DeckProps) { } function App() { - const layers: [ + const layers = [ new ScatterplotLayer({ id: 'deckgl-circle', data: [ @@ -180,7 +180,7 @@ import {ScatterplotLayer} from '@deck.gl/layers'; import 'maplibre-gl/dist/maplibre-gl.css'; function App() { - const layers: [ + const layers = [ new ScatterplotLayer({ id: 'deckgl-circle', data: [ From 2604ca51cebfb772c192bfc02a8323d2560f92ed Mon Sep 17 00:00:00 2001 From: Birk Skyum <74932975+birkskyum@users.noreply.github.com> Date: Mon, 6 May 2024 05:38:38 +0200 Subject: [PATCH 07/10] Add maplibre to landing (#8861) --- website/src/pages/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/index.jsx b/website/src/pages/index.jsx index 04394c1016e..1b15a126cc4 100644 --- a/website/src/pages/index.jsx +++ b/website/src/pages/index.jsx @@ -113,7 +113,7 @@ export default function IndexPage() {

While deck.gl works standalone without a base map, it plays nicely with your favorite - base map providers such as Google Maps, Mapbox, ArcGIS and more. Where the base map + base map libraries such as Google Maps, Mapbox, ArcGIS, MapLibre, and more. Where the base map library permits, deck.gl may interleave with 3D map layers to create seamless visualizations.

From 280a07d656565f9a1c87eff59700ff6ffd0ab271 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Mon, 6 May 2024 03:25:59 -0400 Subject: [PATCH 08/10] fix(carto): Fix broken encoding in POST requests (#8865) --- .../carto/src/api/request-with-parameters.ts | 21 ++++---- modules/carto/src/sources/base-source.ts | 2 +- .../src/sources/boundary-query-source.ts | 8 ++-- .../src/sources/boundary-table-source.ts | 4 +- modules/carto/src/sources/h3-query-source.ts | 4 +- .../carto/src/sources/quadbin-query-source.ts | 4 +- .../carto/src/sources/vector-query-source.ts | 8 ++-- .../carto/src/sources/vector-table-source.ts | 4 +- .../carto/api/request-with-parameters.spec.ts | 48 +++++++++++++++++++ test/modules/carto/mock-fetch.ts | 13 +++-- 10 files changed, 86 insertions(+), 30 deletions(-) diff --git a/modules/carto/src/api/request-with-parameters.ts b/modules/carto/src/api/request-with-parameters.ts index 6301586a3b6..f11cf4c95ea 100644 --- a/modules/carto/src/api/request-with-parameters.ts +++ b/modules/carto/src/api/request-with-parameters.ts @@ -1,3 +1,4 @@ +import {isPureObject} from '@loaders.gl/core'; import {CartoAPIError} from './carto-api-error'; import {DEFAULT_HEADERS, DEFAULT_PARAMETERS, MAX_GET_LENGTH} from './common'; import type {APIErrorContext} from './types'; @@ -5,8 +6,11 @@ import type {APIErrorContext} from './types'; /** * Simple encode parameter */ -function encodeParameter(name: string, value: string | boolean | number): string { - return `${name}=${encodeURIComponent(value)}`; +function encodeParameter(name: string, value: unknown): string { + if (isPureObject(value) || Array.isArray(value)) { + return `${name}=${encodeURIComponent(JSON.stringify(value))}`; + } + return `${name}=${encodeURIComponent(value as string | boolean | number)}`; } const REQUEST_CACHE = new Map>(); @@ -17,10 +21,11 @@ export async function requestWithParameters({ errorContext }: { baseUrl: string; - parameters?: Record; + parameters?: Record; headers: Record; errorContext: APIErrorContext; }): Promise { + parameters = {...DEFAULT_PARAMETERS, ...parameters}; const key = createCacheKey(baseUrl, parameters || {}, customHeaders || {}); if (REQUEST_CACHE.has(key)) { return REQUEST_CACHE.get(key) as Promise; @@ -58,7 +63,7 @@ export async function requestWithParameters({ function createCacheKey( baseUrl: string, - parameters: Record, + parameters: Record, headers: Record ): string { const parameterEntries = Object.entries(parameters).sort(([a], [b]) => (a > b ? 1 : -1)); @@ -66,11 +71,9 @@ function createCacheKey( return JSON.stringify({baseUrl, parameters: parameterEntries, headers: headerEntries}); } -function createURLWithParameters(baseUrl: string, parameters: Record): string { - const encodedParameters = Object.entries({...DEFAULT_PARAMETERS, ...parameters}).map( - ([key, value]) => { - return encodeParameter(key, value); - } +function createURLWithParameters(baseUrl: string, parameters: Record): string { + const encodedParameters = Object.entries(parameters).map(([key, value]) => + encodeParameter(key, value) ); return `${baseUrl}?${encodedParameters.join('&')}`; } diff --git a/modules/carto/src/sources/base-source.ts b/modules/carto/src/sources/base-source.ts index 82b40fdd422..45b31fbc0ce 100644 --- a/modules/carto/src/sources/base-source.ts +++ b/modules/carto/src/sources/base-source.ts @@ -19,7 +19,7 @@ export const SOURCE_DEFAULTS: SourceOptionalOptions = { headers: {} }; -export async function baseSource>( +export async function baseSource>( endpoint: MapType, options: Partial & SourceRequiredOptions, urlParameters: UrlParameters diff --git a/modules/carto/src/sources/boundary-query-source.ts b/modules/carto/src/sources/boundary-query-source.ts index f2d992a8b7a..f6542a8437e 100644 --- a/modules/carto/src/sources/boundary-query-source.ts +++ b/modules/carto/src/sources/boundary-query-source.ts @@ -12,11 +12,11 @@ export type BoundaryQuerySourceOptions = SourceOptions & }; type UrlParameters = { columns?: string; - filters?: string; + filters?: Record; tilesetTableName: string; matchingColumn: string; propertiesSqlQuery: string; - queryParameters?: string; + queryParameters?: Record | unknown[]; }; export const boundaryQuerySource = async function ( @@ -40,10 +40,10 @@ export const boundaryQuerySource = async function ( urlParameters.columns = columns.join(','); } if (filters) { - urlParameters.filters = JSON.stringify(filters); + urlParameters.filters = filters; } if (queryParameters) { - urlParameters.queryParameters = JSON.stringify(queryParameters); + urlParameters.queryParameters = queryParameters; } return baseSource('boundary', options, urlParameters) as Promise; }; diff --git a/modules/carto/src/sources/boundary-table-source.ts b/modules/carto/src/sources/boundary-table-source.ts index f932a8524e2..f0fe3637148 100644 --- a/modules/carto/src/sources/boundary-table-source.ts +++ b/modules/carto/src/sources/boundary-table-source.ts @@ -9,7 +9,7 @@ export type BoundaryTableSourceOptions = SourceOptions & propertiesTableName: string; }; type UrlParameters = { - filters?: string; + filters?: Record; tilesetTableName: string; columns?: string; matchingColumn: string; @@ -30,7 +30,7 @@ export const boundaryTableSource = async function ( urlParameters.columns = columns.join(','); } if (filters) { - urlParameters.filters = JSON.stringify(filters); + urlParameters.filters = filters; } return baseSource('boundary', options, urlParameters) as Promise; }; diff --git a/modules/carto/src/sources/h3-query-source.ts b/modules/carto/src/sources/h3-query-source.ts index 6d3a71f9046..628f3c7d577 100644 --- a/modules/carto/src/sources/h3-query-source.ts +++ b/modules/carto/src/sources/h3-query-source.ts @@ -16,7 +16,7 @@ type UrlParameters = { spatialDataType: SpatialDataType; spatialDataColumn?: string; q: string; - queryParameters?: string; + queryParameters?: Record | unknown[]; }; export const h3QuerySource = async function ( @@ -40,7 +40,7 @@ export const h3QuerySource = async function ( urlParameters.aggregationResLevel = String(aggregationResLevel); } if (queryParameters) { - urlParameters.queryParameters = JSON.stringify(queryParameters); + urlParameters.queryParameters = queryParameters; } return baseSource('query', options, urlParameters) as Promise; }; diff --git a/modules/carto/src/sources/quadbin-query-source.ts b/modules/carto/src/sources/quadbin-query-source.ts index 75000c1eb9a..ccfae16cb65 100644 --- a/modules/carto/src/sources/quadbin-query-source.ts +++ b/modules/carto/src/sources/quadbin-query-source.ts @@ -17,7 +17,7 @@ type UrlParameters = { spatialDataType: SpatialDataType; spatialDataColumn?: string; q: string; - queryParameters?: string; + queryParameters?: Record | unknown[]; }; export const quadbinQuerySource = async function ( @@ -41,7 +41,7 @@ export const quadbinQuerySource = async function ( urlParameters.aggregationResLevel = String(aggregationResLevel); } if (queryParameters) { - urlParameters.queryParameters = JSON.stringify(queryParameters); + urlParameters.queryParameters = queryParameters; } return baseSource('query', options, urlParameters) as Promise; }; diff --git a/modules/carto/src/sources/vector-query-source.ts b/modules/carto/src/sources/vector-query-source.ts index bac88ac98c6..b7bef25f9ad 100644 --- a/modules/carto/src/sources/vector-query-source.ts +++ b/modules/carto/src/sources/vector-query-source.ts @@ -17,12 +17,12 @@ export type VectorQuerySourceOptions = SourceOptions & type UrlParameters = { columns?: string; - filters?: string; + filters?: Record; spatialDataType: SpatialDataType; spatialDataColumn?: string; tileResolution?: string; q: string; - queryParameters?: string; + queryParameters?: Record | unknown[]; }; export const vectorQuerySource = async function ( @@ -48,10 +48,10 @@ export const vectorQuerySource = async function ( urlParameters.columns = columns.join(','); } if (filters) { - urlParameters.filters = JSON.stringify(filters); + urlParameters.filters = filters; } if (queryParameters) { - urlParameters.queryParameters = JSON.stringify(queryParameters); + urlParameters.queryParameters = queryParameters; } return baseSource('query', options, urlParameters) as Promise; }; diff --git a/modules/carto/src/sources/vector-table-source.ts b/modules/carto/src/sources/vector-table-source.ts index 0b272253a67..fe39d774ced 100644 --- a/modules/carto/src/sources/vector-table-source.ts +++ b/modules/carto/src/sources/vector-table-source.ts @@ -16,7 +16,7 @@ export type VectorTableSourceOptions = SourceOptions & ColumnsOption; type UrlParameters = { columns?: string; - filters?: string; + filters?: Record; spatialDataType: SpatialDataType; spatialDataColumn?: string; tileResolution?: string; @@ -45,7 +45,7 @@ export const vectorTableSource = async function ( urlParameters.columns = columns.join(','); } if (filters) { - urlParameters.filters = JSON.stringify(filters); + urlParameters.filters = filters; } return baseSource('table', options, urlParameters) as Promise; }; diff --git a/test/modules/carto/api/request-with-parameters.spec.ts b/test/modules/carto/api/request-with-parameters.spec.ts index 7f7ddf428d3..f20f9489451 100644 --- a/test/modules/carto/api/request-with-parameters.spec.ts +++ b/test/modules/carto/api/request-with-parameters.spec.ts @@ -101,3 +101,51 @@ test('requestWithParameters#nocacheErrorContext', async t => { ); t.end(); }); + +test('requestWithParameters#method', async t => { + await withMockFetchMapsV3(async calls => { + t.equals(calls.length, 0, '0 initial calls'); + + await Promise.all([ + requestWithParameters({ + baseUrl: 'https://example.com/v1/params', + headers: {}, + parameters: {object: {a: 1, b: 2}, array: [1, 2, 3], string: 'short'} + }), + requestWithParameters({ + baseUrl: `https://example.com/v1/params`, + headers: {}, + parameters: {object: {a: 1, b: 2}, array: [1, 2, 3], string: 'long'.padEnd(10_000, 'g')} + }) + ]); + + t.equals(calls.length, 2, '2 requests'); + + // GET + t.true(calls[0].url.startsWith('https://example.com/v1/params?'), 'get - url'); + t.equals(calls[0].method, undefined, 'get - method'); + t.equals(calls[0].body, undefined, 'get - body'); + t.deepEquals( + Array.from(new URL(calls[0].url).searchParams.entries()), + [ + ['v', '3.4'], + ['deckglVersion', 'untranspiled source'], + ['object', '{"a":1,"b":2}'], + ['array', '[1,2,3]'], + ['string', 'short'] + ], + 'get - params' + ); + + // POST + const postBody = JSON.parse(calls[1].body as string); + t.equals(calls[1].method, 'POST', 'post - method'); + t.equals(postBody.v, '3.4', 'post - body.v'); + t.equals(postBody.deckglVersion, 'untranspiled source', 'post - body.deckglVersion'); + t.deepEquals(postBody.object, {a: 1, b: 2}, 'post - body.object'); + t.deepEquals(postBody.array, [1, 2, 3], 'post - body.array'); + t.true(postBody.string.startsWith('longgg'), 'post - body.string'); + t.equals(calls[1].url, 'https://example.com/v1/params', 'post - url'); + }); + t.end(); +}); diff --git a/test/modules/carto/mock-fetch.ts b/test/modules/carto/mock-fetch.ts index 7d4a10fbe6c..33f919f949c 100644 --- a/test/modules/carto/mock-fetch.ts +++ b/test/modules/carto/mock-fetch.ts @@ -5,7 +5,12 @@ import binaryTileData from './data/binaryTile.json'; const BINARY_TILE = new Uint8Array(binaryTileData).buffer; const fetch = globalThis.fetch; -type MockFetchCall = {url: string; headers: Record}; +type MockFetchCall = { + url: string; + headers: Record; + method?: 'GET' | 'POST'; + body?: string; +}; export const TILEJSON_RESPONSE = { tilejson: '2.2.0', @@ -96,8 +101,8 @@ async function setupMockFetchMapsV3( ): Promise { const calls: MockFetchCall[] = []; - const mockFetch = (url: string, {headers}) => { - calls.push({url, headers}); + const mockFetch = (url: string, {headers, method, body}) => { + calls.push({url, headers, method, body}); if (url.indexOf('formatTiles=binary') !== -1) { headers = {...headers, 'Content-Type': 'application/vnd.carto-vector-tile'}; } @@ -114,7 +119,7 @@ function teardownMockFetchMaps() { } export async function withMockFetchMapsV3( - testFunc: (calls: {url: string; headers: Record}[]) => Promise, + testFunc: (calls: MockFetchCall[]) => Promise, responseFunc: ( url: string, headers: HeadersInit, From c9179146538ecae1c14653efcb454fd7d8b971c8 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Mon, 6 May 2024 11:57:23 -0400 Subject: [PATCH 09/10] fix(pydeck,pydeck-carto): Put readthedocs configs in bindings (#8866) * fix(pydeck): move readthedocs config to bindings/pydeck * fix(pydeck-carto): add readthedocs config --- .../pydeck-carto/.readthedocs.yml | 0 bindings/pydeck/.readthedocs.yml | 13 +++++++++++++ 2 files changed, 13 insertions(+) rename .readthedocs.yml => bindings/pydeck-carto/.readthedocs.yml (100%) create mode 100644 bindings/pydeck/.readthedocs.yml diff --git a/.readthedocs.yml b/bindings/pydeck-carto/.readthedocs.yml similarity index 100% rename from .readthedocs.yml rename to bindings/pydeck-carto/.readthedocs.yml diff --git a/bindings/pydeck/.readthedocs.yml b/bindings/pydeck/.readthedocs.yml new file mode 100644 index 00000000000..c2f01649ff4 --- /dev/null +++ b/bindings/pydeck/.readthedocs.yml @@ -0,0 +1,13 @@ +version: 2 +sphinx: + configuration: docs/conf.py +python: + install: + - method: pip + path: . + extra_requirements: + - docs +build: + os: "ubuntu-22.04" + tools: + python: "3.9" From 1a9dd6f1975f8c0a19f5d0e6a354626a97bd957e Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Mon, 6 May 2024 13:30:17 -0400 Subject: [PATCH 10/10] fix(pydeck,pydeck-carto): Rename .readthedocs.yml to .yaml (#8869) --- bindings/pydeck-carto/{.readthedocs.yml => .readthedocs.yaml} | 0 bindings/pydeck/{.readthedocs.yml => .readthedocs.yaml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename bindings/pydeck-carto/{.readthedocs.yml => .readthedocs.yaml} (100%) rename bindings/pydeck/{.readthedocs.yml => .readthedocs.yaml} (100%) diff --git a/bindings/pydeck-carto/.readthedocs.yml b/bindings/pydeck-carto/.readthedocs.yaml similarity index 100% rename from bindings/pydeck-carto/.readthedocs.yml rename to bindings/pydeck-carto/.readthedocs.yaml diff --git a/bindings/pydeck/.readthedocs.yml b/bindings/pydeck/.readthedocs.yaml similarity index 100% rename from bindings/pydeck/.readthedocs.yml rename to bindings/pydeck/.readthedocs.yaml