Skip to content

Commit

Permalink
chore: training area map bug fixe
Browse files Browse the repository at this point in the history
  • Loading branch information
jeafreezy committed Nov 7, 2024
1 parent 3929229 commit dc803d4
Show file tree
Hide file tree
Showing 25 changed files with 560 additions and 229 deletions.
17 changes: 15 additions & 2 deletions frontend/src/app/providers/map-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
import { createContext, useContext, ReactNode, useState, useMemo } from "react";
import { Map } from "maplibre-gl";
import { TerraDraw } from "terra-draw";
import { setupTerraDraw } from "@/components/map/setup-terra-draw";

const MapContext = createContext<{
map: Map | null;
setMap: React.Dispatch<React.SetStateAction<Map | null>>;
terraDraw: TerraDraw | undefined;
}>({
map: null,
setMap: () => {},
terraDraw: undefined,
});

export const MapProvider = ({ children }: { children: ReactNode }) => {
const [map, setMap] = useState<Map | null>(null);
const memoizedValues = useMemo(() => ({ map, setMap }), [map, setMap]);
const terraDraw = useMemo(() => {
if (map) {
const terraDraw = setupTerraDraw(map);
terraDraw.start();
return terraDraw;
}
}, [map]);

return (
<MapContext.Provider value={memoizedValues}>{children}</MapContext.Provider>
<MapContext.Provider value={{ map, setMap, terraDraw }}>
{children}
</MapContext.Provider>
);
};

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/app/providers/model-creation-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export enum MODEL_CREATION_FORM_NAME {
TMS_URL_VALIDITY = "tmsURLValidation",
SELECTED_TRAINING_DATASET_ID = "selectedTrainingDatasetId",
TRAINING_AREAS = "trainingAreas",
DATASET_TIME_NAME = "datasetTileName"
DATASET_TIME_NAME = "datasetTileName",
}

export const FORM_VALIDATION_CONFIG = {
Expand Down Expand Up @@ -134,8 +134,8 @@ const ModelCreationFormContext = createContext<{
>;
}>({
formData: initialFormState,
setFormData: () => { },
handleChange: () => { },
setFormData: () => {},
handleChange: () => {},
createNewTrainingDatasetMutation: {} as UseMutationResult<
TTrainingDataset,
Error,
Expand Down
18 changes: 5 additions & 13 deletions frontend/src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,26 +174,18 @@ const router = createBrowserRouter([
"@/app/routes/account/settings"
);
return {
Component: () => (
<ProtectedPage>
<UserAccountSettingsPage />
</ProtectedPage>
),
Component: () => <UserAccountSettingsPage />,
};
},
},
{
path: APPLICATION_ROUTES.ACCOUNT_PROJECTS,
path: APPLICATION_ROUTES.ACCOUNT_MODELS,
lazy: async () => {
const { UserAccountProjectsPage } = await import(
"@/app/routes/account/projects"
const { UserAccountModelsPage } = await import(
"@/app/routes/account/models"
);
return {
Component: () => (
<ProtectedPage>
<UserAccountProjectsPage />
</ProtectedPage>
),
Component: () => <UserAccountModelsPage />,
};
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PageUnderConstruction } from "@/components/errors";

export const UserAccountProjectsPage = () => {
export const UserAccountModelsPage = () => {
return <PageUnderConstruction />;
};
82 changes: 72 additions & 10 deletions frontend/src/components/map/draw-control.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,78 @@
import { Map } from "maplibre-gl";
import { DrawingModes, ToolTipPlacement } from "@/enums";
import { useCallback, useEffect, useState } from "react";
import { TerraDraw } from "terra-draw";
import { ToolTip } from "../ui/tooltip";
import { PenIcon } from "../ui/icons";

// @ts-expect-error bad type definition
const DrawControl = ({ map }: { map: Map | null }) => {
const handleDraw = () => {};
const DrawControl = ({ terraDraw }: { terraDraw: TerraDraw }) => {
const [mode, setMode] = useState<DrawingModes>(DrawingModes.STATIC);
const [featuresExist, setFeaturesExist] = useState(false);

const changeMode = useCallback(
(newMode: DrawingModes) => {
setMode(newMode);
terraDraw.setMode(newMode);
},
[terraDraw],
);

useEffect(() => {
const handleFeatureChange = () => {
setFeaturesExist(terraDraw.getSnapshot().length > 0);
};

terraDraw.on("change", handleFeatureChange);
return () => {
terraDraw.off("change", handleFeatureChange);
};
}, [terraDraw]);

const renderButton = (
currentMode: DrawingModes,
activeMode: DrawingModes,
label: string,
isActive: boolean,
) => (
<ToolTip content={label} placement={ToolTipPlacement.RIGHT}>
<button
className={`p-2 ${currentMode === activeMode ? "bg-primary" : "bg-white"} flex items-center justify-center`}
onClick={() =>
changeMode(
currentMode === activeMode ? DrawingModes.STATIC : activeMode,
)
}
disabled={activeMode === DrawingModes.SELECT && !featuresExist}
>
{activeMode === DrawingModes.SELECT ? (
<PenIcon
className={`icon ${isActive ? "text-white " : "text-dark"}`}
/>
) : (
<div
className={`border-[2px] ${isActive ? "border-white" : "border-dark"} rounded-md h-4 w-4`}
></div>
)}
</button>
</ToolTip>
);

return (
<button
className="p-1.5 bg-primary flex items-center justify-center"
onClick={handleDraw}
>
<div className="border-2 border-white rounded-md h-4 w-4"></div>
</button>
<>
{renderButton(
mode,
DrawingModes.RECTANGLE,
mode === DrawingModes.STATIC ? "Draw AOI" : "Stop Drawing",
mode === DrawingModes.RECTANGLE,
)}
{/* {renderButton(
mode,
DrawingModes.SELECT,
mode === DrawingModes.STATIC || mode === DrawingModes.RECTANGLE
? "Edit AOI"
: "Stop Editing",
mode === DrawingModes.SELECT,
)} */}
</>
);
};

Expand Down
76 changes: 40 additions & 36 deletions frontend/src/components/map/layer-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useDropdownMenu } from "@/hooks/use-dropdown-menu";
import { Map } from "maplibre-gl";
import { useEffect, useState } from "react";
import { CheckboxGroup } from "../ui/form";
import { ToolTip } from "../ui/tooltip";
import { ToolTipPlacement } from "@/enums";

type TLayer = { id?: string; mapLayerId?: string; value: string }[];

Expand Down Expand Up @@ -83,43 +85,45 @@ const LayerControl = ({
}, [map, layers, layerVisibility]);

return (
<DropDown
dropdownIsOpened={dropdownIsOpened}
onDropdownHide={onDropdownHide}
onDropdownShow={onDropdownShow}
disableCheveronIcon
triggerComponent={
<div className="bg-white p-2 relative">
<LayerStackIcon className="icon-lg" />
<ToolTip content="Layer Control" placement={ToolTipPlacement.BOTTOM}>
<DropDown
dropdownIsOpened={dropdownIsOpened}
onDropdownHide={onDropdownHide}
onDropdownShow={onDropdownShow}
disableCheveronIcon
triggerComponent={
<div className="bg-white p-2 relative">
<LayerStackIcon className="icon-lg" />
</div>
}
withCheckbox
distance={10}
>
<div className="bg-white px-4 py-2 text-nowrap rounded-md w-full flex flex-col gap-y-4">
<p className="text-sm">Basemap</p>
<CheckboxGroup
defaultSelectedOption={selectedBasemap}
options={[{ value: "OSM" }, { value: "Satellite" }]}
// @ts-expect-error bad type definition
onCheck={(basemap) => setSelectedBasemap(basemap)}
/>
{layers.length > 0 && (
<>
<p className="text-sm">Layers</p>
<CheckboxGroup
defaultSelectedOption={Object.keys(layerVisibility).filter(
(layer) => layerVisibility[layer],
)}
multiple
options={layers}
// @ts-expect-error bad type definition
onCheck={handleLayerSelection}
/>
</>
)}
</div>
}
withCheckbox
distance={10}
>
<div className="bg-white px-4 py-2 text-nowrap rounded-md w-full flex flex-col gap-y-4">
<p className="text-sm">Basemap</p>
<CheckboxGroup
defaultSelectedOption={selectedBasemap}
options={[{ value: "OSM" }, { value: "Satellite" }]}
// @ts-expect-error bad type definition
onCheck={(basemap) => setSelectedBasemap(basemap)}
/>
{layers.length > 0 && (
<>
<p className="text-sm">Layers</p>
<CheckboxGroup
defaultSelectedOption={Object.keys(layerVisibility).filter(
(layer) => layerVisibility[layer],
)}
multiple
options={layers}
// @ts-expect-error bad type definition
onCheck={handleLayerSelection}
/>
</>
)}
</div>
</DropDown>
</DropDown>
</ToolTip>
);
};

Expand Down
48 changes: 15 additions & 33 deletions frontend/src/components/map/map.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { MAP_STYLES } from "@/config";
import ZoomControls from "./zoom-controls";
Expand All @@ -8,6 +7,7 @@ import DrawControl from "./draw-control";
import ZoomLevel from "./zoom-level";
import LayerControl from "./layer-control";
import { useMap } from "@/app/providers/map-provider";
import { setupMaplibreMap } from "./setup-maplibre";

type MapComponentProps = {
geolocationControl?: boolean;
Expand All @@ -31,59 +31,41 @@ const MapComponent: React.FC<MapComponentProps> = ({
}) => {
const mapContainerRef = useRef(null);
const [selectedBasemap, setSelectedBasemap] = useState<string>("OSM");
const { map, setMap } = useMap();
const [mapIsReady, setMapIsReady] = useState<boolean>(false);
const { map, setMap, terraDraw } = useMap();

useEffect(() => {
if (!mapContainerRef.current) return;

const map = new maplibregl.Map({
container: mapContainerRef.current,
//@ts-ignore
style: MAP_STYLES[selectedBasemap],
center: [0, 0],
zoom: 0.5,
minZoom: 1,
maxZoom: 21,
const maplibreMap = setupMaplibreMap(
mapContainerRef,
MAP_STYLES[selectedBasemap],
);
maplibreMap.on("load", () => {
setMap(maplibreMap);
});

const onStyleLoad = () => {
setMap(map);
setMapIsReady(true);
};

map.on("style.load", () => {
onStyleLoad();
});

return () => {
map.off("style.load", onStyleLoad);
map.remove();
};
}, []);

// Update the map style whenever the selected basemap changes
useEffect(() => {
if (!mapIsReady) return;
map?.setStyle(MAP_STYLES[selectedBasemap]);
}, [selectedBasemap]);
if (!map) return;
map.setStyle(MAP_STYLES[selectedBasemap]);
}, [selectedBasemap, map]);

return (
<div className="h-full w-full relative" ref={mapContainerRef}>
<div
className={`absolute top-5 ${controlsLocation === "top-right" ? "right-3" : "left-3"} z-[10] flex flex-col gap-y-[1px]`}
>
{mapIsReady && (
{map && (
<>
<ZoomControls map={map} />
{geolocationControl && <GeolocationControl map={map} />}
{drawControl && <DrawControl map={map} />}
{drawControl && terraDraw && <DrawControl terraDraw={terraDraw} />}
</>
)}
</div>
<div
className={`absolute top-5 right-10 z-[10] items-center flex gap-x-4`}
>
{mapIsReady && (
{map && (
<>
{showCurrentZoom && <ZoomLevel map={map} />}
{layerControl && (
Expand Down
23 changes: 23 additions & 0 deletions frontend/src/components/map/setup-maplibre.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import maplibregl, { Map, StyleSpecification } from "maplibre-gl";

export function setupMaplibreMap(
containerRef: React.RefObject<HTMLElement>,
style: StyleSpecification | string,
): Map {
// Check if RTL plugin is needed and set it
if (maplibregl.getRTLTextPluginStatus() === "unavailable") {
maplibregl.setRTLTextPlugin(
"https://unpkg.com/@mapbox/[email protected]/mapbox-gl-rtl-text.min.js",
true,
);
}

return new maplibregl.Map({
container: containerRef.current!,
style: style,
center: [0, 0],
zoom: 0.5,
minZoom: 1,
maxZoom: 21,
});
}
Loading

0 comments on commit dc803d4

Please sign in to comment.