Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tilesets): Support widget calculations on tilesets #50

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2d53ed8
initial commit
donmccurdy Dec 19, 2024
871292f
incremental conversion of tileset utils
donmccurdy Dec 20, 2024
40ba574
incremental typescript conversion
donmccurdy Dec 26, 2024
78055ae
tileset formula example
donmccurdy Dec 26, 2024
7a7bde1
clean up
donmccurdy Dec 26, 2024
cbc929a
incremental WIP, all tileset widget types
donmccurdy Dec 27, 2024
64dfa60
various fixes, tileset widgets
donmccurdy Dec 30, 2024
0600827
unit tests: applySorting, widget-tileset-source
donmccurdy Dec 31, 2024
0143dbb
remove raster changes from branch
donmccurdy Dec 31, 2024
710205c
remaining TS conversions
donmccurdy Dec 31, 2024
0f4625a
port more tests from C4R
donmccurdy Dec 31, 2024
f004458
unit test coverage on 'src/filters/'
donmccurdy Dec 31, 2024
aeef6eb
clean up
donmccurdy Dec 31, 2024
158384f
clean up types
donmccurdy Jan 2, 2025
196e5ec
lint
donmccurdy Jan 2, 2025
2da1d25
clean up
donmccurdy Jan 2, 2025
cb767c0
clean up
donmccurdy Jan 2, 2025
bb65c94
remove redundant BBox definition
donmccurdy Jan 2, 2025
15bad23
add @loaders.gl/schema, clean up types
donmccurdy Jan 2, 2025
608930d
Clean up
donmccurdy Jan 2, 2025
107d5b2
Clean up
donmccurdy Jan 2, 2025
7f226ed
clean up awkward types in WidgetTilesetSource
donmccurdy Jan 2, 2025
f57979a
add TODO
donmccurdy Jan 2, 2025
684390a
align api with spatial index changes
donmccurdy Jan 2, 2025
424f292
fix rebase issues
donmccurdy Jan 2, 2025
d7cb159
clean up todos
donmccurdy Jan 3, 2025
9652a52
clean up types
donmccurdy Jan 3, 2025
aeea61a
WidgetBaseSource -> WidgetRemoteSource
donmccurdy Jan 3, 2025
740354d
refactor widget source classes
donmccurdy Jan 3, 2025
f4de182
add h3 example
donmccurdy Jan 9, 2025
600bc21
lint
donmccurdy Jan 9, 2025
0259de3
chore(release): v0.5.0-alpha.0
donmccurdy Jan 9, 2025
d7ff039
feat(tileset): Add WidgetTilesetSource#extractTileFeatures() (#71)
donmccurdy Jan 15, 2025
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
1 change: 1 addition & 0 deletions examples/01-basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ <h1>Pure JS</h1>
id="table"
header="Store type"
columns='["storetype","revenue"]'
sortBy="revenue"
></table-widget>

<scatter-widget
Expand Down
116 changes: 116 additions & 0 deletions examples/03-tileset/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import maplibregl from 'maplibre-gl';
import {Deck, WebMercatorViewport} from '@deck.gl/core';
import {VectorTileLayer} from '@deck.gl/carto';
import {
Filter,
TilejsonResult,
WidgetTilesetSource,
vectorTilesetSource,
createViewportSpatialFilter,
} from '@carto/api-client';
import '../components/index.js';
import type {Widget, FilterEvent} from '../components/index.js';

/**************************************************************************
* REACTIVE STATE
*/

let data: TilejsonResult & {widgetSource: WidgetTilesetSource};
let viewState = {latitude: 40.7128, longitude: -74.006, zoom: 12};
let filters: Record<string, Filter> = {};

/**************************************************************************
* DECK.GL
*/

const deck = new Deck({
canvas: 'deck-canvas',
initialViewState: viewState,
controller: true,
layers: [],
});

const map = new maplibregl.Map({
container: 'map',
style:
'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
interactive: false,
});

deck.setProps({
onViewStateChange: (params) => {
viewState = params.viewState;
const {longitude, latitude, ...rest} = viewState;
map.jumpTo({center: [longitude, latitude], ...rest});
updateWidgets();
},
});

const widgets: Widget[] = [
bindWidget('#category'),
bindWidget('#formula'),
bindWidget('#histogram'),
bindWidget('#pie'),
bindWidget('#scatter'),
bindWidget('#table'),
];

updateSources();

/**************************************************************************
* UPDATES
*/

async function updateSources() {
data = await vectorTilesetSource({
tableName: 'cartodb-on-gcp-frontend-team.donmccurdy.retail_stores_tileset',
accessToken:
'eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfNDd1dW5tZWciLCJqdGkiOiI1Njc2MDQ0NCJ9.p2_1ts1plciXMI3Pk0GJaHjhdZUd0lKITgdO9BaX2ho',
connectionName: 'bqconn-front',
});

document.querySelector('#footer')!.innerHTML = data.attribution;

updateLayers();
updateWidgets();
}

function updateLayers() {
const layer = new VectorTileLayer({
id: 'retail_stores',
data,
pointRadiusMinPixels: 4,
getFillColor: [200, 0, 80],
onViewportLoad: (tiles) => {
const viewport = new WebMercatorViewport(viewState);
const spatialFilter = createViewportSpatialFilter(viewport.getBounds())!;
data.widgetSource.loadTiles(tiles);
data.widgetSource.extractTileFeatures({spatialFilter});
updateWidgets();
},
});

deck.setProps({layers: [layer]});
}

function updateWidgets() {
for (const widget of widgets) {
widget.data = Promise.resolve(data);
widget.viewState = viewState;
}
}

/**************************************************************************
* INITIALIZATION
*/

function bindWidget(selector: string): Widget {
const widget = document.querySelector<Widget>(selector)!;

widget.addEventListener('filter', (event) => {
filters = (event as FilterEvent).detail.filters;
updateSources();
});

return widget;
}
64 changes: 64 additions & 0 deletions examples/03-tileset/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>Examples / Tileset</title>
<link rel="stylesheet" href="../style.css" />
</head>
<body>
<main id="app">
<header>
<h1>Tileset</h1>
<a href="../">← Back</a>
</header>
<section id="view">
<div id="map"></div>
<canvas id="deck-canvas"></canvas>
</section>
<section id="rail">
<formula-widget
id="formula"
header="Total"
operation="count"
></formula-widget>

<category-widget
id="category"
header="Store type"
operation="count"
column="storetype"
></category-widget>

<pie-widget
id="pie"
header="Store type"
operation="count"
column="storetype"
></pie-widget>

<table-widget
id="table"
header="Store type"
columns='["storetype","revenue"]'
sortBy="revenue"
></table-widget>

<scatter-widget
id="scatter"
header="Size vs. Revenue"
xAxisColumn="size_m2"
yAxisColumn="revenue"
></scatter-widget>

<histogram-widget
id="histogram"
header="Revenue"
column="revenue"
ticks="[1250000, 1500000, 1750000]"
></histogram-widget>
</section>
<footer id="footer"></footer>
</main>
<script type="module" src="./app.ts"></script>
</body>
</html>
116 changes: 116 additions & 0 deletions examples/04-tileset-h3/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import maplibregl from 'maplibre-gl';
import {Deck, WebMercatorViewport} from '@deck.gl/core';
import {H3TileLayer} from '@deck.gl/carto';
import {
Filter,
TilejsonResult,
WidgetTilesetSource,
createViewportSpatialFilter,
h3TilesetSource,
} from '@carto/api-client';
import '../components/index.js';
import type {Widget, FilterEvent} from '../components/index.js';

/**************************************************************************
* REACTIVE STATE
*/

let data: TilejsonResult & {widgetSource: WidgetTilesetSource};
let viewState = {latitude: 40.7128, longitude: -74.006, zoom: 12};
let filters: Record<string, Filter> = {};

/**************************************************************************
* DECK.GL
*/

const deck = new Deck({
canvas: 'deck-canvas',
initialViewState: viewState,
controller: true,
layers: [],
});

const map = new maplibregl.Map({
container: 'map',
style:
'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
interactive: false,
});

deck.setProps({
onViewStateChange: (params) => {
viewState = params.viewState;
const {longitude, latitude, ...rest} = viewState;
map.jumpTo({center: [longitude, latitude], ...rest});
updateWidgets();
},
});

const widgets: Widget[] = [
bindWidget('#formula'),
bindWidget('#histogram'),
bindWidget('#scatter'),
bindWidget('#table'),
];

updateSources();

/**************************************************************************
* UPDATES
*/

async function updateSources() {
data = await h3TilesetSource({
tableName:
'cartodb-on-gcp-frontend-team.donmccurdy.retail_stores_tileset_h3',
accessToken:
'eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfNDd1dW5tZWciLCJqdGkiOiI1Njc2MDQ0NCJ9.p2_1ts1plciXMI3Pk0GJaHjhdZUd0lKITgdO9BaX2ho',
connectionName: 'bqconn-front',
});

document.querySelector('#footer')!.innerHTML = data.attribution;

updateLayers();
updateWidgets();
}

function updateLayers() {
const layer = new H3TileLayer({
id: 'retail_stores',
data,
pointRadiusMinPixels: 4,
getFillColor: [200, 0, 80],
extruded: false,
onViewportLoad: (tiles) => {
const viewport = new WebMercatorViewport(viewState);
const spatialFilter = createViewportSpatialFilter(viewport.getBounds())!;
data.widgetSource.loadTiles(tiles);
data.widgetSource.extractTileFeatures({spatialFilter});
updateWidgets();
},
});

deck.setProps({layers: [layer]});
}

function updateWidgets() {
for (const widget of widgets) {
widget.data = Promise.resolve(data);
widget.viewState = viewState;
}
}

/**************************************************************************
* INITIALIZATION
*/

function bindWidget(selector: string): Widget {
const widget = document.querySelector<Widget>(selector)!;

widget.addEventListener('filter', (event) => {
filters = (event as FilterEvent).detail.filters;
updateSources();
});

return widget;
}
50 changes: 50 additions & 0 deletions examples/04-tileset-h3/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>Examples / H3 Tileset</title>
<link rel="stylesheet" href="../style.css" />
</head>
<body>
<main id="app">
<header>
<h1>Tileset</h1>
<a href="../">← Back</a>
</header>
<section id="view">
<div id="map"></div>
<canvas id="deck-canvas"></canvas>
</section>
<section id="rail">
<formula-widget
id="formula"
header="Total"
operation="count"
></formula-widget>

<table-widget
id="table"
header="Store type"
columns='["revenue", "size_m2"]'
sortBy="revenue"
></table-widget>

<scatter-widget
id="scatter"
header="Size vs. Revenue"
xAxisColumn="size_m2"
yAxisColumn="revenue"
></scatter-widget>

<histogram-widget
id="histogram"
header="Revenue"
column="revenue"
ticks="[1250000, 1500000, 1750000]"
></histogram-widget>
</section>
<footer id="footer"></footer>
</main>
<script type="module" src="./app.ts"></script>
</body>
</html>
6 changes: 4 additions & 2 deletions examples/components/widgets/formula-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,15 @@ export class FormulaWidget extends BaseWidget {
pending: () =>
html`<h3>${this.header}</h3>
<figure>
<div class="scorecard">&hellip;</div>
<div class="scorecard"></div>
<figcaption>${this.caption}</figcaption>
</figure>`,
complete: (taskResult) => html`
<h3>${this.header}</h3>
<figure>
<div class="scorecard">${taskResult.toLocaleString()}</div>
<div class="scorecard">
${taskResult ? taskResult.toLocaleString() : '…'}
</div>
<figcaption>${this.caption}</figcaption>
</figure>
`,
Expand Down
2 changes: 1 addition & 1 deletion examples/components/widgets/table-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class TableWidget extends BaseWidget {
private _task = new Task(this, {
task: async ([data, columns, sortBy, sortDirection, limit], {signal}) => {
if (!data) {
return {hasData: false, rows: [], totalCount: -1};
return {rows: [], totalCount: 0};
}

await sleep(DEBOUNCE_TIME_MS);
Expand Down
8 changes: 6 additions & 2 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
<body>
<h1>Examples</h1>
<ol>
<li><a href="./01-basic/index.html">pure js</a></li>
<li><a href="./02-spatial-index/index.html">spatial index</a></li>
<li><a href="/01-basic/index.html">pure js</a></li>
<li><a href="/02-spatial-index/index.html">spatial index</a></li>
<li><a href="/03-tileset/index.html">tileset</a></li>
<li>
<a href="/04-tileset-h3/index.html">tileset - H3</a>
</li>
</ol>
</body>
</html>
Loading