diff --git a/packages/create-vue/package.json b/packages/create-vue/package.json index 96766b5..5f6bb9b 100644 --- a/packages/create-vue/package.json +++ b/packages/create-vue/package.json @@ -27,6 +27,7 @@ "@loaders.gl/core": "^4.2.2", "@luma.gl/core": "^9.0.16", "@luma.gl/engine": "^9.0.16", + "@vueuse/core": "^11.0.3", "maplibre-gl": "^4.5.0", "vue": "^3.4.31", "vue-router": "4" diff --git a/packages/create-vue/src/components/views/Default.vue b/packages/create-vue/src/components/views/Default.vue index 1ace44a..3d5a568 100644 --- a/packages/create-vue/src/components/views/Default.vue +++ b/packages/create-vue/src/components/views/Default.vue @@ -7,6 +7,7 @@ import { shallowRef, watchEffect, } from 'vue'; +import { refDebounced } from '@vueuse/core'; import { Map } from 'maplibre-gl'; import { AccessorFunction, Deck, MapViewState, Color } from '@deck.gl/core'; import { colorCategories, VectorTileLayer } from '@deck.gl/carto'; @@ -14,6 +15,7 @@ import { vectorQuerySource } from '@carto/api-client'; import Layers from '../common/Layers.vue'; import Legend from '../common/Legend.vue'; import Card from '../common/Card.vue'; +import FormulaWidget from '../widgets/FormulaWidget.vue'; const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json'; @@ -74,6 +76,7 @@ const layers = computed(() => [ const map = shallowRef(null); const deck = shallowRef(null); const viewState = ref(INITIAL_VIEW_STATE); +const viewStateDebounced = refDebounced(viewState, 200); const attributionHTML = ref(''); watchEffect(() => { @@ -131,7 +134,10 @@ onUnmounted(() => { -
+
diff --git a/packages/create-vue/src/components/widgets/CategoryWidget.vue b/packages/create-vue/src/components/widgets/CategoryWidget.vue new file mode 100644 index 0000000..e69de29 diff --git a/packages/create-vue/src/components/widgets/FormulaWidget.vue b/packages/create-vue/src/components/widgets/FormulaWidget.vue new file mode 100644 index 0000000..126d4df --- /dev/null +++ b/packages/create-vue/src/components/widgets/FormulaWidget.vue @@ -0,0 +1,73 @@ + + + diff --git a/packages/create-vue/src/utils.ts b/packages/create-vue/src/utils.ts index d1e6f64..6a67c04 100644 --- a/packages/create-vue/src/utils.ts +++ b/packages/create-vue/src/utils.ts @@ -1,4 +1,11 @@ -import { Color } from '@deck.gl/core'; +import { SpatialFilter } from '@carto/api-client'; +import { Color, MapViewState, WebMercatorViewport } from '@deck.gl/core'; + +export type WidgetStatus = 'loading' | 'complete' | 'error'; + +export const numberFormatter = new Intl.NumberFormat('US', { + maximumSignificantDigits: 3, +}); /** * Converts an array of RGB values (0–255) to a hexadecimal @@ -11,3 +18,23 @@ export function toHexString(color: Color): string { Math.round(color[2]); return '#' + ('000000' + hex.toString(16)).slice(-6); } + +/** + * Creates a {@link SpatialFilter} for use in Widget API queries, + * given {@link MapViewState} from deck.gl. + */ +export function createSpatialFilter(viewState: MapViewState): SpatialFilter { + const viewport = new WebMercatorViewport(viewState); + return { + type: 'Polygon', + coordinates: [ + [ + viewport.unproject([0, 0]), + viewport.unproject([viewport.width, 0]), + viewport.unproject([viewport.width, viewport.height]), + viewport.unproject([0, viewport.height]), + viewport.unproject([0, 0]), + ], + ], + }; +} diff --git a/yarn.lock b/yarn.lock index cb50a52..cd20786 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2202,6 +2202,7 @@ __metadata: "@luma.gl/engine": "npm:^9.0.16" "@vitejs/plugin-basic-ssl": "npm:^1.1.0" "@vitejs/plugin-vue": "npm:^5.0.5" + "@vueuse/core": "npm:^11.0.3" maplibre-gl: "npm:^4.5.0" typescript: "npm:^5.2.2" vite: "npm:^5.3.4" @@ -5178,6 +5179,13 @@ __metadata: languageName: node linkType: hard +"@types/web-bluetooth@npm:^0.0.20": + version: 0.0.20 + resolution: "@types/web-bluetooth@npm:0.0.20" + checksum: 10c0/3a49bd9396506af8f1b047db087aeeea9fe4301b7fad4fe06ae0f6e00d331138caae878fd09e6410658b70b4aaf10e4b191c41c1a5ff72211fe58da290c7d003 + languageName: node + linkType: hard + "@types/wrap-ansi@npm:^3.0.0": version: 3.0.0 resolution: "@types/wrap-ansi@npm:3.0.0" @@ -5608,6 +5616,34 @@ __metadata: languageName: node linkType: hard +"@vueuse/core@npm:^11.0.3": + version: 11.0.3 + resolution: "@vueuse/core@npm:11.0.3" + dependencies: + "@types/web-bluetooth": "npm:^0.0.20" + "@vueuse/metadata": "npm:11.0.3" + "@vueuse/shared": "npm:11.0.3" + vue-demi: "npm:>=0.14.10" + checksum: 10c0/9b8266d7d7d3e2942b632217da6dc651ef7394b1744736941f48e32d73db3ccd1a5684a70d40af9c956d58f74e4719a6fa31a884cb8f76d4e09f762dbf0bfc0a + languageName: node + linkType: hard + +"@vueuse/metadata@npm:11.0.3": + version: 11.0.3 + resolution: "@vueuse/metadata@npm:11.0.3" + checksum: 10c0/d6ad604bd59b59f3342e5c227efc322e562150550003c82527cd32be97ae4e28654eacca06870ceec9b5b835c61056d83edfdfe6ff6c856f53f6a6302790ae53 + languageName: node + linkType: hard + +"@vueuse/shared@npm:11.0.3": + version: 11.0.3 + resolution: "@vueuse/shared@npm:11.0.3" + dependencies: + vue-demi: "npm:>=0.14.10" + checksum: 10c0/3d42bf185dcfd17aa20f2381271057cf55484c983795ed211c69586526b7b80ec5641a476b0ed8645dc19991203396b5a7c6219e93f9a79fab8f2ab47f680448 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1": version: 1.12.1 resolution: "@webassemblyjs/ast@npm:1.12.1" @@ -16312,6 +16348,22 @@ __metadata: languageName: node linkType: hard +"vue-demi@npm:>=0.14.10": + version: 0.14.10 + resolution: "vue-demi@npm:0.14.10" + peerDependencies: + "@vue/composition-api": ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + "@vue/composition-api": + optional: true + bin: + vue-demi-fix: bin/vue-demi-fix.js + vue-demi-switch: bin/vue-demi-switch.js + checksum: 10c0/a9ed8712fa36d01bc13c39757f95f30cebf42d557b99e94bff86d8660c81f2911b41220f7affc023d1ffcc19e13999e4a83019991e264787cca2c616e83aea48 + languageName: node + linkType: hard + "vue-router@npm:4": version: 4.4.2 resolution: "vue-router@npm:4.4.2"