@@ -61,44 +61,44 @@ bun add @tracktor/map
6161import { MapProvider , MarkerMap } from " @tracktor/map" ;
6262
6363const markers = [
64- {
65- id: 1 ,
66- lng: 2.3522 ,
67- lat: 48.8566 ,
68- Tooltip: <div >Paris</div >,
69- color: " primary" ,
70- variant: " default" ,
71- },
72- {
73- id: 2 ,
74- lng: - 0.1276 ,
75- lat: 51.5074 ,
76- Tooltip: <div >London</div >,
77- color: " secondary" ,
78- variant: " default" ,
79- },
64+ {
65+ id: 1 ,
66+ lng: 2.3522 ,
67+ lat: 48.8566 ,
68+ Tooltip: <div >Paris</div >,
69+ color: " primary" ,
70+ variant: " default" ,
71+ },
72+ {
73+ id: 2 ,
74+ lng: - 0.1276 ,
75+ lat: 51.5074 ,
76+ Tooltip: <div >London</div >,
77+ color: " secondary" ,
78+ variant: " default" ,
79+ },
8080];
8181
8282function App() {
83- return (
84- <MapProvider
85- licenseMuiX = " your-muix-license"
86- licenceMapbox = " your-mapbox-token"
87- >
88- <MarkerMap
89- markers = { markers }
90- center = { [2.3522 , 48.8566 ]}
91- zoom = { 5 }
92- fitBounds
93- height = { 500 }
94- width = " 100%"
95- onMapClick = { (lng , lat , marker ) => {
96- console .log (" Clicked at:" , lng , lat );
97- if (marker ) console .log (" Marker clicked:" , marker );
98- }}
99- />
100- </MapProvider >
101- );
83+ return (
84+ <MapProvider
85+ licenseMuiX = " your-muix-license"
86+ licenceMapbox = " your-mapbox-token"
87+ >
88+ <MarkerMap
89+ markers = { markers }
90+ center = { [2.3522 , 48.8566 ]}
91+ zoom = { 5 }
92+ fitBounds
93+ height = { 500 }
94+ width = " 100%"
95+ onMapClick = { (lng , lat , marker ) => {
96+ console .log (" Clicked at:" , lng , lat );
97+ if (marker ) console .log (" Marker clicked:" , marker );
98+ }}
99+ />
100+ </MapProvider >
101+ );
102102}
103103```
104104
@@ -115,10 +115,10 @@ Wraps your map components and injects required providers (theme, tokens, MUI X l
115115- ` licenceMapbox ` — Your Mapbox access token
116116``` tsx
117117<MapProvider
118- licenseMuiX = " your-license"
119- licenceMapbox = " your-token"
118+ licenseMuiX = " your-license"
119+ licenceMapbox = " your-token"
120120>
121- { /* Your map components */ }
121+ { /* Your map components */ }
122122</MapProvider >
123123```
124124
@@ -147,7 +147,7 @@ Main map component that handles:
147147
148148| Prop | Type | Default | Description |
149149| ------| ------| ---------| -------------|
150- | ` center ` | ` [lng, lat ]` | ` [2.3522, 48.8566] ` | Initial map center coordinates |
150+ | ` center ` | ` LngLatLike \| number[ ]` | ` [2.3522, 48.8566] ` | Initial map center coordinates [ lng, lat ] |
151151| ` zoom ` | ` number ` | ` 5 ` | Initial zoom level (0-22) |
152152| ` width ` | ` string \| number ` | ` "100%" ` | Map container width |
153153| ` height ` | ` string \| number ` | ` 300 ` | Map container height |
@@ -170,13 +170,14 @@ Main map component that handles:
170170| ------| ------| ---------| -------------|
171171| ` cooperativeGestures ` | ` boolean ` | ` true ` | Require modifier key for zoom/pan |
172172| ` doubleClickZoom ` | ` boolean ` | ` true ` | Enable double-click to zoom |
173- | ` onMapClick ` | ` (lng, lat, marker?) => void ` | - | Callback for map clicks |
173+ | ` onMapClick ` | ` (lng, lat, marker?) => void ` | - | Callback for map clicks (includes clicked marker if applicable) |
174174
175175#### Marker Props
176176
177177| Prop | Type | Default | Description |
178178| ------| ------| ---------| -------------|
179179| ` markers ` | ` MarkerProps[] ` | ` [] ` | Array of markers to display |
180+ | ` markerImageURL ` | ` string ` | - | Custom marker icon URL |
180181| ` openPopup ` | ` string \| number ` | ` undefined ` | ID of marker with open popup |
181182| ` openPopupOnHover ` | ` boolean ` | ` false ` | Open popups on hover instead of click |
182183| ` popupMaxWidth ` | ` string ` | ` "300px" ` | Maximum popup width |
@@ -222,76 +223,104 @@ const marker = {
222223
223224---
224225
225- ### Routing Props
226+ ### Itinerary Props ( ` itineraryParams ` )
226227
227- Add a route between two points by providing ` from ` and ` to ` coordinates .
228+ Draw a route between two points with customizable styling and routing engines .
228229
229230| Prop | Type | Default | Description |
230231| ------| ------| ---------| -------------|
231- | ` from ` | ` [lng, lat ] ` | - | Route starting point |
232- | ` to ` | ` [lng, lat ] ` | - | Route ending point |
232+ | ` from ` | ` [number, number ] ` | - | Route starting point [ lng, lat ] |
233+ | ` to ` | ` [number, number ] ` | - | Route ending point [ lng, lat ] |
233234| ` profile ` | ` "driving" \| "walking" \| "cycling" ` | ` "driving" ` | Transportation mode |
234235| ` engine ` | ` "OSRM" \| "Mapbox" ` | ` "OSRM" ` | Routing service to use |
235- | ` itineraryLineStyle ` | ` { color, width, opacity } ` | ` { color: "#3b82f6", width: 4, opacity: 0.8 } ` | Route line appearance |
236+ | ` itineraryLineStyle ` | ` Partial<ItineraryLineStyle> ` | ` { color: "#3b82f6", width: 4, opacity: 0.8 } ` | Route line appearance |
237+ | ` initialRoute ` | ` Feature<LineString> ` | - | Precomputed GeoJSON route |
238+ | ` onRouteComputed ` | ` (route) => void ` | - | Callback fired when route is computed |
239+ | ` itineraryLabel ` | ` ReactNode ` | - | Label displayed along the route (e.g., "12 min") |
236240
237241** Example:**
238242``` tsx
239243<MapView
240- from = { [2.3522 , 48.8566 ]} // Paris
241- to = { [- 0.1276 , 51.5074 ]} // London
242- profile = " driving"
243- engine = " OSRM"
244- itineraryLineStyle = { {
245- color: " #10b981" ,
246- width: 5 ,
247- opacity: 0.9
244+ itineraryParams = { {
245+ from: [2.3522 , 48.8566 ], // Paris
246+ to: [- 0.1276 , 51.5074 ], // London
247+ profile: " driving" ,
248+ engine: " OSRM" ,
249+ itineraryLineStyle: {
250+ color: " #10b981" ,
251+ width: 5 ,
252+ opacity: 0.9
253+ },
254+ itineraryLabel: <span >Route principale</span >,
255+ onRouteComputed : (route ) => {
256+ console .log (" Route computed:" , route );
257+ }
248258 }}
249259/>
250260```
251261
252262---
253263
254- ### Nearest Marker Search
264+ ### Nearest Marker Search ( ` findNearestMarker ` )
255265
256266Find and highlight the closest marker to a given point within a maximum distance.
257267
258- | Prop | Type | Description |
259- | ------| ------| -------------|
260- | ` findNearestMarker.origin ` | ` [lng, lat] ` | Starting point for search |
261- | ` findNearestMarker.destinations ` | ` Array<{lng, lat, id}> ` | Candidate destinations |
262- | ` findNearestMarker.maxDistanceMeters ` | ` number ` | Maximum search radius |
263- | ` onNearestFound ` | ` (id, coords, distance) => void ` | Callback with nearest result |
268+ | Prop | Type | Default | Description |
269+ | ------| ------| ---------| -------------|
270+ | ` origin ` | ` [number, number] ` | - | Starting point for search [ lng, lat] |
271+ | ` destinations ` | ` Array<{id, lng, lat}> ` | - | Candidate destinations |
272+ | ` maxDistanceMeters ` | ` number ` | - | Maximum search radius in meters |
273+ | ` profile ` | ` "driving" \| "walking" \| "cycling" ` | ` "driving" ` | Routing profile for distance calculation |
274+ | ` engine ` | ` "OSRM" \| "Mapbox" ` | ` "OSRM" ` | Routing engine to use |
275+ | ` onNearestFound ` | ` (results) => void ` | - | Callback with all nearest results |
276+ | ` initialNearestResults ` | ` NearestResult[] ` | - | Precomputed nearest results |
277+ | ` itineraryLineStyle ` | ` Partial<ItineraryLineStyle> ` | - | Style override for auto-generated itinerary |
278+
279+ ** NearestResult Type:**
280+ ``` tsx
281+ interface NearestResult {
282+ id: number | string ;
283+ point: [number , number ]; // [lng, lat]
284+ distance: number ; // in meters
285+ routeFeature? : Feature <LineString > | null ;
286+ }
287+ ```
264288
265289** Example:**
266290``` tsx
267291<MapView
268292 findNearestMarker = { {
269293 origin: [2.3522 , 48.8566 ],
270294 destinations: markers .map (m => ({
295+ id: m .id ,
271296 lng: m .lng ,
272- lat: m .lat ,
273- id: m .id
297+ lat: m .lat
274298 })),
275299 maxDistanceMeters: 5000 ,
276- }}
277- onNearestFound = { (id , coords , distance ) => {
278- console .log (` Nearest: ${id } at ${distance }m ` );
300+ profile: " walking" ,
301+ engine: " OSRM" ,
302+ onNearestFound : (results ) => {
303+ console .log (` Found ${results .length } markers within range ` );
304+ results .forEach (r => {
305+ console .log (` Marker ${r .id } at ${r .distance }m ` );
306+ });
307+ }
279308 }}
280309/>
281310```
282311
283312---
284313
285- ### Isochrone Props
314+ ### Isochrone Props ( ` isochrone ` )
286315
287316Compute and display areas reachable within specific time intervals.
288317
289- | Prop | Type | Description |
290- | ------| ------| -------------|
291- | ` isochrone. origin` | ` [lng, lat ] ` | Center point for isochrone |
292- | ` isochrone. profile` | ` "driving" \| "walking" \| "cycling" ` | Transportation mode |
293- | ` isochrone. intervals` | ` number[] ` | Time intervals in minutes |
294- | ` isochrone. onIsochroneLoaded` | ` (data) => void ` | Callback with GeoJSON result |
318+ | Prop | Type | Default | Description |
319+ | ------| ------| ---------| --------- ----|
320+ | ` origin ` | ` [number, number ] ` | - | Center point for isochrone [ lng, lat ] |
321+ | ` profile ` | ` "driving" \| "walking" \| "cycling" ` | ` "driving "` | Transportation mode |
322+ | ` intervals ` | ` number[] ` | ` [5, 10, 15 ]` | Time intervals in minutes |
323+ | ` onIsochroneLoaded ` | ` (data) => void ` | - | Callback with GeoJSON result |
295324
296325** Example:**
297326``` tsx
@@ -309,7 +338,7 @@ Compute and display areas reachable within specific time intervals.
309338
310339---
311340
312- ### GeoJSON Features
341+ ### GeoJSON Features ( ` features ` )
313342
314343Display custom vector features like polygons, lines, or points.
315344
@@ -472,6 +501,44 @@ function InteractiveMap() {
472501}
473502```
474503
504+ ### 🚗 Combined Routing & Nearest Search
505+ ``` tsx
506+ function DeliveryMap() {
507+ const [origin] = useState ([2.3522 , 48.8566 ]);
508+ const [destinations] = useState ([
509+ { id: 1 , lng: 2.35 , lat: 48.86 },
510+ { id: 2 , lng: 2.36 , lat: 48.85 },
511+ { id: 3 , lng: 2.34 , lat: 48.87 }
512+ ]);
513+
514+ return (
515+ <MapView
516+ markers = { destinations .map (d => ({
517+ id: d .id ,
518+ lng: d .lng ,
519+ lat: d .lat ,
520+ Tooltip: <div >Destination { d .id } </div >
521+ }))}
522+ findNearestMarker = { {
523+ origin ,
524+ destinations ,
525+ maxDistanceMeters: 10000 ,
526+ profile: " driving" ,
527+ engine: " OSRM" ,
528+ itineraryLineStyle: {
529+ color: " #22c55e" ,
530+ width: 4 ,
531+ opacity: 0.8
532+ },
533+ onNearestFound : (results ) => {
534+ console .log (" Nearest destinations:" , results );
535+ }
536+ }}
537+ />
538+ );
539+ }
540+ ```
541+
475542---
476543
477544## 💡 Tips & Best Practices
@@ -482,20 +549,23 @@ function InteractiveMap() {
482549- ** Use ` fitBoundsAnimationKey ` ** to control when bounds recalculate
483550- ** Disable animations** for large datasets: ` disableAnimation={true} `
484551- ** Debounce dynamic updates** when tracking real-time data
552+ - ** Use ` initialRoute ` and ` initialNearestResults ` ** to avoid redundant API calls
485553
486554### UX Improvements
487555
488556- Combine ` openPopupOnHover ` and ` disableAnimation ` for smooth interactions
489557- Use ` fitBoundsPadding ` to ensure markers aren't at screen edges
490558- Set appropriate ` popupMaxWidth ` for mobile responsiveness
491559- Provide visual feedback with custom ` IconComponent ` states
560+ - Use ` itineraryLabel ` to display route duration or distance
492561
493562### Routing Best Practices
494563
495564- ** Use OSRM** (free) for basic routing needs
496565- ** Use Mapbox** for production apps requiring SLA and support
497- - Cache route results to minimize API calls
498- - Handle network errors gracefully
566+ - Cache route results with ` initialRoute ` to minimize API calls
567+ - Handle network errors gracefully with ` onRouteComputed ` callback
568+ - Combine ` findNearestMarker ` with ` itineraryParams ` for optimal routing workflows
499569
500570---
501571
@@ -647,10 +717,17 @@ This will:
647717- Reduce marker count or use clustering
648718- Disable animations for large datasets
649719- Memoize marker data
720+ - Use ` initialRoute ` and ` initialNearestResults ` for cached data
721+
722+ ** Routing not working:**
723+ - Verify coordinates are in [ lng, lat] format (not lat, lng)
724+ - Check that routing engine is accessible
725+ - Ensure profile matches your use case
726+ - Verify maxDistanceMeters is reasonable for nearest search
650727
651728### Getting Help
652729
653- - 📖 Check the [ documentation] ( https://tracktor. github.io /map )
730+ - 📖 Check the [ documentation] ( https://github.com/Tracktor /map )
654731- 🐛 [ Report bugs] ( https://github.com/tracktor-tech/tracktor-map/issues )
655732- 💬 Join discussions in GitHub Discussions
656733
@@ -666,9 +743,10 @@ This will:
666743## 🧭 Links
667744
668745- 📦 ** npm** : [ @tracktor/map ] ( https://www.npmjs.com/package/@tracktor/map )
669- - 💻 ** GitHub** : [ tracktor-tech/tracktor- map] ( https://github.com/tracktor-tech/tracktor- map )
746+ - 💻 ** GitHub** : [ @ tracktor/ map ] ( https://github.com/Tracktor/ map )
670747- 🌐 ** Docs** : [ tracktor.github.io/map] ( https://tracktor.github.io/map )
671748- 🎨 ** Design System** : [ @tracktor/design-system ] ( https://www.npmjs.com/package/@tracktor/design-system )
749+ - Sandbox Demo: [ tracktor.github.io/map/sandbox] ( https://tracktor.github.io/map )
672750
673751---
674752
0 commit comments