@navigoo/map-components 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/MapView.tsx +39 -129
package/package.json
CHANGED
|
@@ -29,6 +29,12 @@ const MapView: React.FC<MapViewProps> = ({
|
|
|
29
29
|
const clickMarkerRef = useRef<L.Marker | null>(null);
|
|
30
30
|
const routePolylinesRef = useRef<L.Polyline[]>([]);
|
|
31
31
|
|
|
32
|
+
// 📍 Définition des limites du Cameroun
|
|
33
|
+
const CAMEROON_BOUNDS = L.latLngBounds(
|
|
34
|
+
[1.65, 8.4], // Coin sud-ouest
|
|
35
|
+
[13.08, 16.2] // Coin nord-est
|
|
36
|
+
);
|
|
37
|
+
|
|
32
38
|
const parseWKTLineString = (wkt: string): [number, number][] => {
|
|
33
39
|
try {
|
|
34
40
|
const geo = parse(wkt);
|
|
@@ -50,26 +56,23 @@ const MapView: React.FC<MapViewProps> = ({
|
|
|
50
56
|
return [];
|
|
51
57
|
};
|
|
52
58
|
|
|
53
|
-
useEffect(() => {
|
|
59
|
+
useEffect(() => {
|
|
54
60
|
if (mapContainerRef.current && !mapRef.current) {
|
|
55
|
-
const maxZoom =
|
|
61
|
+
const maxZoom = 18;
|
|
62
|
+
|
|
63
|
+
// 🌍 Configuration initiale de la carte
|
|
56
64
|
mapRef.current = L.map(mapContainerRef.current, {
|
|
57
|
-
center: [7.
|
|
58
|
-
zoom:
|
|
59
|
-
minZoom:
|
|
60
|
-
maxZoom
|
|
61
|
-
maxBounds:
|
|
62
|
-
[1.65, 8.4], // Coin sud-ouest du Cameroun
|
|
63
|
-
[13.08, 16.2], // Coin nord-est du Cameroun
|
|
64
|
-
],
|
|
65
|
+
center: [7.3697, 12.3547], // Centre géographique du Cameroun
|
|
66
|
+
zoom: 6,
|
|
67
|
+
minZoom: 5, // Permet un léger recul
|
|
68
|
+
maxZoom,
|
|
69
|
+
maxBounds: CAMEROON_BOUNDS,
|
|
65
70
|
maxBoundsViscosity: 1.0,
|
|
66
71
|
});
|
|
67
72
|
|
|
68
73
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
69
|
-
attribution: '©
|
|
70
|
-
maxZoom
|
|
71
|
-
tileSize: 256,
|
|
72
|
-
zoomOffset: 0,
|
|
74
|
+
attribution: '© OpenStreetMap contributors',
|
|
75
|
+
maxZoom,
|
|
73
76
|
}).addTo(mapRef.current);
|
|
74
77
|
|
|
75
78
|
L.Icon.Default.mergeOptions({
|
|
@@ -78,150 +81,57 @@ useEffect(() => {
|
|
|
78
81
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
|
79
82
|
});
|
|
80
83
|
|
|
81
|
-
|
|
82
84
|
routeLayerRef.current = L.layerGroup().addTo(mapRef.current);
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const closestPlace = await apiClient.findClosestPlace(lat, lng);
|
|
87
|
-
const placeName = closestPlace?.name || 'Position sélectionnée';
|
|
88
|
-
|
|
89
|
-
if (clickMarkerRef.current) {
|
|
90
|
-
mapRef.current?.removeLayer(clickMarkerRef.current);
|
|
91
|
-
clickMarkerRef.current = null;
|
|
92
|
-
} else {
|
|
93
|
-
clickMarkerRef.current = L.marker([lat, lng])
|
|
94
|
-
.addTo(mapRef.current!)
|
|
95
|
-
.bindPopup(`<b>${placeName}</b><br>Lat: ${lat.toFixed(6)}<br>Lng: ${lng.toFixed(6)}`)
|
|
96
|
-
.openPopup();
|
|
97
|
-
}
|
|
98
|
-
});
|
|
86
|
+
// 🧭 Afficher tout le Cameroun dès le départ
|
|
87
|
+
mapRef.current.fitBounds(CAMEROON_BOUNDS, { animate: false });
|
|
99
88
|
}
|
|
100
89
|
|
|
101
90
|
return () => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
mapRef.current = null;
|
|
105
|
-
}
|
|
91
|
+
mapRef.current?.remove();
|
|
92
|
+
mapRef.current = null;
|
|
106
93
|
};
|
|
107
94
|
}, [apiClient]);
|
|
108
95
|
|
|
109
96
|
useEffect(() => {
|
|
110
97
|
if (!mapRef.current) return;
|
|
111
98
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
routeLayerRef.current.clearLayers();
|
|
115
|
-
}
|
|
116
|
-
if (markerRef.current) {
|
|
117
|
-
markerRef.current.remove();
|
|
118
|
-
markerRef.current = null;
|
|
119
|
-
}
|
|
120
|
-
if (clickMarkerRef.current) {
|
|
121
|
-
clickMarkerRef.current.remove();
|
|
122
|
-
clickMarkerRef.current = null;
|
|
123
|
-
}
|
|
124
|
-
|
|
99
|
+
// Nettoyage
|
|
100
|
+
routeLayerRef.current?.clearLayers();
|
|
125
101
|
routePolylinesRef.current = [];
|
|
126
102
|
|
|
127
|
-
// Fonction pour centrer la carte sur un point avec un marqueur
|
|
128
|
-
const centerOnPoint = async (lat: number, lng: number, placeName: string, zoom: number = 16) => {
|
|
129
|
-
let displayName = placeName;
|
|
130
|
-
if (placeName === 'Votre position') {
|
|
131
|
-
const closestPlace = await apiClient.findClosestPlace(lat, lng);
|
|
132
|
-
displayName = closestPlace?.name || placeName;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Supprimer l'ancien marqueur s'il existe
|
|
136
|
-
if (markerRef.current) {
|
|
137
|
-
markerRef.current.remove();
|
|
138
|
-
markerRef.current = null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
mapRef.current!.setView([lat, lng], zoom, { animate: true });
|
|
142
|
-
markerRef.current = L.marker([lat, lng])
|
|
143
|
-
.addTo(mapRef.current!)
|
|
144
|
-
.bindPopup(`<b>${displayName}</b><br>Lat: ${lat.toFixed(6)}<br>Lng: ${lng.toFixed(6)}`)
|
|
145
|
-
.openPopup();
|
|
146
|
-
};
|
|
147
|
-
|
|
148
103
|
if (routes && routes.length > 0) {
|
|
149
|
-
// Gérer les itinéraires
|
|
150
104
|
let allCoordinates: [number, number][] = [];
|
|
105
|
+
|
|
151
106
|
routes.forEach((route, index) => {
|
|
152
107
|
const coordinates: [number, number][] = [];
|
|
153
|
-
route.steps.forEach(
|
|
108
|
+
route.steps.forEach(step => {
|
|
154
109
|
const latLngs = parseWKTLineString(step.geometry);
|
|
155
110
|
if (latLngs.length > 0) coordinates.push(...latLngs);
|
|
156
111
|
});
|
|
157
112
|
|
|
158
113
|
if (coordinates.length > 0) {
|
|
159
114
|
const color = index === selectedRouteIndex ? 'green' : 'black';
|
|
160
|
-
const
|
|
161
|
-
const opacity = index === selectedRouteIndex ? 1.0 : 0.5;
|
|
162
|
-
|
|
163
|
-
const polyline = L.polyline(coordinates, { color, weight, opacity })
|
|
115
|
+
const polyline = L.polyline(coordinates, { color, weight: 4, opacity: 0.8 })
|
|
164
116
|
.addTo(routeLayerRef.current!)
|
|
165
|
-
.on('click', (
|
|
166
|
-
L.DomEvent.stopPropagation(e);
|
|
167
|
-
setSelectedRouteIndex(index);
|
|
168
|
-
routePolylinesRef.current.forEach((pl, i) => {
|
|
169
|
-
pl.setStyle({
|
|
170
|
-
color: i === index ? 'green' : 'black',
|
|
171
|
-
weight: i === index ? 5 : 3,
|
|
172
|
-
opacity: i === index ? 1.0 : 0.5,
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
const bounds = polyline.getBounds();
|
|
176
|
-
const center = bounds.getCenter();
|
|
177
|
-
L.popup()
|
|
178
|
-
.setLatLng(center)
|
|
179
|
-
.setContent(`
|
|
180
|
-
<b>Route ${index + 1}</b><br>
|
|
181
|
-
Distance: ${route.distance.toFixed(2)} m<br>
|
|
182
|
-
Durée: ${(route.duration / 60).toFixed(2)} min<br>
|
|
183
|
-
Départ: ${route.startPlaceName || 'Départ'}<br>
|
|
184
|
-
Destination: ${route.endPlaceName || 'Destination'}
|
|
185
|
-
`)
|
|
186
|
-
.openOn(mapRef.current!);
|
|
187
|
-
});
|
|
188
|
-
|
|
117
|
+
.on('click', () => setSelectedRouteIndex(index));
|
|
189
118
|
routePolylinesRef.current.push(polyline);
|
|
190
|
-
allCoordinates
|
|
191
|
-
|
|
192
|
-
if (index === selectedRouteIndex) {
|
|
193
|
-
const startPoint = coordinates[0];
|
|
194
|
-
const endPoint = coordinates[coordinates.length - 1];
|
|
195
|
-
(async () => {
|
|
196
|
-
let startPlaceName = route.startPlaceName || 'Départ';
|
|
197
|
-
if (route.startPlaceName === 'Votre position') {
|
|
198
|
-
const closestStartPlace = await apiClient.findClosestPlace(startPoint[1], startPoint[0]);
|
|
199
|
-
startPlaceName = closestStartPlace?.name || route.startPlaceName;
|
|
200
|
-
}
|
|
201
|
-
L.marker(startPoint).addTo(routeLayerRef.current!).bindPopup(`
|
|
202
|
-
<b>${startPlaceName}</b><br>Lat: ${startPoint[0].toFixed(6)}<br>Lng: ${startPoint[1].toFixed(6)}
|
|
203
|
-
`);
|
|
204
|
-
L.marker(endPoint).addTo(routeLayerRef.current!).bindPopup(`
|
|
205
|
-
<b>${route.endPlaceName || 'Destination'}</b><br>Lat: ${endPoint[0].toFixed(6)}<br>Lng: ${endPoint[1].toFixed(6)}
|
|
206
|
-
`);
|
|
207
|
-
})();
|
|
208
|
-
}
|
|
119
|
+
allCoordinates.push(...coordinates);
|
|
209
120
|
}
|
|
210
121
|
});
|
|
211
122
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
mapRef.current!.setView([7.365, 12.3], 7, { animate: true });
|
|
123
|
+
// 🗺 Ajustement intelligent de la vue
|
|
124
|
+
const routeBounds = L.latLngBounds(allCoordinates);
|
|
125
|
+
const mergedBounds = routeBounds.extend(CAMEROON_BOUNDS); // Ne jamais sortir du pays
|
|
126
|
+
mapRef.current.fitBounds(mergedBounds, { padding: [30, 30] });
|
|
127
|
+
}
|
|
128
|
+
else if (!routes?.length) {
|
|
129
|
+
// 🌍 Recentrage sur tout le Cameroun si aucun itinéraire
|
|
130
|
+
mapRef.current.fitBounds(CAMEROON_BOUNDS, { animate: true });
|
|
221
131
|
}
|
|
222
|
-
}, [
|
|
132
|
+
}, [routes, selectedRouteIndex]);
|
|
223
133
|
|
|
224
|
-
return <div className="w-full h-screen"
|
|
134
|
+
return <div ref={mapContainerRef} className="w-full h-screen rounded-xl overflow-hidden shadow-md" />;
|
|
225
135
|
};
|
|
226
136
|
|
|
227
|
-
export default
|
|
137
|
+
export default MapView;
|