From addb30aa8321eb92567cc9afc3d0a5aabe158062 Mon Sep 17 00:00:00 2001 From: "Juan D. Jara" Date: Wed, 11 Dec 2024 11:00:20 +0100 Subject: [PATCH] first h3 widgets example --- h3-widgets/index.html | 9 ++++-- h3-widgets/index.ts | 71 +++++++++++++++++++++++++++++++++++------ h3-widgets/package.json | 2 ++ h3-widgets/style.css | 20 +++++++++++- 4 files changed, 90 insertions(+), 12 deletions(-) diff --git a/h3-widgets/index.html b/h3-widgets/index.html index 72b1491..687d8a7 100644 --- a/h3-widgets/index.html +++ b/h3-widgets/index.html @@ -10,7 +10,7 @@

✨👀 You're viewing

-

CARTO Spatial Features datasets in H3 (12M rows).

+

CARTO Widgets for H3 (12M rows).

More info about CARTO Spatial Features datasets in H3 (12M rows).

-
+

Total Population

+
+

Urbanity categories

+ + +
diff --git a/h3-widgets/index.ts b/h3-widgets/index.ts index abec412..af08b84 100644 --- a/h3-widgets/index.ts +++ b/h3-widgets/index.ts @@ -5,18 +5,22 @@ import {Deck, MapViewState} from '@deck.gl/core'; import {H3TileLayer, BASEMAP, colorBins} from '@deck.gl/carto'; import { initSelectors } from './selectorUtils'; import { debounce, getSpatialFilterFromViewState } from './utils'; -import { h3QuerySource } from '@carto/api-client' +import { h3QuerySource, WidgetSource } from '@carto/api-client' +import Chart from 'chart.js/auto'; const cartoConfig = { + // @ts-expect-error misconfigured env variables apiBaseUrl: import.meta.env.VITE_API_BASE_URL, + // @ts-expect-error misconfigured env variables accessToken: import.meta.env.VITE_API_ACCESS_TOKEN, connectionName: 'carto_dw' }; const INITIAL_VIEW_STATE: MapViewState = { - latitude: 40.7128, // New York - longitude: -74.006, // New York - zoom: 7, + // Spain + latitude: 37.3753636, + longitude: -5.9962577, + zoom: 6, pitch: 0, bearing: 0, minZoom: 3.5, @@ -36,6 +40,9 @@ let viewState = INITIAL_VIEW_STATE const variableSelector = document.getElementById('variable') as HTMLSelectElement; const aggMethodLabel = document.getElementById('agg-method') as HTMLSelectElement; const formulaWidget = document.getElementById('formula-data') as HTMLDivElement; +const categoriesWidget = document.getElementById('categories-data') as HTMLCanvasElement; +const histogramWidget = document.getElementById('histogram-data') as HTMLCanvasElement; +let histogramChart: Chart; aggMethodLabel.innerText = aggregationExp; variableSelector?.addEventListener('change', () => { @@ -51,8 +58,8 @@ variableSelector?.addEventListener('change', () => { function render() { source = h3QuerySource({ ...cartoConfig, - aggregationExp: `${aggregationExp} as value`, - sqlQuery: `SELECT * FROM cartobq.public_account.derived_spatialfeatures_usa_h3int_res8_v1_yearly_v2` + aggregationExp: `${aggregationExp} as value`, + sqlQuery: `SELECT * FROM carto-demo-data.demo_tables.derived_spatialfeatures_esp_h3res8_v1_yearly_v2` }); renderLayers(); renderWidgets(); @@ -91,15 +98,61 @@ function renderLayers() { } async function renderWidgets() { - formulaWidget.innerHTML = 'Loading...' const { widgetSource } = await source - const formula = await widgetSource.getFormula({ + await Promise.all([ + renderFormula(widgetSource), + renderHistogram(widgetSource) + ]) +} + +async function renderFormula(ws: WidgetSource) { + formulaWidget.innerHTML = 'Loading...' + const formula = await ws.getFormula({ column: selectedVariable, + operation: 'sum', + spatialFilter: getSpatialFilterFromViewState(viewState), + viewState, + }) + formulaWidget.textContent = Intl.NumberFormat('en-US', { + maximumFractionDigits: 0, + // notation: 'compact' + }).format(formula.value) +} + +const TICKS = [100, 500, 1000, 5000]; + +async function renderHistogram(ws: WidgetSource) { + histogramWidget.parentElement?.querySelector('.loader')?.classList.toggle('hidden', false); + histogramWidget.classList.toggle('hidden', true); + + const categories = await ws.getCategories({ + column: 'urbanity', operation: 'count', spatialFilter: getSpatialFilterFromViewState(viewState), viewState, }) - formulaWidget.textContent = Intl.NumberFormat('en-US').format(formula.value) + + histogramWidget.parentElement?.querySelector('.loader')?.classList.toggle('hidden', true); + histogramWidget.classList.toggle('hidden', false); + + if (histogramChart) { + histogramChart.data.labels = categories.map((c) => c.name); + histogramChart.data.datasets[0].data = categories.map((c) => c.value); + histogramChart.update(); + } else { + histogramChart = new Chart(histogramWidget, { + type: 'bar', + data: { + labels: categories.map((c) => c.name), + datasets: [ + { + label: 'Urbanity category', + data: categories.map((c) => c.value), + } + ] + }, + }) + } } const debouncedRenderWidgets = debounce(renderWidgets, 500); diff --git a/h3-widgets/package.json b/h3-widgets/package.json index f7fd3ef..06894ed 100644 --- a/h3-widgets/package.json +++ b/h3-widgets/package.json @@ -14,6 +14,7 @@ "vite": "^4.5.0" }, "dependencies": { + "@carto/api-client": "^0.4.1-alpha.0", "@deck.gl/aggregation-layers": "^9.0.17", "@deck.gl/carto": "^9.0.17", "@deck.gl/core": "^9.0.17", @@ -21,6 +22,7 @@ "@deck.gl/geo-layers": "^9.0.17", "@deck.gl/layers": "^9.0.17", "@deck.gl/mesh-layers": "^9.0.17", + "chart.js": "^4.4.7", "maplibre-gl": "^3.5.2" } } diff --git a/h3-widgets/style.css b/h3-widgets/style.css index d286268..8be1dad 100644 --- a/h3-widgets/style.css +++ b/h3-widgets/style.css @@ -122,7 +122,7 @@ button:hover { border: #ccc 1px solid; } -.widgets { +.widget { margin: 2rem 0; } @@ -131,3 +131,21 @@ button:hover { font-weight: 600; font-family: monospace; } + +.relative { position: relative; } + +.loader { + font-size: 14px; + font-weight: 400; + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + opacity: 1; + transition: opacity 0.3s; +} + +.hidden { + opacity: 0; +}