Skip to content

Commit

Permalink
Merge pull request #89 from aodn/feature/5733-map-click-function
Browse files Browse the repository at this point in the history
Feature/5733 map click function
  • Loading branch information
utas-raymondng authored Jul 16, 2024
2 parents a09c98d + 9b71ebf commit 23394e5
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 76 deletions.
43 changes: 36 additions & 7 deletions src/components/map/mapbox/component/SpatialExtents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { MapLayerMouseEvent } from "mapbox-gl";
interface SpatialExtentsProps {
layerId: string;
onDatasetSelected?: (uuid: Array<string>) => void;
// added layer ids are layers added on current map other than spatial extents layer
// they are used in onEmptySpaceClick to identify if the click falls in empty space or in any layers
addedLayerIds?: Array<string>;
}

const createPointsLayerId = (id: string) => `${id}-points`;
Expand All @@ -26,13 +29,13 @@ const createSourceId = (layerId: string, uuid: string) =>
const SpatialExtents: FC<SpatialExtentsProps> = ({
layerId,
onDatasetSelected,
addedLayerIds = [],
}) => {
const { map } = useContext(MapContext);
const dispatch = useDispatch<AppDispatch>();
const [spatialExtentsUUid, setSpatialExtentsUUid] = useState<Array<string>>();

// util function to get collection data given uuid
// and based on boolean indicating whether to fetch only geometry-related properties
const getCollectionData = useCallback(
async (uuid: string) => {
const param: SearchParameters = {
Expand Down Expand Up @@ -147,29 +150,55 @@ const SpatialExtents: FC<SpatialExtentsProps> = ({
};
}, [spatialExtentsUUid, layerId, getCollectionData, map]);

const onPointMouseClick = useCallback(
const onPointClick = useCallback(
(ev: MapLayerMouseEvent): void => {
ev.preventDefault();
// Make sure even same id under same area will be set once.
if (ev.features) {
const uuids = [
...new Set(ev.features.map((feature) => feature.properties?.uuid)),
];
setSpatialExtentsUUid(uuids);

// Give time for the state to be updated
if (onDatasetSelected) setTimeout(() => onDatasetSelected(uuids), 100);
if (onDatasetSelected) {
onDatasetSelected(uuids);
}
}
},
[setSpatialExtentsUUid, onDatasetSelected]
);

const onEmptySpaceClick = useCallback(
(ev: MapLayerMouseEvent) => {
const point = map?.project(ev.lngLat);

// Query for features at the clicked point, but only in the cluster and unclustered point layers
const features = point
? map?.queryRenderedFeatures(point, {
layers: addedLayerIds,
})
: [];

// If no features are found at the click point (i.e., clicked on empty space)
if (features && features.length === 0) {
// Clear the spatial extents uuid array
setSpatialExtentsUUid([]);
// TODO: if we need to clear selected datasets when click on empty space
if (onDatasetSelected) onDatasetSelected([]);
}
},
[map, addedLayerIds, onDatasetSelected]
);

useEffect(() => {
map?.on("click", layerId, onPointMouseClick);
map?.on("click", layerId, onPointClick);
map?.on("click", onEmptySpaceClick);

return () => {
map?.off("click", layerId, onPointMouseClick);
map?.off("click", layerId, onPointClick);
map?.off("click", onEmptySpaceClick);
};
}, [map, layerId, onPointMouseClick]);
}, [map, layerId, onPointClick, onEmptySpaceClick]);

// Remove all layers and sources created by addSpatialExtentsLayer
useEffect(() => {
Expand Down
44 changes: 25 additions & 19 deletions src/components/map/mapbox/layers/ClusterLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface ClusterLayerConfig {

interface ClusterLayerProps extends LayersProps {
clusterLayerConfig?: Partial<ClusterLayerConfig>;
onDatasetSelected?: (uuid: Array<string>) => void;
}

const defaultClusterLayerConfig: ClusterLayerConfig = {
Expand Down Expand Up @@ -75,13 +76,14 @@ const defaultClusterLayerConfig: ClusterLayerConfig = {

// These function help to get the correct id and reduce the need to set those id in the
// useEffect list
const getLayerId = (id: string | undefined) => `cluster-layer-${id}`;
export const getLayerId = (id: string | undefined) => `cluster-layer-${id}`;

const getClusterSourceId = (layerId: string) => `${layerId}-source`;
export const getClusterSourceId = (layerId: string) => `${layerId}-source`;

const getClusterLayerId = (layerId: string) => `${layerId}-clusters`;
export const getClusterLayerId = (layerId: string) => `${layerId}-clusters`;

const getUnclusterPointId = (layerId: string) => `${layerId}-unclustered-point`;
export const getUnclusterPointId = (layerId: string) =>
`${layerId}-unclustered-point`;

const ClusterLayer: FC<ClusterLayerProps> = ({
collections,
Expand Down Expand Up @@ -160,24 +162,24 @@ const ClusterLayer: FC<ClusterLayerProps> = ({
"circle-color": [
"step",
["get", "point_count"],
config.clusterCircleColor?.default,
config.pointCountThresholds?.medium,
config.clusterCircleColor?.medium,
config.pointCountThresholds?.large,
config.clusterCircleColor?.large,
config.pointCountThresholds?.extra_large,
config.clusterCircleColor?.extra_large,
config.clusterCircleColor.default,
config.pointCountThresholds.medium,
config.clusterCircleColor.medium,
config.pointCountThresholds.large,
config.clusterCircleColor.large,
config.pointCountThresholds.extra_large,
config.clusterCircleColor.extra_large,
],
"circle-radius": [
"step",
["get", "point_count"],
config.clusterCircleSize?.default,
config.pointCountThresholds?.medium,
config.clusterCircleSize?.medium,
config.pointCountThresholds?.large,
config.clusterCircleSize?.large,
config.pointCountThresholds?.extra_large,
config.clusterCircleSize?.extra_large,
config.clusterCircleSize.default,
config.pointCountThresholds.medium,
config.clusterCircleSize.medium,
config.pointCountThresholds.large,
config.clusterCircleSize.large,
config.pointCountThresholds.extra_large,
config.clusterCircleSize.extra_large,
],
},
});
Expand Down Expand Up @@ -268,7 +270,11 @@ const ClusterLayer: FC<ClusterLayerProps> = ({
layerId={unclusterPointLayer}
onDatasetSelected={onDatasetSelected}
/>
<SpatialExtents layerId={unclusterPointLayer} />
<SpatialExtents
layerId={unclusterPointLayer}
addedLayerIds={[clusterLayer, unclusterPointLayer]}
onDatasetSelected={onDatasetSelected}
/>
</>
);
};
Expand Down
9 changes: 8 additions & 1 deletion src/components/map/mapbox/layers/HeatmapLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LayersProps, createCentroidDataSource } from "./Layers";
import { mergeWithDefaults } from "../../../common/utils";
import MapPopup from "../component/MapPopup";
import SpatialExtents from "../component/SpatialExtents";
import { OGCCollection } from "../../../common/store/searchReducer";

interface HeatmapLayer {
maxZoom: number;
Expand All @@ -28,7 +29,9 @@ interface HeatmapConfig {
}

interface HeatmapLayerProps extends LayersProps {
collections?: OGCCollection[] | undefined;
heatmapLayerConfig?: Partial<HeatmapConfig>;
onDatasetSelected?: (uuid: Array<string>) => void;
}

const defaultHeatmapConfig: HeatmapConfig = {
Expand Down Expand Up @@ -201,7 +204,11 @@ const HeatmapLayer: FC<HeatmapLayerProps> = ({
layerId={getCircleLayerId(layerId)}
onDatasetSelected={onDatasetSelected}
/>
<SpatialExtents layerId={getCircleLayerId(layerId)} />
<SpatialExtents
layerId={getCircleLayerId(layerId)}
addedLayerIds={[getHeatmapLayerId(layerId), getCircleLayerId(layerId)]}
onDatasetSelected={onDatasetSelected}
/>
</>
);
};
Expand Down
35 changes: 27 additions & 8 deletions src/components/result/GridResultCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Box,
Card,
CardActionArea,
CardActions,
Expand All @@ -7,16 +8,15 @@ import {
Grid,
Typography,
} from "@mui/material";
import { pageDefault } from "../common/constants";
import { OGCCollection } from "../common/store/searchReducer";
import { useNavigate } from "react-router-dom";
import React, { useCallback } from "react";
import StaticResultCardButton from "../common/buttons/StaticResultCardButton";
import DynamicResultCardButton from "../common/buttons/DynamicResultCardButton";
import { trimContent } from "./CardUtils";
import InfoIcon from "@mui/icons-material/Info";
import LinkIcon from "@mui/icons-material/Link";
import DownloadIcon from "@mui/icons-material/Download";
import TaskAltIcon from "@mui/icons-material/TaskAlt";
import { margin, padding } from "../../styles/constants";

interface GridResultCardProps {
content?: OGCCollection;
Expand All @@ -33,6 +33,7 @@ interface GridResultCardProps {
) => void)
| undefined;
onClickCard?: ((uuid: string) => void) | undefined;
isSelectedDataset?: boolean;
}

const GridResultCard: React.FC<GridResultCardProps> = (props) => {
Expand All @@ -58,7 +59,10 @@ const GridResultCard: React.FC<GridResultCardProps> = (props) => {
return (
<Card
variant="outlined"
sx={{ height: "100%" }}
sx={{
height: "300px",
border: props.isSelectedDataset ? "2px solid #618CA5" : "none",
}}
data-testid="result-card-grid"
>
<CardActionArea onClick={handleClick}>
Expand All @@ -72,15 +76,30 @@ const GridResultCard: React.FC<GridResultCardProps> = (props) => {
/>
</Grid>
<Grid item xs={12}>
<Typography
component="div"
<Box
sx={{
marginBottom: "10px",
height: "64px",
display: "flex",
justifyContent: "space-between",
}}
>
{trimContent(props.content?.title)}
</Typography>
<Typography
sx={{
paddingY: padding.small,
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitLineClamp: "2",
WebkitBoxOrient: "vertical",
}}
>
{props.content.title}
</Typography>
{props.isSelectedDataset && (
<TaskAltIcon color="primary" sx={{ mt: margin.lg }} />
)}
</Box>
</Grid>
</Grid>
</CardContent>
Expand Down
60 changes: 45 additions & 15 deletions src/components/result/ListResultCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Box,
Card,
CardActionArea,
CardActions,
Expand All @@ -7,17 +8,16 @@ import {
Grid,
Typography,
} from "@mui/material";
import { pageDefault } from "../common/constants";
import WhereToVoteIcon from "@mui/icons-material/WhereToVote";
import DownloadIcon from "@mui/icons-material/Download";
import InfoIcon from "@mui/icons-material/Info";
import { OGCCollection } from "../common/store/searchReducer";
import { useNavigate } from "react-router-dom";
import { trimContent } from "./CardUtils";
import LinkIcon from "@mui/icons-material/Link";
import DynamicResultCardButton from "../common/buttons/DynamicResultCardButton";
import StaticResultCardButton from "../common/buttons/StaticResultCardButton";
import { useCallback } from "react";
import TaskAltIcon from "@mui/icons-material/TaskAlt";
import { margin } from "../../styles/constants";

interface ResultCardProps {
content: OGCCollection;
Expand Down Expand Up @@ -46,6 +46,7 @@ interface ResultCardProps {
) => void)
| undefined;
onClickCard?: ((uuid: string) => void) | undefined;
isSelectedDataset?: boolean;
}

const ListResultCard = (props: ResultCardProps) => {
Expand All @@ -70,21 +71,40 @@ const ListResultCard = (props: ResultCardProps) => {
return (
<Card
variant="outlined"
sx={{
width: "99%",
minHeight: "250px",
border: props.isSelectedDataset ? "2px solid #618CA5" : "none",
}}
data-testid="result-card-list"
sx={{ width: "99%" }}
>
<CardActionArea onClick={handleClick}>
<CardContent>
<Typography
component="div"
sx={{ marginBottom: "10px", height: "48px" }}
<Box
sx={{
marginBottom: "10px",
height: "48px",
display: "flex",
justifyContent: "space-between",
}}
>
<Grid container>
<Grid item xs={11}>
{trimContent(props.content.title)}
</Grid>
</Grid>
</Typography>
<Typography
sx={{
padding: 0,
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitLineClamp: "2",
WebkitBoxOrient: "vertical",
}}
>
{props.content.title}
</Typography>
{props.isSelectedDataset && (
<TaskAltIcon color="primary" sx={{ mt: margin.sm }} />
)}
</Box>

<Grid container spacing={1}>
<Grid item xs={3}>
<CardMedia
Expand All @@ -94,8 +114,18 @@ const ListResultCard = (props: ResultCardProps) => {
/>
</Grid>
<Grid item xs={9}>
<Typography variant="body2">
{trimContent(props.content.description, 180)}
<Typography
variant="body2"
sx={{
padding: 0,
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitLineClamp: "5",
WebkitBoxOrient: "vertical",
}}
>
{props.content.description}
</Typography>
</Grid>
</Grid>
Expand Down
Loading

0 comments on commit 23394e5

Please sign in to comment.