Skip to content

Commit

Permalink
Merge pull request #223 from aodn/feature/6041-detail-map-layer-button
Browse files Browse the repository at this point in the history
Feature/6041 detail map layer button
  • Loading branch information
utas-raymondng authored Nov 20, 2024
2 parents 02ff160 + 2c9f3e1 commit 30eb0b5
Show file tree
Hide file tree
Showing 10 changed files with 434 additions and 73 deletions.
7 changes: 2 additions & 5 deletions src/components/common/store/searchReducer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ const fetchResultByUuidNoStore = createAsyncThunk<
.catch(errorHandling(thunkApi))
);

const fetchDatasetByUuid = createAsyncThunk<
const fetchFeaturesByUuid = createAsyncThunk<
FeatureCollection<Point>,
string,
{ rejectValue: ErrorResponse }
Expand Down Expand Up @@ -404,11 +404,8 @@ export {
fetchResultNoStore,
fetchResultAppendStore,
fetchResultByUuidNoStore,
fetchDatasetByUuid,
fetchFeaturesByUuid,
fetchParameterVocabsWithStore,
};

export default searcher.reducer;
function createErrorRespons(): any {
throw new Error("Function not implemented.");
}
42 changes: 42 additions & 0 deletions src/components/icon/AddIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SvgIcon } from "@mui/material";

const AddIcon = () => {
return (
<SvgIcon
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="4"
y="4"
width="16"
height="16"
rx="2"
stroke="currentColor"
strokeWidth="2"
fill="none"
/>
<line
x1="12"
y1="8"
x2="12"
y2="16"
stroke="currentColor"
strokeWidth="2"
/>
<line
x1="8"
y1="12"
x2="16"
y2="12"
stroke="currentColor"
strokeWidth="2"
/>
</SvgIcon>
);
};

export default AddIcon;
27 changes: 27 additions & 0 deletions src/components/icon/BBoxIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { SvgIcon } from "@mui/material";

const BBoxIcon = () => {
return (
<SvgIcon
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="4"
y="4"
width="16"
height="16"
rx="2"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeDasharray="4,2"
/>
</SvgIcon>
);
};

export default BBoxIcon;
23 changes: 23 additions & 0 deletions src/components/icon/XIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { SvgIcon } from "@mui/material";
import React from "react";

interface XIconProps {
color?: string;
}

const XIcon: React.FC<XIconProps> = ({ color = "red" }) => {
return (
<SvgIcon
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<line x1="6" y1="6" x2="18" y2="18" stroke={color} strokeWidth="2" />
<line x1="6" y1="18" x2="18" y2="6" stroke={color} strokeWidth="2" />
</SvgIcon>
);
};

export default XIcon;
141 changes: 141 additions & 0 deletions src/components/map/mapbox/controls/BBoxSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import {
Box,
Divider,
FormControl,
FormControlLabel,
FormGroup,
IconButton,
Popper,
Typography,
} from "@mui/material";
import BBoxIcon from "../../../icon/BBoxIcon";
import grey from "../../../common/colors/grey";
import { borderRadius, fontSize } from "../../../../styles/constants";
import AddIcon from "../../../icon/AddIcon";
import XIcon from "../../../icon/XIcon";
import { Map } from "mapbox-gl";

interface BBoxSelectionProps {
map: Map | null | undefined;
}

const BBoxSelection: React.FC<BBoxSelectionProps> = ({ map }) => {
const [open, setOpen] = useState<boolean>(false);

const anchorRef = useRef(null);
const popperRef = useRef<HTMLDivElement>(null);

const handleAddBBox = useCallback(() => {
setOpen(false);
}, []);

const handleClickOutside = useCallback((event: MouseEvent) => {
if (!popperRef.current || !anchorRef.current) {
return;
}
if (!popperRef.current.contains(event.target as Node)) {
setOpen(false);
}
}, []);

useEffect(() => {
if (open) {
document.addEventListener("mousedown", handleClickOutside);
} else {
document.removeEventListener("mousedown", handleClickOutside);
}

return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [handleClickOutside, open]);

return (
<>
<IconButton
ref={anchorRef}
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
onClick={() => setOpen(!open)}
>
<BBoxIcon />
</IconButton>
<Popper
id="detailmap-popper-id"
open={open}
anchorEl={anchorRef.current}
ref={popperRef}
role={undefined}
placement={"left-start"}
disablePortal
modifiers={[
{
name: "offset",
options: {
offset: [0, 10], // This applies an offset of 10px downward
},
},
]}
>
<Box
sx={{
color: grey["mapMenuText"],
display: "inline-block",
whiteSpace: "nowrap",
borderRadius: borderRadius["menu"],
backgroundColor: grey["resultCard"],
zIndex: 1,
}}
>
<Typography
sx={{
backgroundColor: "white",
borderRadius: borderRadius["menuTop"],
fontSize: fontSize["mapMenuItem"],
paddingTop: "7px",
paddingBottom: "7px",
paddingLeft: "15px",
fontWeight: "bold",
}}
>
Bounding Box Selection
</Typography>
<Divider />
<Box sx={{ paddingLeft: "15px", paddingRight: "15px" }}>
<Divider />
<Box sx={{ paddingLeft: "15px", paddingRight: "15px" }}>
<Divider />
<FormControl component="fieldset">
<FormGroup>
<FormControlLabel
onClick={handleAddBBox}
control={<AddIcon />}
label={
<Typography sx={{ fontSize: fontSize["mapMenuSubItem"] }}>
Add More Selection
</Typography>
}
/>
<FormControlLabel
control={<XIcon />}
label={
<Typography sx={{ fontSize: fontSize["mapMenuSubItem"] }}>
Clear Selection
</Typography>
}
/>
</FormGroup>
</FormControl>
</Box>
</Box>
</Box>
</Popper>
</>
);
};

export default BBoxSelection;
134 changes: 134 additions & 0 deletions src/components/map/mapbox/controls/PolygonSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import BBoxIcon from "../../../icon/BBoxIcon";
import XIcon from "../../../icon/XIcon";
import { Map } from "mapbox-gl";
import MapboxDraw from "@mapbox/mapbox-gl-draw";
import * as turf from "@turf/turf";
import { createRoot } from "react-dom/client";

interface PolygonSelectionProps {
map: Map | undefined | null;
}

const PolygonSelection: React.FC<PolygonSelectionProps> = ({ map }) => {
const [open, setOpen] = useState<boolean>(false);

const anchorRef = useRef(null);
const popperRef = useRef<HTMLDivElement>(null);
const [roundedArea, setRoundedArea] = useState<number | undefined>(undefined);

const handleAddBBox = useCallback(() => {
setOpen(false);
}, []);

useEffect(() => {
console.log("aaamap", map);
}, [map]);

const handleClickOutside = useCallback((event: MouseEvent) => {
if (!popperRef.current || !anchorRef.current) {
return;
}
if (!popperRef.current.contains(event.target as Node)) {
setOpen(false);
}
}, []);

useEffect(() => {
if (open) {
document.addEventListener("mousedown", handleClickOutside);
} else {
document.removeEventListener("mousedown", handleClickOutside);
}

return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [handleClickOutside, open]);

useEffect(() => {
const draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
polygon: true,
trash: true,
},
defaultMode: "draw_polygon",
styles: [
{
id: "gl-draw-polygon-fill",
type: "fill",
paint: {
"fill-color": "#6e599f",
"fill-opacity": 0.5,
},
},
{
id: "gl-draw-polygon-stroke",
type: "line",
paint: {
"line-color": "#6e599f",
"line-width": 2,
},
},
{
id: "gl-draw-line",
type: "line",
paint: {
"line-color": "#6e599f",
"line-width": 2,
},
},
{
id: "gl-draw-point",
type: "circle",
paint: {
"circle-radius": 5,
"circle-color": "#6e599f",
},
},
],
});

const updateArea = (e: any) => {
const data = draw.getAll();
if (data.features.length > 0) {
const area = turf.area(data);
setRoundedArea(Math.round(area * 100) / 100);
} else {
setRoundedArea(undefined);
}
};
if (map) {
map.addControl(draw);

const drawButton = document.querySelector(".mapbox-gl-draw_polygon");
if (drawButton) {
const root = createRoot(drawButton);
root.render(<BBoxIcon />);
}

const trashButton = document.querySelector(".mapbox-gl-draw_trash");
if (trashButton) {
const root = createRoot(trashButton);
root.render(<XIcon />);
}

map.on("draw.create", updateArea);
map.on("draw.delete", updateArea);
map.on("draw.update", updateArea);
}

return () => {
if (!map) return;
map.off("draw.create", updateArea);
map.off("draw.delete", updateArea);
map.off("draw.update", updateArea);
map.removeControl(draw);
};
}, [map]);

return <></>;
};

export default PolygonSelection;
Loading

0 comments on commit 30eb0b5

Please sign in to comment.