Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 66 additions & 11 deletions website/src/components/explorer/details.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,68 @@ function cellUnits(hex) {
: { area: UNITS.m2, dist: UNITS.m };
}

function ClickableH3Index({ hex, setUserInput }) {
function ClickableH3Index({ hex, setUserInput, onHoverCells }) {
const onClick = useCallback(() => {
setUserInput(hex);
}, [hex, setUserInput]);
if (onHoverCells) {
onHoverCells([]);
}
}, [hex, setUserInput, onHoverCells]);

const onMouseEnter = useCallback(() => {
if (onHoverCells) {
onHoverCells([hex]);
}
}, [hex, onHoverCells]);
const onMouseLeave = useCallback(() => {
if (onHoverCells) {
onHoverCells([]);
}
}, [onHoverCells]);

return (
<a onClick={onClick} style={{ cursor: "pointer" }}>
<a
onClick={onClick}
style={{ cursor: "pointer" }}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{hex}
</a>
);
}

function ClickableH3IndexList({ hexes, setUserInput, showAll = true }) {
function ClickableH3IndexList({
hexes,
setUserInput,
showAll = true,
onHoverCells,
}) {
const onShowAllClick = useCallback(() => {
setUserInput(hexes.join(", "));
}, [hexes, setUserInput]);

const showAllOnMouseEnter = useCallback(() => {
if (onHoverCells) {
onHoverCells(hexes);
}
}, [hexes, onHoverCells]);
const showAllOnMouseLeave = useCallback(() => {
if (onHoverCells) {
onHoverCells([]);
}
}, [onHoverCells]);

return (
<>
{hexes.map((hex, index) => {
const link = (
<ClickableH3Index key={hex} setUserInput={setUserInput} hex={hex} />
<ClickableH3Index
key={hex}
setUserInput={setUserInput}
hex={hex}
onHoverCells={onHoverCells}
/>
);
if (index === 0) {
return link;
Expand All @@ -93,7 +134,12 @@ function ClickableH3IndexList({ hexes, setUserInput, showAll = true }) {
{showAll ? (
<>
&nbsp;
<a onClick={onShowAllClick} style={{ cursor: "pointer" }}>
<a
onClick={onShowAllClick}
style={{ cursor: "pointer" }}
onMouseEnter={showAllOnMouseEnter}
onMouseLeave={showAllOnMouseLeave}
>
(show all)
</a>
</>
Expand All @@ -110,6 +156,7 @@ export function SelectedHexDetails({
splitUserInput,
showNavigation = true,
showDetails = true,
onHoverCells,
}) {
if (splitUserInput.length === 1) {
const hex = splitUserInput[0];
Expand Down Expand Up @@ -141,19 +188,22 @@ export function SelectedHexDetails({

return (
<p style={{ marginBottom: "0" }}>
Lat./Lng.: <tt>{coords}</tt>
{showCellId ? (
<>
<br />
ID: <ClickableH3Index hex={hex} setUserInput={setUserInput} />
</>
) : null}
{showNavigation ? (
<>
<br />
<details>
<summary>Relations</summary>
Parent:{" "}
{parent ? (
<ClickableH3Index hex={parent} setUserInput={setUserInput} />
<ClickableH3Index
hex={parent}
setUserInput={setUserInput}
onHoverCells={onHoverCells}
/>
) : (
<tt>(none)</tt>
)}
Expand All @@ -163,6 +213,7 @@ export function SelectedHexDetails({
<ClickableH3IndexList
hexes={children}
setUserInput={setUserInput}
onHoverCells={onHoverCells}
/>
) : (
<tt>(none)</tt>
Expand All @@ -172,15 +223,18 @@ export function SelectedHexDetails({
<ClickableH3IndexList
hexes={neighbors}
setUserInput={setUserInput}
onHoverCells={onHoverCells}
/>
<br />
</>
</details>
) : (
<></>
)}
{showDetails ? (
<details>
<summary>Details</summary>
Lat./Lng.: <tt>{coords}</tt>
<br />
Resolution: <tt>{res}</tt>
<br />
Base cell: <tt>{baseCell}</tt>
Expand Down Expand Up @@ -208,6 +262,7 @@ export function SelectedHexDetails({
hexes={splitUserInput}
setUserInput={setUserInput}
showAll={false}
onHoverCells={onHoverCells}
/>
</p>
);
Expand Down
21 changes: 16 additions & 5 deletions website/src/components/explorer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Contains code adapted from https://observablehq.com/@nrabinowitz/h3-index-inspector under the ISC license

import React, { useCallback, useMemo, ReactNode } from "react";
import React, { useCallback, useMemo, ReactNode, useState, useId } from "react";
import { isValidCell, latLngToCell, getResolution } from "h3-js";
import {
Banner,
Expand All @@ -15,6 +15,7 @@ import { WhereAmIButton } from "./where-am-i";
import geojson2h3 from "geojson2h3";
import wkt from "wkt";
import { Feature, MultiPolygon, Polygon } from "geojson";
import { useColorMode } from "@docusaurus/theme-common";

const CELL_COUNT_THRESHOLD = 50;
const CELL_COUNT_UPPER_THRESHOLD = 5000;
Expand Down Expand Up @@ -217,6 +218,9 @@ function zoomToResolution(zoom: number) {
export default function HomeExporer({ children }: { children: ReactNode }) {
const [userInput, setUserInput] = useQueryState("hex", "");
const [userResolution, setUserResolution] = useQueryState<number>("res", -1);
const [previewCells, setPreviewCells] = useState<string[]>([]);
const { colorMode } = useColorMode();
const resolutionInputId = useId();

const { splitUserInput, showCellId, inputGeoJson, showResolutionInput } =
useMemo(
Expand Down Expand Up @@ -285,10 +289,11 @@ export default function HomeExporer({ children }: { children: ReactNode }) {
userValidHex={userValidHex}
objectOnClick={objectOnClick}
coordinateOnClick={coordinateOnClick}
previewCells={previewCells}
/>
</DemoContainer>
</HeroExampleContainer>
<BannerContainer>
<BannerContainer colorMode={colorMode}>
<textarea
value={userInput}
onChange={(e) => {
Expand All @@ -309,18 +314,21 @@ export default function HomeExporer({ children }: { children: ReactNode }) {
splitUserInput={splitUserInput}
showCellId={showCellId}
setUserInput={setUserInput}
showNavigation={false}
showNavigation={true}
showDetails={true}
onHoverCells={setPreviewCells}
/>
) : null}
{showResolutionInput !== null ? (
<div>
<label htmlFor={resolutionInputId}>Resolution:</label>
<input
id={resolutionInputId}
type="number"
min="0"
max="15"
placeholder="Resolution"
value={`${userResolution}`}
placeholder="Auto"
value={`${userResolution !== -1 ? userResolution : ""}`}
onChange={(e) => {
try {
const res = parseInt(e.target.value, 10);
Expand All @@ -332,6 +340,9 @@ export default function HomeExporer({ children }: { children: ReactNode }) {
console.error(err);
}
}}
style={{
marginLeft: "0.25em",
}}
/>
</div>
) : null}
Expand Down
76 changes: 60 additions & 16 deletions website/src/components/explorer/map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { MOBILE_CUTOFF_WINDOW_WIDTH } from "../common";
import { useHex } from "./useHex";
import { GeoJsonLayer } from "deck.gl";
import { PathStyleExtension } from "@deck.gl/extensions";
import { useColorMode } from "@docusaurus/theme-common";

const INITIAL_VIEW_STATE = {
longitude: -74.012,
Expand All @@ -28,7 +29,8 @@ const INITIAL_VIEW_STATE = {
minZoom: 0,
};

const MAP_STYLE = "mapbox://styles/mapbox/light-v11";
const LIGHT_MAP_STYLE = "mapbox://styles/mapbox/light-v11";
const DARK_MAP_STYLE = "mapbox://styles/mapbox/dark-v11";

/**
*
Expand All @@ -37,9 +39,9 @@ const MAP_STYLE = "mapbox://styles/mapbox/light-v11";
* @param {object | null} opts.inputGeoJson
* @param {boolean} opts.userValidHex
* @param {Object} [opts.initialViewState]
* @param {string} [opts.mapStyle]
* @param {({ hex: string }) => void | undefined} [opts.objectOnClick]
* @param {({ coordinate: [number, number], zoom: number, resolution: number }) => void | undefined} [opts.coordinateOnClick]
* @param {string[] | undefined} [opts.previewCells]
* @returns
*/
export function ExplorerMap(opts) {
Expand All @@ -48,16 +50,17 @@ export function ExplorerMap(opts) {
inputGeoJson = null,
userValidHex = false,
initialViewState = INITIAL_VIEW_STATE,
mapStyle = MAP_STYLE,
objectOnClick = undefined,
coordinateOnClick = undefined,
previewCells = undefined,
} = opts;
const context = useDocusaurusContext();
const [currentInitialViewState, setCurrentInitialViewState] =
useState(initialViewState);
const [deckLoaded, setDeckLoaded] = useState(false);
const deckRef = useRef();
const [windowWidth, setWindowWidth] = useState(null);
const { colorMode } = useColorMode();

useEffect(() => {
const updateWidth = () => {
Expand Down Expand Up @@ -144,8 +147,9 @@ export function ExplorerMap(opts) {
new GeoJsonLayer({
id: "userinput",
data: inputGeoJson,
getFillColor: [0, 0, 0],
getLineColor: [100, 100, 100],
getFillColor: colorMode === "dark" ? [250, 250, 250] : [0, 0, 0],
getLineColor:
colorMode === "dark" ? [220, 220, 220] : [100, 100, 100],
getLineWidth: 1,
lineWidthMinPixels: 1,
lineWidthUnits: "pixels",
Expand All @@ -160,6 +164,31 @@ export function ExplorerMap(opts) {
}),
]
: [];
const inputPreviewLayers = previewCells
? [
new H3HexagonLayer({
id: "previewhex",
data: previewCells.map((hex) => ({ hex })),
getHexagon: (d) => d.hex,
extruded: false,
filled: false,
stroked: true,
getLineColor:
colorMode === "dark" ? [140, 140, 140] : [120, 120, 120],
getLineWidth: 2,
lineWidthUnits: "pixels",
lineWidthMinPixels: 1,
highPrecision: true,
pickable: false,
filled: false,
// @ts-expect-error
getDashArray: [5, 5],
dashJustified: true,
dashGapPickable: true,
extensions: [new PathStyleExtension({ dash: true })],
}),
]
: [];

const layers = userValidHex
? [
Expand All @@ -170,26 +199,41 @@ export function ExplorerMap(opts) {
extruded: false,
filled: false,
stroked: true,
getLineColor: [0, 0, 0],
getLineColor: colorMode === "dark" ? [255, 255, 255] : [0, 0, 0],
getLineWidth: 2,
lineWidthUnits: "pixels",
lineWidthMinPixels: 2,
highPrecision: true,
pickable: true,
filled: true,
getFillColor: [0, 0, 0, 30],
getFillColor:
colorMode === "dark" ? [255, 255, 255, 30] : [0, 0, 0, 30],
}),
...inputPreviewLayers,
...inputGeoJsonLayers,
]
: backgroundHexLayers;

const getTooltip = useCallback(({ object }) => {
if (object && object.hex) {
return {
html: `<tt>${object.hex}</tt>`,
};
}
}, []);
const getTooltip = useCallback(
({ object }) => {
if (object && object.hex) {
return {
html: `<tt>${object.hex}</tt>`,
style:
colorMode === "dark"
? {
backgroundColor: "black",
color: "white",
}
: {
backgroundColor: "white",
color: "black",
},
};
}
},
[colorMode],
);

const getCursor = useCallback(({ isHovering }) => {
if (isHovering) {
Expand All @@ -205,7 +249,7 @@ export function ExplorerMap(opts) {
if (objectOnClick) {
objectOnClick({ hex: object.hex });
}
} else if (object && object instanceof string) {
} else if (object && typeof object === "string") {
if (objectOnClick) {
objectOnClick({ hex: object });
}
Expand Down Expand Up @@ -242,7 +286,7 @@ export function ExplorerMap(opts) {
interactive={false}
projection={"mercator"}
mapboxAccessToken={context.siteConfig.customFields.mapboxAccessToken}
mapStyle={mapStyle}
mapStyle={colorMode === "dark" ? DARK_MAP_STYLE : LIGHT_MAP_STYLE}
/>
</DeckGL>
);
Expand Down
Loading
Loading