Skip to content

Commit

Permalink
feat(vue): Add FormulaWidget
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Aug 29, 2024
1 parent 42a277a commit 64cc4a0
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/create-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 7 additions & 1 deletion packages/create-vue/src/components/views/Default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ 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';
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';
Expand Down Expand Up @@ -74,6 +76,7 @@ const layers = computed(() => [
const map = shallowRef<Map | null>(null);
const deck = shallowRef<Deck | null>(null);
const viewState = ref<MapViewState>(INITIAL_VIEW_STATE);
const viewStateDebounced = refDebounced(viewState, 200);
const attributionHTML = ref<string>('');
watchEffect(() => {
Expand Down Expand Up @@ -131,7 +134,10 @@ onUnmounted(() => {
</Card>
<span class="flex-space" />
<Card title="Tower count">
<div class="skeleton" style="height: 8em" />
<FormulaWidget
:data="data"
:viewState="viewStateDebounced as MapViewState"
/>
</Card>
<Card title="Towers by radio">
<div class="skeleton" style="height: 6em" />
Expand Down
Empty file.
73 changes: 73 additions & 0 deletions packages/create-vue/src/components/widgets/FormulaWidget.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script setup lang="ts">
import { ref } from 'vue';
import { computedAsync } from '@vueuse/core';
import { MapViewState } from '@deck.gl/core';
import { AggregationType, WidgetSource } from '@carto/api-client';
import {
createSpatialFilter,
WidgetStatus,
numberFormatter,
} from '../../utils';
const props = withDefaults(
defineProps<{
data: Promise<{ widgetSource: WidgetSource }>;
column?: string;
operation?: AggregationType;
viewState?: MapViewState;
}>(),
{
column: '',
operation: 'count',
},
);
const status = ref<WidgetStatus>('loading');
const value = computedAsync(async (onCancel) => {
const column = props.column;
const operation = props.operation;
const viewState = props.viewState;
const abortController = new AbortController();
onCancel(() => abortController.abort());
status.value = 'loading';
return props.data
.then(({ widgetSource }) =>
widgetSource.getFormula({
column,
operation,
spatialFilter: viewState && createSpatialFilter(viewState),
abortController,
}),
)
.then((response) => {
status.value = 'complete';
return response.value;
})
.catch(() => {
if (!abortController.signal.aborted) {
status.value = 'error';
}
return -1;
});
}, -1);
</script>

<template>
<template v-if="status === 'loading'">
<span class="title">...</span>
</template>

<template v-else-if="status === 'error'">
<span class="title">⚠ Error</span>
</template>

<template v-else>
<data class="title" :value="value"
>{{ numberFormatter.format(value) }}
</data>
</template>
</template>
29 changes: 28 additions & 1 deletion packages/create-vue/src/utils.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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]),
],
],
};
}
52 changes: 52 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit 64cc4a0

Please sign in to comment.