@navigoo/map-components 1.0.8 → 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 +31 -170
package/package.json
CHANGED
|
@@ -29,7 +29,7 @@ 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
|
|
32
|
+
// 📍 Définition des limites du Cameroun
|
|
33
33
|
const CAMEROON_BOUNDS = L.latLngBounds(
|
|
34
34
|
[1.65, 8.4], // Coin sud-ouest
|
|
35
35
|
[13.08, 16.2] // Coin nord-est
|
|
@@ -60,25 +60,19 @@ const MapView: React.FC<MapViewProps> = ({
|
|
|
60
60
|
if (mapContainerRef.current && !mapRef.current) {
|
|
61
61
|
const maxZoom = 18;
|
|
62
62
|
|
|
63
|
-
// Configuration de la carte
|
|
63
|
+
// 🌍 Configuration initiale de la carte
|
|
64
64
|
mapRef.current = L.map(mapContainerRef.current, {
|
|
65
|
-
center: [
|
|
66
|
-
zoom: 6,
|
|
67
|
-
minZoom:
|
|
68
|
-
maxZoom
|
|
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
69
|
maxBounds: CAMEROON_BOUNDS,
|
|
70
|
-
maxBoundsViscosity: 1.0,
|
|
70
|
+
maxBoundsViscosity: 1.0,
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
// Forcer la carte à rester dans les limites initiales
|
|
74
|
-
mapRef.current.setMaxBounds(CAMEROON_BOUNDS);
|
|
75
|
-
|
|
76
73
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
77
|
-
attribution: '©
|
|
78
|
-
maxZoom
|
|
79
|
-
tileSize: 256,
|
|
80
|
-
zoomOffset: 0,
|
|
81
|
-
bounds: CAMEROON_BOUNDS, // Limiter les tuiles aux bounds du Cameroun
|
|
74
|
+
attribution: '© OpenStreetMap contributors',
|
|
75
|
+
maxZoom,
|
|
82
76
|
}).addTo(mapRef.current);
|
|
83
77
|
|
|
84
78
|
L.Icon.Default.mergeOptions({
|
|
@@ -89,188 +83,55 @@ const MapView: React.FC<MapViewProps> = ({
|
|
|
89
83
|
|
|
90
84
|
routeLayerRef.current = L.layerGroup().addTo(mapRef.current);
|
|
91
85
|
|
|
92
|
-
//
|
|
93
|
-
mapRef.current.
|
|
94
|
-
if (mapRef.current) {
|
|
95
|
-
const currentCenter = mapRef.current.getCenter();
|
|
96
|
-
const currentZoom = mapRef.current.getZoom();
|
|
97
|
-
|
|
98
|
-
if (!CAMEROON_BOUNDS.contains(currentCenter)) {
|
|
99
|
-
mapRef.current.panInsideBounds(CAMEROON_BOUNDS, { animate: true });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
mapRef.current.on('click', async (e: L.LeafletMouseEvent) => {
|
|
105
|
-
const { lat, lng } = e.latlng;
|
|
106
|
-
|
|
107
|
-
// Vérifier que le clic est dans les limites du Cameroun
|
|
108
|
-
if (!CAMEROON_BOUNDS.contains([lat, lng])) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const closestPlace = await apiClient.findClosestPlace(lat, lng);
|
|
113
|
-
const placeName = closestPlace?.name || 'Position sélectionnée';
|
|
114
|
-
|
|
115
|
-
if (clickMarkerRef.current) {
|
|
116
|
-
mapRef.current?.removeLayer(clickMarkerRef.current);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
clickMarkerRef.current = L.marker([lat, lng])
|
|
120
|
-
.addTo(mapRef.current!)
|
|
121
|
-
.bindPopup(`<b>${placeName}</b><br>Lat: ${lat.toFixed(6)}<br>Lng: ${lng.toFixed(6)}`)
|
|
122
|
-
.openPopup();
|
|
123
|
-
});
|
|
86
|
+
// 🧭 Afficher tout le Cameroun dès le départ
|
|
87
|
+
mapRef.current.fitBounds(CAMEROON_BOUNDS, { animate: false });
|
|
124
88
|
}
|
|
125
89
|
|
|
126
90
|
return () => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
mapRef.current = null;
|
|
130
|
-
}
|
|
91
|
+
mapRef.current?.remove();
|
|
92
|
+
mapRef.current = null;
|
|
131
93
|
};
|
|
132
94
|
}, [apiClient]);
|
|
133
95
|
|
|
134
96
|
useEffect(() => {
|
|
135
97
|
if (!mapRef.current) return;
|
|
136
98
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
routeLayerRef.current.clearLayers();
|
|
140
|
-
}
|
|
141
|
-
if (markerRef.current) {
|
|
142
|
-
markerRef.current.remove();
|
|
143
|
-
markerRef.current = null;
|
|
144
|
-
}
|
|
145
|
-
if (clickMarkerRef.current) {
|
|
146
|
-
clickMarkerRef.current.remove();
|
|
147
|
-
clickMarkerRef.current = null;
|
|
148
|
-
}
|
|
149
|
-
|
|
99
|
+
// Nettoyage
|
|
100
|
+
routeLayerRef.current?.clearLayers();
|
|
150
101
|
routePolylinesRef.current = [];
|
|
151
102
|
|
|
152
|
-
// Fonction pour centrer la carte sur un point avec un marqueur
|
|
153
|
-
const centerOnPoint = async (lat: number, lng: number, placeName: string, zoom: number = 16) => {
|
|
154
|
-
// Vérifier que le point est dans les limites du Cameroun
|
|
155
|
-
if (!CAMEROON_BOUNDS.contains([lat, lng])) {
|
|
156
|
-
console.warn('Le point est en dehors des limites du Cameroun');
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let displayName = placeName;
|
|
161
|
-
if (placeName === 'Votre position') {
|
|
162
|
-
const closestPlace = await apiClient.findClosestPlace(lat, lng);
|
|
163
|
-
displayName = closestPlace?.name || placeName;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Supprimer l'ancien marqueur s'il existe
|
|
167
|
-
if (markerRef.current) {
|
|
168
|
-
markerRef.current.remove();
|
|
169
|
-
markerRef.current = null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
mapRef.current!.setView([lat, lng], zoom, { animate: true });
|
|
173
|
-
markerRef.current = L.marker([lat, lng])
|
|
174
|
-
.addTo(mapRef.current!)
|
|
175
|
-
.bindPopup(`<b>${displayName}</b><br>Lat: ${lat.toFixed(6)}<br>Lng: ${lng.toFixed(6)}`)
|
|
176
|
-
.openPopup();
|
|
177
|
-
};
|
|
178
|
-
|
|
179
103
|
if (routes && routes.length > 0) {
|
|
180
|
-
// Gérer les itinéraires
|
|
181
104
|
let allCoordinates: [number, number][] = [];
|
|
105
|
+
|
|
182
106
|
routes.forEach((route, index) => {
|
|
183
107
|
const coordinates: [number, number][] = [];
|
|
184
|
-
route.steps.forEach(
|
|
108
|
+
route.steps.forEach(step => {
|
|
185
109
|
const latLngs = parseWKTLineString(step.geometry);
|
|
186
110
|
if (latLngs.length > 0) coordinates.push(...latLngs);
|
|
187
111
|
});
|
|
188
112
|
|
|
189
113
|
if (coordinates.length > 0) {
|
|
190
114
|
const color = index === selectedRouteIndex ? 'green' : 'black';
|
|
191
|
-
const
|
|
192
|
-
const opacity = index === selectedRouteIndex ? 1.0 : 0.5;
|
|
193
|
-
|
|
194
|
-
const polyline = L.polyline(coordinates, { color, weight, opacity })
|
|
115
|
+
const polyline = L.polyline(coordinates, { color, weight: 4, opacity: 0.8 })
|
|
195
116
|
.addTo(routeLayerRef.current!)
|
|
196
|
-
.on('click', (
|
|
197
|
-
L.DomEvent.stopPropagation(e);
|
|
198
|
-
setSelectedRouteIndex(index);
|
|
199
|
-
routePolylinesRef.current.forEach((pl, i) => {
|
|
200
|
-
pl.setStyle({
|
|
201
|
-
color: i === index ? 'green' : 'black',
|
|
202
|
-
weight: i === index ? 5 : 3,
|
|
203
|
-
opacity: i === index ? 1.0 : 0.5,
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
const bounds = polyline.getBounds();
|
|
207
|
-
const center = bounds.getCenter();
|
|
208
|
-
L.popup()
|
|
209
|
-
.setLatLng(center)
|
|
210
|
-
.setContent(`
|
|
211
|
-
<b>Route ${index + 1}</b><br>
|
|
212
|
-
Distance: ${route.distance.toFixed(2)} m<br>
|
|
213
|
-
Durée: ${(route.duration / 60).toFixed(2)} min<br>
|
|
214
|
-
Départ: ${route.startPlaceName || 'Départ'}<br>
|
|
215
|
-
Destination: ${route.endPlaceName || 'Destination'}
|
|
216
|
-
`)
|
|
217
|
-
.openOn(mapRef.current!);
|
|
218
|
-
});
|
|
219
|
-
|
|
117
|
+
.on('click', () => setSelectedRouteIndex(index));
|
|
220
118
|
routePolylinesRef.current.push(polyline);
|
|
221
|
-
allCoordinates
|
|
222
|
-
|
|
223
|
-
if (index === selectedRouteIndex) {
|
|
224
|
-
const startPoint = coordinates[0];
|
|
225
|
-
const endPoint = coordinates[coordinates.length - 1];
|
|
226
|
-
(async () => {
|
|
227
|
-
let startPlaceName = route.startPlaceName || 'Départ';
|
|
228
|
-
if (route.startPlaceName === 'Votre position') {
|
|
229
|
-
const closestStartPlace = await apiClient.findClosestPlace(startPoint[1], startPoint[0]);
|
|
230
|
-
startPlaceName = closestStartPlace?.name || route.startPlaceName;
|
|
231
|
-
}
|
|
232
|
-
L.marker(startPoint).addTo(routeLayerRef.current!).bindPopup(`
|
|
233
|
-
<b>${startPlaceName}</b><br>Lat: ${startPoint[0].toFixed(6)}<br>Lng: ${startPoint[1].toFixed(6)}
|
|
234
|
-
`);
|
|
235
|
-
L.marker(endPoint).addTo(routeLayerRef.current!).bindPopup(`
|
|
236
|
-
<b>${route.endPlaceName || 'Destination'}</b><br>Lat: ${endPoint[0].toFixed(6)}<br>Lng: ${endPoint[1].toFixed(6)}
|
|
237
|
-
`);
|
|
238
|
-
})();
|
|
239
|
-
}
|
|
119
|
+
allCoordinates.push(...coordinates);
|
|
240
120
|
}
|
|
241
121
|
});
|
|
242
122
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
Math.max(paddedBounds.getSouth(), CAMEROON_BOUNDS.getSouth()),
|
|
252
|
-
Math.max(paddedBounds.getWest(), CAMEROON_BOUNDS.getWest())
|
|
253
|
-
],
|
|
254
|
-
[
|
|
255
|
-
Math.min(paddedBounds.getNorth(), CAMEROON_BOUNDS.getNorth()),
|
|
256
|
-
Math.min(paddedBounds.getEast(), CAMEROON_BOUNDS.getEast())
|
|
257
|
-
]
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
mapRef.current!.fitBounds(finalBounds);
|
|
261
|
-
}
|
|
262
|
-
} else if (searchedPlace && searchedPlace.coordinates) {
|
|
263
|
-
centerOnPoint(searchedPlace.coordinates.lat, searchedPlace.coordinates.lng, searchedPlace.name);
|
|
264
|
-
} else if (userLocation) {
|
|
265
|
-
centerOnPoint(userLocation.latitude, userLocation.longitude, 'Votre position');
|
|
266
|
-
} else {
|
|
267
|
-
// Retour à la vue par défaut du Cameroun
|
|
268
|
-
mapRef.current!.setView([5.5, 12.0], 6, { animate: true });
|
|
269
|
-
mapRef.current!.setMaxBounds(CAMEROON_BOUNDS);
|
|
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 });
|
|
270
131
|
}
|
|
271
|
-
}, [
|
|
132
|
+
}, [routes, selectedRouteIndex]);
|
|
272
133
|
|
|
273
|
-
return <div className="w-full h-screen"
|
|
134
|
+
return <div ref={mapContainerRef} className="w-full h-screen rounded-xl overflow-hidden shadow-md" />;
|
|
274
135
|
};
|
|
275
136
|
|
|
276
|
-
export default
|
|
137
|
+
export default MapView;
|