Skip to content

Commit

Permalink
Merge pull request #13 from wri/highlight-confirm-location
Browse files Browse the repository at this point in the history
highlights map layers
  • Loading branch information
oliverroick authored Dec 19, 2024
2 parents 119c4ac + fb3fd66 commit dbebb0f
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
MOCK_QUERIES=true
VITE_MOCK_QUERIES=true
4 changes: 3 additions & 1 deletion src/atoms.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { atom } from "jotai";
import { v4 as uuidv4 } from "uuid";

export const mapLayersAtom = atom([]);
export const highlightedLayerAtom = atom();
export const confirmedLocationAtom = atom();
export const chatHistoryAtom = atom([]);
export const sessionIdAtom = atom(uuidv4());
export const isLoadingAtom = atom(false);
Expand All @@ -23,7 +25,7 @@ export const addPrompt = atom(null, (get, set, promt) => {
}

let queryUrl = "https://api.zeno.ds.io/stream";
if (import.meta.env.MOCK_QUERIES === "true") {
if (import.meta.env.VITE_MOCK_QUERIES === "true") {
queryUrl = "/stream";
}

Expand Down
1 change: 0 additions & 1 deletion src/components/ChatOutput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { chatHistoryAtom, isLoadingAtom } from "../atoms";
function ChatOutput() {
const [ chatHistory ] = useAtom(chatHistoryAtom);
const [ isLoading ] = useAtom(isLoadingAtom);
console.log(chatHistory);
const containerRef = useRef();

useEffect(() => {
Expand Down
129 changes: 87 additions & 42 deletions src/components/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,90 @@ import MapGl, {
Source,
AttributionControl,
} from "react-map-gl/maplibre";
import { useEffect } from "react";
import { config } from "../theme";
import { useEffect, useState, useRef } from "react";
import bbox from "@turf/bbox";
import { mapLayersAtom } from "../atoms";
import { mapLayersAtom, highlightedLayerAtom, confirmedLocationAtom } from "../atoms";
import { useAtomValue } from "jotai";
import { useRef } from "react";

/**
* Map component
* Children are the layers to render on the map
*/
function Map() {
const pink500 = config.theme.tokens.colors.pink["500"];
const blue500 = config.theme.tokens.colors.blue["500"];
const [currentFeatures, setFeatures] = useState([]);
const mapRef = useRef();

const mapLayers = useAtomValue(mapLayersAtom);
const highlightedLayer = useAtomValue(highlightedLayerAtom);
const confirmedLocation = useAtomValue(confirmedLocationAtom);

// if there are layers, calculate the bounds
// each layer is a feature collection, so we need to calculate the bounds of all features
useEffect(() => {
if (mapLayers.length > 0) {
const bounds = mapLayers.reduce(
(acc, layer) => {
const layerBounds = bbox(layer);
return [
Math.min(acc[0], layerBounds[0]),
Math.min(acc[1], layerBounds[1]),
Math.max(acc[2], layerBounds[2]),
Math.max(acc[3], layerBounds[3]),
];
},
[Infinity, Infinity, -Infinity, -Infinity]
);
if (!confirmedLocation) {
const bounds = mapLayers.reduce(
(acc, layer) => {
const layerBounds = bbox(layer);
return [
Math.min(acc[0], layerBounds[0]),
Math.min(acc[1], layerBounds[1]),
Math.max(acc[2], layerBounds[2]),
Math.max(acc[3], layerBounds[3]),
];
},
[Infinity, Infinity, -Infinity, -Infinity]
);

mapRef.current.fitBounds(
[
[bounds[0], bounds[1]],
[bounds[2], bounds[3]],
],
{
padding: 100,
}
);
mapRef.current.fitBounds(
[
[bounds[0], bounds[1]],
[bounds[2], bounds[3]],
],
{
padding: 100,
}
);
} else {
// If the location is confirmed, fit to the bounds of the confirmed location
const bounds = bbox(confirmedLocation);
mapRef.current.fitBounds(
[
[bounds[0], bounds[1]],
[bounds[2], bounds[3]],
],
{
padding: 100,
}
);
}
}
}, [mapLayers]);

mapLayers.forEach(layer => layer.features.forEach(
((feature) => {
if (feature.id === highlightedLayer) {
// add highlight layer to attributes
feature.properties = {
...feature.properties,
"fill-opacity": 0.5,
"fill-color": pink500,
"line-color": pink500,
};
} else {
// reset the layer to default
feature.properties = {
...feature.properties,
"fill-opacity": 0.25,
"fill-color": blue500,
"line-color": blue500,
};
}
})));
setFeatures(mapLayers.reduce((acc, layer) => [...acc, ...layer.features], []));
}, [mapLayers, highlightedLayer, confirmedLocation, pink500, blue500]);

return (
<MapGl
Expand All @@ -66,23 +107,27 @@ function Map() {
>
<Layer id="background-tiles" type="raster" />
</Source>
{mapLayers.map((layer, idx) => {
const layerId = layer?.features[0]?.id || idx;
return (
<Source id={layerId} type="geojson" data={layer} key={layerId}>
<Layer
id={`fill-layer-${layerId}`}
type="fill"
paint={{ "fill-color": "#1857e0", "fill-opacity": 0.25 }}
/>
<Layer
id={`line-layer-${layerId}`}
type="line"
paint={{ "line-color": "#1857e0", "line-width": 2 }}
/>
</Source>
);
})}
{currentFeatures?.map((feature, idx) => {
const layerId = feature.id || idx;
const fillColor = feature.properties["fill-color"] || blue500;
const lineColor = feature.properties["line-color"] || blue500;
const fillOpacity = feature.properties["fill-opacity"] || 0.25;

return (
<Source id={layerId} type="geojson" data={feature} key={layerId}>
<Layer
id={`fill-layer-${layerId}`}
type="fill"
paint={{ "fill-color": fillColor , "fill-opacity": fillOpacity }}
/>
<Layer
id={`line-layer-${layerId}`}
type="line"
paint={{ "line-color": lineColor, "line-width": 2 }}
/>
</Source>
);
})};
<AttributionControl customAttribution="Background tiles: © <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap contributors</a>" />
</MapGl>
);
Expand Down
12 changes: 10 additions & 2 deletions src/components/MessageOut/HumanInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import T from "prop-types";
import { Button, List } from "@chakra-ui/react";
import { useSetAtom } from "jotai";
import { addPrompt } from "../../atoms";
import { addPrompt, confirmedLocationAtom, highlightedLayerAtom } from "../../atoms";
import MessageOutWrapper from "./wrapper";

/**
Expand All @@ -15,6 +15,8 @@ import MessageOutWrapper from "./wrapper";
*/
function HumanInput({ message, options, artifact }) {
const submit = useSetAtom(addPrompt);
const confirmLocation = useSetAtom(confirmedLocationAtom);
const setHighlightedLayer = useSetAtom(highlightedLayerAtom);

return (
<MessageOutWrapper>
Expand All @@ -31,7 +33,13 @@ function HumanInput({ message, options, artifact }) {
type="button"
colorPalette="blue"
borderRadius="full"
onClick={() => submit({ query: `${index}`, queryType: "human_input" })}
onClick={() => {
submit({ query: `${index}`, queryType: "human_input" });
confirmLocation(feature);
}}
onMouseEnter={() => setHighlightedLayer(feature.id)}
onMouseLeave={() => setHighlightedLayer(null)}
_hover={{ bg: "pink.500" }}
>
{feature.properties.name}
</Button>
Expand Down
3 changes: 2 additions & 1 deletion src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createRoot } from "react-dom/client";
import App from "./App";
import "@fontsource/ibm-plex-sans/index.css";

const isMock = import.meta.env.MOCK_QUERIES === "true";
const isMock = import.meta.env.VITE_MOCK_QUERIES === "true";

async function deferRender() {
if (isMock) {
Expand All @@ -12,6 +12,7 @@ async function deferRender() {
} else {
return Promise.resolve();
}

}

deferRender().then(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/theme/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react";
import globalCss from "./globalCss";

const config = defineConfig({
export const config = defineConfig({
globalCss,
theme: {
tokens: {
Expand Down

0 comments on commit dbebb0f

Please sign in to comment.