Skip to content

Commit 9285737

Browse files
committed
Refactor CustomPriorityBoundsField to include AutoZoomToBounds feature for improved map interaction.
1 parent a1b1412 commit 9285737

5 files changed

Lines changed: 107 additions & 11 deletions

File tree

src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenge.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ import {
3434
CustomArrayFieldTemplate,
3535
CustomCheckboxField,
3636
CustomFieldTemplate,
37-
PriorityBoundsFieldAdapter,
3837
CustomSelectWidget,
3938
CustomTextWidget,
4039
LabelWithHelp,
4140
MarkdownDescriptionField,
4241
MarkdownEditField,
42+
PriorityBoundsFieldAdapter,
4343
} from "../../../../Custom/RJSFFormFieldAdapter/RJSFFormFieldAdapter";
4444
import External from "../../../../External/External";
4545
import WithCurrentUser from "../../../../HOCs/WithCurrentUser/WithCurrentUser";

src/components/Custom/RJSFFormFieldAdapter/CustomPriorityBoundsField.jsx

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { useState, useEffect } from "react";
1+
import { useEffect, useState } from "react";
22
import { MapContainer, TileLayer, useMap } from "react-leaflet";
33
import SvgSymbol from "../../SvgSymbol/SvgSymbol";
44
import "leaflet/dist/leaflet.css";
55
import "leaflet-lasso";
6-
import BoundsSelector from "./components/BoundsSelector";
6+
import L from "leaflet";
77
import { FormattedMessage } from "react-intl";
88
import messages from "./Messages";
9+
import BoundsSelector from "./components/BoundsSelector";
910
import { usePriorityBoundsData } from "./context/PriorityBoundsDataContext";
1011

1112
const MapViewPreserver = ({ onViewChange }) => {
@@ -31,21 +32,101 @@ const MapViewPreserver = ({ onViewChange }) => {
3132
return null;
3233
};
3334

35+
const AutoZoomToBounds = ({
36+
boundsData,
37+
hasZoomed,
38+
setHasZoomed,
39+
allPriorityBounds,
40+
priorityType,
41+
}) => {
42+
const map = useMap();
43+
44+
useEffect(() => {
45+
if (!map || hasZoomed) {
46+
return;
47+
}
48+
49+
try {
50+
// Calculate bounds from all available polygon data
51+
const leafletBounds = L.latLngBounds();
52+
let hasValidBounds = false;
53+
54+
// First, try to use the current priority bounds
55+
if (boundsData && boundsData.length > 0) {
56+
boundsData.forEach((feature) => {
57+
if (feature?.geometry?.coordinates?.[0]) {
58+
const coords = feature.geometry.coordinates[0];
59+
coords.forEach((coord) => {
60+
if (Array.isArray(coord) && coord.length >= 2) {
61+
// Convert from [lng, lat] to [lat, lng]
62+
leafletBounds.extend([coord[1], coord[0]]);
63+
hasValidBounds = true;
64+
}
65+
});
66+
}
67+
});
68+
}
69+
70+
// If no bounds from current priority, try to use all priority bounds
71+
if (!hasValidBounds && allPriorityBounds) {
72+
Object.entries(allPriorityBounds).forEach(([type, priorityData]) => {
73+
if (Array.isArray(priorityData) && priorityData.length > 0) {
74+
priorityData.forEach((feature) => {
75+
if (feature?.geometry?.coordinates?.[0]) {
76+
const coords = feature.geometry.coordinates[0];
77+
coords.forEach((coord) => {
78+
if (Array.isArray(coord) && coord.length >= 2) {
79+
// Convert from [lng, lat] to [lat, lng]
80+
leafletBounds.extend([coord[1], coord[0]]);
81+
hasValidBounds = true;
82+
}
83+
});
84+
}
85+
});
86+
}
87+
});
88+
}
89+
90+
if (hasValidBounds) {
91+
// Add some padding to the bounds
92+
const paddedBounds = leafletBounds.pad(0.1);
93+
94+
// Fit the map to the bounds with a slight delay to ensure map is ready
95+
setTimeout(() => {
96+
map.fitBounds(paddedBounds, {
97+
padding: [20, 20],
98+
maxZoom: 16, // Prevent zooming in too far
99+
animate: true,
100+
duration: 0.5,
101+
});
102+
}, 100);
103+
104+
setHasZoomed(true);
105+
}
106+
} catch (error) {
107+
console.error("Error calculating bounds for auto-zoom:", error);
108+
}
109+
}, [map, boundsData, hasZoomed, setHasZoomed, allPriorityBounds, priorityType]);
110+
111+
return null;
112+
};
113+
34114
/**
35115
* Custom field for selecting priority bounds on a map
36116
*/
37117
const CustomPriorityBoundsField = (props) => {
38118
const [isMapVisible, setIsMapVisible] = useState(false);
39119
const [viewState, setViewState] = useState({ center: [0, 0], zoom: 2 });
120+
const [hasZoomed, setHasZoomed] = useState(false);
40121

41122
// Determine priority type from field name
42123
const priorityType = props.name?.includes("highPriorityBounds")
43124
? "high"
44125
: props.name?.includes("mediumPriorityBounds")
45-
? "medium"
46-
: props.name?.includes("lowPriorityBounds")
47-
? "low"
48-
: "default";
126+
? "medium"
127+
: props.name?.includes("lowPriorityBounds")
128+
? "low"
129+
: "default";
49130

50131
const formData = props.formData || [];
51132
const { priorityBoundsData, updatePriorityBounds } = usePriorityBoundsData();
@@ -83,6 +164,13 @@ const CustomPriorityBoundsField = (props) => {
83164
}
84165
}, [formData]);
85166

167+
// Reset zoom flag when map becomes visible or data changes
168+
useEffect(() => {
169+
if (isMapVisible) {
170+
setHasZoomed(false);
171+
}
172+
}, [isMapVisible]);
173+
86174
const handleChange = (newData) => {
87175
if (typeof props.onChange === "function") {
88176
props.onChange(Array.isArray(newData) ? [...newData] : newData);
@@ -142,6 +230,14 @@ const CustomPriorityBoundsField = (props) => {
142230
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
143231
/>
144232

233+
<AutoZoomToBounds
234+
boundsData={formData}
235+
hasZoomed={hasZoomed}
236+
setHasZoomed={setHasZoomed}
237+
allPriorityBounds={priorityBoundsData}
238+
priorityType={priorityType}
239+
/>
240+
145241
<BoundsSelector
146242
value={formData}
147243
onChange={handleChange}

src/components/Custom/RJSFFormFieldAdapter/components/BoundsSelector.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useState, useEffect, useRef } from "react";
2-
import { useMap } from "react-leaflet";
31
import L from "leaflet";
2+
import { useEffect, useRef, useState } from "react";
3+
import { useMap } from "react-leaflet";
44
import "leaflet-lasso";
55

66
// Simple color scheme for priority types

src/components/Custom/RJSFFormFieldAdapter/context/PriorityBoundsDataContext.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createContext, useContext, useState, useCallback } from "react";
1+
import { createContext, useCallback, useContext, useState } from "react";
22

33
// Context for sharing priority bounds data across all priority fields
44
const PriorityBoundsDataContext = createContext({

src/components/TaskClusterMap/TaskClusterMap.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import WithVisibleLayer from "../HOCs/WithVisibleLayer/WithVisibleLayer";
2424
import { LegendToggleControl } from "./LegendToggleControl";
2525
import MapControlsDrawer from "./MapControlsDrawer";
2626
import MapMarkers from "./MapMarkers";
27-
import PriorityBoundsLayer from "./PriorityBoundsLayer";
2827
import messages from "./Messages";
28+
import PriorityBoundsLayer from "./PriorityBoundsLayer";
2929
import ZoomInMessage from "./ZoomInMessage";
3030
import "./TaskClusterMap.scss";
3131

0 commit comments

Comments
 (0)