@navigoo/map-components 1.0.8 → 1.1.0

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.8",
3
+ "version": "1.1.0",
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",
@@ -24,17 +24,16 @@ const MapView: React.FC<MapViewProps> = ({
24
24
  }) => {
25
25
  const mapRef = useRef<L.Map | null>(null);
26
26
  const mapContainerRef = useRef<HTMLDivElement>(null);
27
- const markerRef = useRef<L.Marker | null>(null);
28
27
  const routeLayerRef = useRef<L.LayerGroup | null>(null);
29
- const clickMarkerRef = useRef<L.Marker | null>(null);
30
28
  const routePolylinesRef = useRef<L.Polyline[]>([]);
31
29
 
32
- // Définition des limites du Cameroun
30
+ // Bornes élargies du Cameroun
33
31
  const CAMEROON_BOUNDS = L.latLngBounds(
34
- [1.65, 8.4], // Coin sud-ouest
35
- [13.08, 16.2] // Coin nord-est
32
+ [1.0, 7.8], // Sud-Ouest
33
+ [13.5, 16.5] // Nord-Est
36
34
  );
37
35
 
36
+ // 🔍 Fonction pour parser les géométries WKT
38
37
  const parseWKTLineString = (wkt: string): [number, number][] => {
39
38
  try {
40
39
  const geo = parse(wkt);
@@ -42,7 +41,7 @@ const MapView: React.FC<MapViewProps> = ({
42
41
  return geo.coordinates.map(([lng, lat]: [number, number]) => [lat, lng] as [number, number]);
43
42
  }
44
43
  } catch (error) {
45
- console.error('wellknown parsing failed:', error);
44
+ console.error('Erreur de parsing WKT:', error);
46
45
  }
47
46
  const match = wkt.match(/LINESTRING\s*\(([^)]+)\)/);
48
47
  if (match) {
@@ -56,31 +55,27 @@ const MapView: React.FC<MapViewProps> = ({
56
55
  return [];
57
56
  };
58
57
 
58
+ // 🌍 Initialisation de la carte
59
59
  useEffect(() => {
60
60
  if (mapContainerRef.current && !mapRef.current) {
61
61
  const maxZoom = 18;
62
-
63
- // Configuration de la carte avec limites strictes
62
+
64
63
  mapRef.current = L.map(mapContainerRef.current, {
65
- center: [5.5, 12.0], // Centre du Cameroun
66
- zoom: 6, // Zoom pour voir tout le Cameroun
67
- minZoom: 6,
68
- maxZoom: maxZoom,
64
+ center: [7.3697, 12.3547], // Centre du Cameroun
65
+ zoom: 6,
66
+ minZoom: 5,
67
+ maxZoom,
69
68
  maxBounds: CAMEROON_BOUNDS,
70
- maxBoundsViscosity: 1.0, // Empêche complètement le dépassement des limites
69
+ maxBoundsViscosity: 0.4, // Souple permet un léger glissement
71
70
  });
72
71
 
73
- // Forcer la carte à rester dans les limites initiales
74
- mapRef.current.setMaxBounds(CAMEROON_BOUNDS);
75
-
72
+ // 🗺 Couche OpenStreetMap
76
73
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
77
- attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
78
- maxZoom: 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
 
78
+ // 🧭 Icônes par défaut
84
79
  L.Icon.Default.mergeOptions({
85
80
  iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
86
81
  iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
@@ -89,188 +84,63 @@ const MapView: React.FC<MapViewProps> = ({
89
84
 
90
85
  routeLayerRef.current = L.layerGroup().addTo(mapRef.current);
91
86
 
92
- // Recentrer la carte si l'utilisateur essaie de la faire sortir des limites
93
- mapRef.current.on('drag', () => {
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
- });
87
+ // Recentrage automatique après affichage
88
+ setTimeout(() => {
89
+ mapRef.current?.invalidateSize();
90
+ mapRef.current?.fitBounds(CAMEROON_BOUNDS, { animate: true, padding: [20, 20] });
91
+ }, 300);
124
92
  }
125
93
 
126
94
  return () => {
127
- if (mapRef.current) {
128
- mapRef.current.remove();
129
- mapRef.current = null;
130
- }
95
+ mapRef.current?.remove();
96
+ mapRef.current = null;
131
97
  };
132
98
  }, [apiClient]);
133
99
 
100
+ // 🚗 Affichage des itinéraires
134
101
  useEffect(() => {
135
102
  if (!mapRef.current) return;
136
103
 
137
- // Nettoyer les couches précédentes
138
- if (routeLayerRef.current) {
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
-
104
+ // Nettoyage avant rendu
105
+ routeLayerRef.current?.clearLayers();
150
106
  routePolylinesRef.current = [];
151
107
 
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
108
  if (routes && routes.length > 0) {
180
- // Gérer les itinéraires
181
109
  let allCoordinates: [number, number][] = [];
110
+
182
111
  routes.forEach((route, index) => {
183
112
  const coordinates: [number, number][] = [];
184
- route.steps.forEach((step) => {
113
+ route.steps.forEach(step => {
185
114
  const latLngs = parseWKTLineString(step.geometry);
186
115
  if (latLngs.length > 0) coordinates.push(...latLngs);
187
116
  });
188
117
 
189
118
  if (coordinates.length > 0) {
190
119
  const color = index === selectedRouteIndex ? 'green' : 'black';
191
- const weight = index === selectedRouteIndex ? 5 : 3;
192
- const opacity = index === selectedRouteIndex ? 1.0 : 0.5;
193
-
194
- const polyline = L.polyline(coordinates, { color, weight, opacity })
120
+ const polyline = L.polyline(coordinates, { color, weight: 4, opacity: 0.8 })
195
121
  .addTo(routeLayerRef.current!)
196
- .on('click', (e: L.LeafletMouseEvent) => {
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
-
122
+ .on('click', () => setSelectedRouteIndex(index));
220
123
  routePolylinesRef.current.push(polyline);
221
- allCoordinates = [...allCoordinates, ...coordinates];
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
- }
124
+ allCoordinates.push(...coordinates);
240
125
  }
241
126
  });
242
127
 
243
- if (allCoordinates.length > 0) {
244
- // Ajuster la vue pour montrer l'itinéraire, mais dans les limites du Cameroun
245
- const routeBounds = L.latLngBounds(allCoordinates);
246
- const paddedBounds = routeBounds.pad(0.1); // Ajouter un peu de marge
247
-
248
- // S'assurer que les bounds ajustées sont dans les limites du Cameroun
249
- const finalBounds = L.latLngBounds(
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');
128
+ // 🗺 Ajustement intelligent de la vue sans sortir du Cameroun
129
+ const routeBounds = L.latLngBounds(allCoordinates);
130
+ const mergedBounds = routeBounds.extend(CAMEROON_BOUNDS);
131
+ mapRef.current.fitBounds(mergedBounds, { padding: [30, 30] });
266
132
  } 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);
133
+ // 🌍 Recentrage sur le Cameroun si aucun itinéraire
134
+ mapRef.current.fitBounds(CAMEROON_BOUNDS, { animate: true });
270
135
  }
271
- }, [apiClient, userLocation, searchedPlace, routes, selectedRouteIndex, setSelectedRouteIndex]);
136
+ }, [routes, selectedRouteIndex]);
272
137
 
273
- return <div className="w-full h-screen" ref={mapContainerRef} />;
138
+ return (
139
+ <div
140
+ ref={mapContainerRef}
141
+ className="w-full h-screen rounded-xl overflow-hidden shadow-md"
142
+ />
143
+ );
274
144
  };
275
145
 
276
- export default MapView;
146
+ export default MapView;