Skip to content

Commit

Permalink
add cell click interactivity [#7]
Browse files Browse the repository at this point in the history
  • Loading branch information
bdon committed Aug 29, 2024
1 parent 3114389 commit cb2582e
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 28 deletions.
28 changes: 28 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,35 @@ h1 {
align-self: center;
}

.draw {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
}

button.active {
background-color: darkslategray;
color: white;
}

@media (prefers-color-scheme: dark) {
button.active {
background-color: yellow;
color: black;
}

.maplibregl-popup-content {
background: #242424;
}

.maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
border-top-color: #242424;
}

.maplibregl-popup-anchor-top .maplibregl-popup-tip {
border-bottom-color: #242424;
}
}

@media screen and (max-width: 768px) {
.container {
Expand Down
114 changes: 86 additions & 28 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSignal, createEffect, onMount } from "solid-js";
import "maplibre-gl/dist/maplibre-gl.css";
import "./App.css";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { r1, s2, s1 } from "s2js";
import { greatCircle } from "@turf/great-circle";
import { flatten } from "@turf/flatten";
Expand All @@ -10,9 +10,8 @@ import {
TerraDraw,
TerraDrawMapLibreGLAdapter,
TerraDrawRectangleMode,
TerraDrawAngledRectangleMode,
TerraDrawPolygonMode,
TerraDrawCircleMode,
TerraDrawRenderMode,
} from "terra-draw";
import {
Feature,
Expand Down Expand Up @@ -227,15 +226,20 @@ const getCellVisualization = (union: s2.CellUnion): FeatureCollection => {
.map((f) => f.geometry.coordinates)
.flat(1);

const center = s2.LatLng.fromPoint(cell.center());

return {
type: "Feature",
id: cell.id.toString(),
geometry: {
type: "Polygon",
coordinates: [coordinates],
},
properties: {
level: cell.level,
token: s2.cellid.toToken(cell.id),
centerLng: s1.angle.degrees(center.lng),
centerLat: s1.angle.degrees(center.lat),
},
};
});
Expand All @@ -252,6 +256,7 @@ function App() {

const [maxLevel, setMaxLevel] = createSignal(30);
const [maxCells, setMaxCells] = createSignal(200);
const [drawMode, setDrawMode] = createSignal("");

const updateCovering = () => {
const snapshot = draw!.getSnapshot();
Expand All @@ -264,12 +269,10 @@ function App() {
let covering;
switch (snapshot[0].properties.mode) {
case "rectangle": {
console.info("rectangle covering");
covering = getCovering(regionCoverer, polygons, rectBuilder);
break;
}
default: {
console.info("polygon covering");
covering = getCovering(regionCoverer, polygons, polygonBuilder);
}
}
Expand All @@ -293,6 +296,13 @@ function App() {
updateCovering();
};

const startDrawMode = (mode: string) => {
if (draw) {
draw.setMode(mode);
setDrawMode(mode);
}
};

onMount(() => {
let basemapTheme = "white";
let cellColor = "darkslategray";
Expand Down Expand Up @@ -320,18 +330,19 @@ function App() {
adapter: new TerraDrawMapLibreGLAdapter({ map, maplibregl }),
modes: [
new TerraDrawRectangleMode(options),
new TerraDrawAngledRectangleMode(options),
new TerraDrawRenderMode({ modeName: "render", styles: {} }),
new TerraDrawPolygonMode(options),
new TerraDrawCircleMode(options),
],
});

draw.on("finish", () => {
startDrawMode("render");
updateCovering();
});

draw.start();
draw.setMode("rectangle");

startDrawMode("rectangle");

map.on("load", () => {
map.addSource("covering", {
Expand All @@ -347,7 +358,12 @@ function App() {
source: "covering",
paint: {
"fill-color": cellColor,
"fill-opacity": 0.5,
"fill-opacity": [
"case",
["boolean", ["feature-state", "hover"], false],
0.7,
0.5,
],
},
});
map.addLayer({
Expand All @@ -373,6 +389,52 @@ function App() {
},
});

let hoveredCellId:string | number | undefined;

map.on("mousemove", "covering-fill", (e) => {
if (e.features && e.features.length > 0) {
if (hoveredCellId) {
map.setFeatureState(
{ source: "covering", id: hoveredCellId },
{ hover: false },
);
}
hoveredCellId = e.features[0].id;
map.setFeatureState(
{ source: "covering", id: hoveredCellId },
{ hover: true },
);
}
map.getCanvas().style.cursor = "pointer";
});

map.on("mouseleave", "covering-fill", () => {
map.getCanvas().style.cursor = "";
});

map.on("click", "covering-fill", (e) => {
if (!e || !e.features || e.features.length === 0) return;
const properties = e.features[0].properties;
const coordinates = {
lng: properties.centerLng,
lat: properties.centerLat,
};

while (Math.abs(e.lngLat.lng - coordinates.lng) > 180) {
coordinates.lng += e.lngLat.lng > coordinates.lng ? 360 : -360;
}

new maplibregl.Popup({ closeButton: false })
.setLngLat(coordinates)
.setHTML(
`<div>
<div>Level ${properties.level}</div>
<div>${properties.token}</div>
</div>`,
)
.addTo(map);
});

// initialize the view with a predefined union
const init = initialUnion();
if (init && init.length) {
Expand All @@ -387,24 +449,21 @@ function App() {
<div class="container">
<div class="sidebar">
<div class="controls">
<div class="input">
<div class="label">
<label>draw mode:</label>
</div>
<select
onChange={(e) => {
if (draw) {
draw.setMode(e.target.value);
}
}}
<div class="draw">
<button
class={drawMode() === "rectangle" ? "active" : ""}
onClick={() => startDrawMode("rectangle")}
>
<option value="rectangle">rectangle mode</option>
<option value="polygon">polygon mode</option>
<option value="angled-rectangle">angled rectangle mode</option>
<option value="circle">circle mode</option>
</select>
Draw Rectangle
</button>
<button
class={drawMode() === "polygon" ? "active" : ""}
onClick={() => startDrawMode("polygon")}
>
Draw Polygon
</button>
<button onClick={clear}>Clear</button>
</div>

<div class="input">
<div class="label">
<label>max cells per shape:</label>
Expand All @@ -429,7 +488,6 @@ function App() {
}}
/>
</div>
<button onClick={clear}>clear</button>
</div>
<div class="text">
<h1>s2js Demo</h1>
Expand Down Expand Up @@ -460,8 +518,8 @@ function App() {
tiles.
</p>
<p class="faq">
<strong>Why don't the cells seem to cover my region?</strong> The library
interprets edges in the input as geodesics; this can be
<strong>Why don't the cells seem to cover my region?</strong> The
library interprets edges in the input as geodesics; this can be
mitigated by shorter distances between boundary vertices.
</p>
<a href="https://github.com/bdon/s2js-demos">Fork me on GitHub</a>
Expand Down

0 comments on commit cb2582e

Please sign in to comment.