@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navigoo/map-components",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Reusable React components for mapping and routing in Yaoundé",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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 = 16;
61
+ const maxZoom = 18;
62
+
63
+ // 🌍 Configuration initiale de la carte
56
64
  mapRef.current = L.map(mapContainerRef.current, {
57
- center: [7.365, 12.3], // Centre approximatif du Cameroun
58
- zoom: 7, // Zoom ajusté pour voir l'ensemble du Cameroun
59
- minZoom: 6,
60
- maxZoom: 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: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
70
- maxZoom: 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
- mapRef.current.on('click', async (e: L.LeafletMouseEvent) => {
85
- const { lat, lng } = e.latlng;
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
- if (mapRef.current) {
103
- mapRef.current.remove();
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
- // Nettoyer les couches précédentes
113
- if (routeLayerRef.current) {
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((step) => {
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 weight = index === selectedRouteIndex ? 5 : 3;
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', (e: L.LeafletMouseEvent) => {
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 = [...allCoordinates, ...coordinates];
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
- if (allCoordinates.length > 0) {
213
- mapRef.current!.fitBounds(L.latLngBounds(allCoordinates));
214
- }
215
- } else if (searchedPlace && searchedPlace.coordinates) {
216
- centerOnPoint(searchedPlace.coordinates.lat, searchedPlace.coordinates.lng, searchedPlace.name);
217
- } else if (userLocation) {
218
- centerOnPoint(userLocation.latitude, userLocation.longitude, 'Votre position');
219
- } else {
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
- }, [apiClient, userLocation, searchedPlace, routes, selectedRouteIndex, setSelectedRouteIndex]);
132
+ }, [routes, selectedRouteIndex]);
223
133
 
224
- return <div className="w-full h-screen" ref={mapContainerRef} />;
134
+ return <div ref={mapContainerRef} className="w-full h-screen rounded-xl overflow-hidden shadow-md" />;
225
135
  };
226
136
 
227
- export default MapView;
137
+ export default MapView;