Skip to content

Commit

Permalink
chore: Migrate examples to TypeScript (#8759)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress committed Apr 7, 2024
1 parent 254a4b9 commit 80ac105
Show file tree
Hide file tree
Showing 66 changed files with 1,315 additions and 1,532 deletions.
@@ -1,11 +1,12 @@
import React, {useEffect, useState} from 'react';
import {createRoot} from 'react-dom/client';

import DeckGL from '@deck.gl/react';
import {FirstPersonView, COORDINATE_SYSTEM} from '@deck.gl/core';
import {SimpleMeshLayer} from '@deck.gl/mesh-layers';
import {SphereGeometry} from '@luma.gl/engine';

import type {FirstPersonViewState} from '@deck.gl/core';

// Video created by the NASA Jet Propulsion Laboratory, Public domain, via Wikimedia Commons
// Source: https://commons.wikimedia.org/wiki/File:NASA_VR-360_Astronaut_Training-_Space_Walk.webm
const VIDEO_URL =
Expand All @@ -17,7 +18,7 @@ const sphere = new SphereGeometry({
radius: 150
});

const PLAY_BUTTON_STYLE = {
const PLAY_BUTTON_STYLE: React.CSSProperties = {
width: '100%',
height: '100%',
display: 'flex',
Expand All @@ -26,7 +27,7 @@ const PLAY_BUTTON_STYLE = {
opacity: 0.5
};

const INITIAL_VIEW_STATE = {
const INITIAL_VIEW_STATE: FirstPersonViewState = {
latitude: 0,
longitude: 0,
position: [0, 0, 0],
Expand All @@ -36,7 +37,7 @@ const INITIAL_VIEW_STATE = {

export default function App() {
const [isPlaying, setPlaying] = useState(false);
const [video, setVideo] = useState(null);
const [video, setVideo] = useState<HTMLVideoElement>();

useEffect(() => {
let videoEl;
Expand Down Expand Up @@ -95,6 +96,6 @@ export default function App() {
);
}

export function renderToDOM(container) {
export function renderToDOM(container: HTMLDivElement) {
createRoot(container).render(<App />);
}
2 changes: 1 addition & 1 deletion examples/website/360-video/index.html
Expand Up @@ -12,7 +12,7 @@
<div id="app"></div>
</body>
<script type="module">
import {renderToDOM} from './app.jsx';
import {renderToDOM} from './app.tsx';
renderToDOM(document.getElementById('app'));
</script>
</html>
2 changes: 2 additions & 0 deletions examples/website/360-video/package.json
Expand Up @@ -9,6 +9,8 @@
"build": "vite build"
},
"dependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"deck.gl": "^9.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
Expand Down
8 changes: 8 additions & 0 deletions examples/website/360-video/tsconfig.json
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es2020",
"jsx": "react",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
}
}
86 changes: 0 additions & 86 deletions examples/website/collision-filter/app.jsx

This file was deleted.

102 changes: 102 additions & 0 deletions examples/website/collision-filter/app.tsx
@@ -0,0 +1,102 @@
/* global fetch */
import React, {useEffect, useMemo, useState} from 'react';
import {createRoot} from 'react-dom/client';
import {Map} from 'react-map-gl//maplibre';
import DeckGL from '@deck.gl/react';
import {GeoJsonLayer, TextLayer} from '@deck.gl/layers';
import {CollisionFilterExtension, CollisionFilterExtensionProps} from '@deck.gl/extensions';
import {calculateLabels, Label} from './calculate-labels';

import type {Position, MapViewState} from '@deck.gl/core';
import type {FeatureCollection, Geometry} from 'geojson';

const DATA_URL =
'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/collision-filter/ne_10_roads.json';

const INITIAL_VIEW_STATE: MapViewState = {
longitude: -100,
latitude: 24,
zoom: 5,
minZoom: 5,
maxZoom: 12
};

type RoadProperties = {
name: string;
scalerank: number;
};

export default function App({
mapStyle = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json',
sizeScale = 10,
collisionEnabled = true,
pointSpacing = 5
}: {
mapStyle?: string;
sizeScale?: number;
collisionEnabled?: boolean;
pointSpacing?: number
}) {
const [roads, setRoads] = useState<FeatureCollection<Geometry, RoadProperties>>();

useEffect(() => {
fetch(DATA_URL)
.then(resp => resp.json())
.then(setRoads);
}, []);

const roadLabels = useMemo(() => calculateLabels(roads, d => d.name !== null, pointSpacing), [roads, pointSpacing]);

const layers = [
new GeoJsonLayer({
id: 'roads-outline',
data: roads,
getLineWidth: f => f.properties.scalerank + 2,
lineWidthScale: 40,
lineWidthMinPixels: 3,
getLineColor: [0, 173, 230]
}),
new GeoJsonLayer({
id: 'roads',
data: roads,
getLineWidth: f => f.properties.scalerank,
lineWidthScale: 40,
lineWidthMinPixels: 1,
parameters: {depthCompare: 'always'},
getLineColor: [255, 255, 255]
}),
new TextLayer<Label<RoadProperties>, CollisionFilterExtensionProps>({
id: 'labels',
data: roadLabels,
getColor: [0, 0, 0],
getBackgroundColor: [255, 255, 255],
getBorderColor: [0, 173, 230],
getBorderWidth: 1,
getPosition: d => d.position as Position,
getText: d => d.parent.name,
getSize: d => Math.max(d.parent.scalerank * 2, 10),
getAngle: d => d.angle,
billboard: false,
background: true,
backgroundPadding: [4, 1],
characterSet: 'auto',
fontFamily: 'monospace',

// CollisionFilterExtension props
collisionEnabled,
getCollisionPriority: d => d.priority,
collisionTestProps: {sizeScale},
extensions: [new CollisionFilterExtension()]
})
];

return (
<DeckGL layers={layers} initialViewState={INITIAL_VIEW_STATE} controller={true}>
<Map reuseMaps mapStyle={mapStyle} />
</DeckGL>
);
}

export function renderToDOM(container: HTMLDivElement) {
createRoot(container).render(<App />);
}
83 changes: 83 additions & 0 deletions examples/website/collision-filter/calculate-labels.ts
@@ -0,0 +1,83 @@
import {geomEach, along, rhumbBearing, lineDistance, lineString, booleanEqual} from '@turf/turf';
import type {FeatureCollection, Geometry, Feature, LineString} from 'geojson';

export type Label<FeatureProperties> = {
position: number[];
priority: number;
angle: number;
parent: FeatureProperties;
};

/* Utility to add rotated labels along lines and assign collision priorities */
export function calculateLabels<FeatureProperties>(
geojson: FeatureCollection<Geometry, FeatureProperties> | undefined,
filter: (properties: FeatureProperties) => boolean,
pointSpacing: number
): Label<FeatureProperties>[] {
if (!geojson) return [];

const result: Label<FeatureProperties>[] = [];

function addLabelsAlongLineString(coordinates: LineString["coordinates"], properties: FeatureProperties) {
// Add labels to minimize overlaps, pick odd values from each level
// 1 <- depth 1
// 1 2 3 <- depth 2
// 1 2 3 4 5 6 7 <- depth 3
const feature = lineString(coordinates, properties);
const lineLength = Math.floor(lineDistance(feature));
let delta = lineLength / 2; // Spacing between points at level
let depth = 1;
while (delta > pointSpacing) {
for (let i = 1; i < 2 ** depth; i += 2) {
const label = getLabelAtPoint(feature, lineLength, i * delta, 100 - depth); // Top levels have highest priority
result.push(label);
}
depth++;
delta /= 2;
}
}

// @ts-ignore turf type FeatureCollection is not compatible with geojson type
geomEach(geojson, (geometry: Geometry, i, properties: FeatureProperties) => {
if (!filter(properties)) return;

switch (geometry.type) {
case 'LineString':
addLabelsAlongLineString(geometry.coordinates, properties);
break;

case 'MultiLineString':
for (const coordinates of geometry.coordinates) {
addLabelsAlongLineString(coordinates, properties);
}
break;

default:
// ignore
}
});

return result;
}

function getLabelAtPoint<FeatureProperties>(
line: Feature<LineString, FeatureProperties>,
lineLength: number,
dAlong: number,
priority: number
): Label<FeatureProperties> {
const offset = dAlong + 1 < lineLength ? 1 : -1;
const point = along(line, dAlong);
const nextPoint = along(line, dAlong + offset);
if (booleanEqual(point, nextPoint)) return;

let angle = 90 - rhumbBearing(point, nextPoint);
if (Math.abs(angle) > 90) angle += 180;

return {
position: point.geometry.coordinates,
priority,
angle,
parent: line.properties
};
}
48 changes: 0 additions & 48 deletions examples/website/collision-filter/calculateLabels.js

This file was deleted.

0 comments on commit 80ac105

Please sign in to comment.