https://github.com/tracktor/map
https://github.com/tracktor/map
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/tracktor/map
- Owner: Tracktor
- License: isc
- Created: 2025-04-09T09:39:22.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-07-02T16:09:38.000Z (12 months ago)
- Last Synced: 2025-07-02T17:26:01.957Z (12 months ago)
- Language: TypeScript
- Size: 1.15 MB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# πΊοΈ @tracktor/map
A modern, lightweight React map library built on top of Mapbox GL JS and react-map-gl. Designed for simplicity, flexibility, and visual elegance.
Easily combine markers, routes, GeoJSON features, isochrones, and nearest-point calculations β all with a declarative, type-safe API.
[](https://www.npmjs.com/package/@tracktor/map)
[](LICENSE)
---
## π Installation
```bash
npm install @tracktor/map
```
or
```bash
yarn add @tracktor/map
```
or
```bash
bun add @tracktor/map
```
---
## βοΈ Requirements
| Dependency | Version | Purpose |
|------------|---------|---------|
| `react` | 17+ / 18+ / 19+ | Core React runtime |
| `react-dom` | 17+ / 18+ / 19+ | React DOM rendering |
| `mapbox-gl` | β₯3.0.0 | Map rendering engine |
| `@tracktor/design-system` | β₯4.0.0 | UI theming and components |
| `@mui/icons-material` | * | Material UI icons |
| `@mui/x-license` | * | MUI X license integration |
πͺΆ **You'll also need a Mapbox access token** to render maps. Get one at [mapbox.com](https://account.mapbox.com/access-tokens/).
---
## β¨ Features
β
**Declarative API** β manage complex map interactions with simple props
β
**Markers & Popups** β customizable React components or image-based icons
β
**Routing & Isochrones** β visualize travel-time areas or compute optimal routes
β
**GeoJSON Layers** β render vector data dynamically
β
**Nearest Marker Search** β find and highlight closest points instantly
β
**Type-safe API** β full TypeScript support with smart IntelliSense
β
**Responsive Design** β automatically adapts to any container or screen size
β
**Built for performance** β minimal re-renders, efficient map updates
β
**Multiple Routing Engines** β supports both OSRM (free) and Mapbox Directions API
β
**Flexible Map Styles** β works with Mapbox styles, OpenStreetMap, or custom raster tiles
---
## π§© Quick Start
```tsx
import { MapProvider, MarkerMap } from "@tracktor/map";
const markers = [
{
id: 1,
lng: 2.3522,
lat: 48.8566,
Tooltip:
Paris,
color: "primary",
variant: "default",
},
{
id: 2,
lng: -0.1276,
lat: 51.5074,
Tooltip: London,
color: "secondary",
variant: "default",
},
];
function App() {
return (
{
console.log("Clicked at:", lng, lat);
if (marker) console.log("Marker clicked:", marker);
}}
/>
);
}
```
---
## π§ Components Overview
### `MapProvider`
Wraps your map components and injects required providers (theme, tokens, MUI X license).
**Required Props:**
- `licenseMuiX` β Your MUI X license key
- `licenceMapbox` β Your Mapbox access token
```tsx
{/* Your map components */}
```
### `MapView` / `MarkerMap`
Main map component that handles:
- Marker rendering with custom icons or React components
- Interactive popups with hover/click modes
- Automatic bounds fitting
- Map click events
- Responsive container sizing
### Specialized Components
- **`RouteMap`** β Draw routes between two points using OSRM or Mapbox
- **`IsochroneMap`** β Compute and display travel-time polygons
- **`FeatureMap`** β Display custom GeoJSON layers with styling
---
## π§± Props Reference
### `MapView` Props
#### Core Map Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `center` | `LngLatLike \| number[]` | `[2.3522, 48.8566]` | Initial map center coordinates [lng, lat] |
| `zoom` | `number` | `5` | Initial zoom level (0-22) |
| `width` | `string \| number` | `"100%"` | Map container width |
| `height` | `string \| number` | `300` | Map container height |
| `loading` | `boolean` | `false` | Show skeleton loader |
| `square` | `boolean` | `false` | Enforce 1:1 aspect ratio |
| `containerStyle` | `SxProps` | `undefined` | Custom MUI sx styles |
#### Map Appearance
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `theme` | `"light" \| "dark"` | `"light"` | Map color theme |
| `baseMapView` | `"street" \| "satellite"` | `"street"` | Base map layer type |
| `mapStyle` | `string` | - | Custom Mapbox style URL |
| `projection` | `ProjectionSpecification` | `"mercator"` | Map projection system |
#### Interaction Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `cooperativeGestures` | `boolean` | `true` | Require modifier key for zoom/pan |
| `doubleClickZoom` | `boolean` | `true` | Enable double-click to zoom |
| `onMapClick` | `(lng, lat, marker?) => void` | - | Callback for map clicks (includes clicked marker if applicable) |
#### Marker Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `markers` | `MarkerProps[]` | `[]` | Array of markers to display |
| `markerImageURL` | `string` | - | Custom marker icon URL |
| `openPopup` | `string \| number` | `undefined` | ID of marker with open popup |
| `openPopupOnHover` | `boolean` | `false` | Open popups on hover instead of click |
| `popupMaxWidth` | `string` | `"300px"` | Maximum popup width |
#### Bounds & Animation
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `fitBounds` | `boolean` | `true` | Auto-fit map to show all markers |
| `fitBoundsPadding` | `number` | `0` | Padding (px) around fitted bounds |
| `fitBoundDuration` | `number` | `500` | Animation duration (ms) |
| `disableAnimation` | `boolean` | `false` | Disable all animations |
| `fitBoundsAnimationKey` | `unknown` | - | Change to re-trigger fit bounds |
---
### Marker Props (`MarkerProps`)
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `id` | `string \| number` | β
| Unique marker identifier |
| `lng` | `number` | β
| Longitude coordinate |
| `lat` | `number` | β
| Latitude coordinate |
| `Tooltip` | `ReactNode` | - | Content for popup/tooltip |
| `IconComponent` | `React.ComponentType` | - | Custom React icon component |
| `iconProps` | `object` | - | Props passed to IconComponent |
| `color` | `string` | - | Marker color (MUI palette) |
| `variant` | `string` | - | Marker style variant |
**Example with custom icon:**
```tsx
import LocationOnIcon from '@mui/icons-material/LocationOn';
const marker = {
id: 1,
lng: 2.3522,
lat: 48.8566,
IconComponent: LocationOnIcon,
iconProps: { fontSize: 'large', color: 'error' },
Tooltip:
Custom Icon Marker
};
```
---
### Itinerary Props (`itineraryParams`)
Draw a route between two points with customizable styling and routing engines.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `from` | `[number, number]` | - | Route starting point [lng, lat] |
| `to` | `[number, number]` | - | Route ending point [lng, lat] |
| `profile` | `"driving" \| "walking" \| "cycling"` | `"driving"` | Transportation mode |
| `engine` | `"OSRM" \| "Mapbox"` | `"OSRM"` | Routing service to use |
| `itineraryLineStyle` | `Partial` | `{ color: "#3b82f6", width: 4, opacity: 0.8 }` | Route line appearance |
| `initialRoute` | `Feature` | - | Precomputed GeoJSON route |
| `onRouteComputed` | `(route) => void` | - | Callback fired when route is computed |
| `itineraryLabel` | `ReactNode` | - | Label displayed along the route (e.g., "12 min") |
**Example:**
```tsx
Route principale,
onRouteComputed: (route) => {
console.log("Route computed:", route);
}
}}
/>
```
---
### Nearest Marker Search (`findNearestMarker`)
Find and highlight the closest marker to a given point within a maximum distance.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `origin` | `[number, number]` | - | Starting point for search [lng, lat] |
| `destinations` | `Array<{id, lng, lat}>` | - | Candidate destinations |
| `maxDistanceMeters` | `number` | - | Maximum search radius in meters |
| `profile` | `"driving" \| "walking" \| "cycling"` | `"driving"` | Routing profile for distance calculation |
| `engine` | `"OSRM" \| "Mapbox"` | `"OSRM"` | Routing engine to use |
| `onNearestFound` | `(results) => void` | - | Callback with all nearest results |
| `initialNearestResults` | `NearestResult[]` | - | Precomputed nearest results |
| `itineraryLineStyle` | `Partial` | - | Style override for auto-generated itinerary |
**NearestResult Type:**
```tsx
interface NearestResult {
id: number | string;
point: [number, number]; // [lng, lat]
distance: number; // in meters
routeFeature?: Feature | null;
}
```
**Example:**
```tsx
({
id: m.id,
lng: m.lng,
lat: m.lat
})),
maxDistanceMeters: 5000,
profile: "walking",
engine: "OSRM",
onNearestFound: (results) => {
console.log(`Found ${results.length} markers within range`);
results.forEach(r => {
console.log(`Marker ${r.id} at ${r.distance}m`);
});
}
}}
/>
```
---
### Isochrone Props (`isochrone`)
Compute and display areas reachable within specific time intervals.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `origin` | `[number, number]` | - | Center point for isochrone [lng, lat] |
| `profile` | `"driving" \| "walking" \| "cycling"` | `"driving"` | Transportation mode |
| `intervals` | `number[]` | `[5, 10, 15]` | Time intervals in minutes |
| `onIsochroneLoaded` | `(data) => void` | - | Callback with GeoJSON result |
**Example:**
```tsx
{
console.log("Isochrone data:", geojson);
}
}}
/>
```
---
### GeoJSON Features (`features`)
Display custom vector features like polygons, lines, or points.
| Prop | Type | Description |
|------|------|-------------|
| `features` | `Feature \| Feature[] \| FeatureCollection` | GeoJSON data to render |
**Example:**
```tsx
```
---
## π§ Advanced Use Cases
### π§ Real-time GPS Tracking
```tsx
function LiveTracking() {
const [position, setPosition] = useState([2.3522, 48.8566]);
useEffect(() => {
const watchId = navigator.geolocation.watchPosition((pos) => {
setPosition([pos.coords.longitude, pos.coords.latitude]);
});
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return (
You are here
}]}
center={position}
zoom={15}
fitBounds={false}
/>
);
}
```
### π Dynamic Data with React Query
```tsx
import { useQuery } from '@tanstack/react-query';
function DynamicMarkers() {
const { data: markers } = useQuery({
queryKey: ['locations'],
queryFn: fetchLocations,
refetchInterval: 5000 // Refresh every 5s
});
return (
);
}
```
### π¨ Custom Marker Components
```tsx
function CustomMarker({ isActive, count }) {
return (
{count}
);
}
```
### πΊοΈ Multi-Route Comparison
```tsx
```
### π― Click-to-Add Markers
```tsx
function InteractiveMap() {
const [markers, setMarkers] = useState([]);
const handleMapClick = (lng, lat) => {
setMarkers(prev => [...prev, {
id: Date.now(),
lng,
lat,
Tooltip:
Point {markers.length + 1}
}]);
};
return (
);
}
```
### π Combined Routing & Nearest Search
```tsx
function DeliveryMap() {
const [origin] = useState([2.3522, 48.8566]);
const [destinations] = useState([
{ id: 1, lng: 2.35, lat: 48.86 },
{ id: 2, lng: 2.36, lat: 48.85 },
{ id: 3, lng: 2.34, lat: 48.87 }
]);
return (
({
id: d.id,
lng: d.lng,
lat: d.lat,
Tooltip:
Destination {d.id}
}))}
findNearestMarker={{
origin,
destinations,
maxDistanceMeters: 10000,
profile: "driving",
engine: "OSRM",
itineraryLineStyle: {
color: "#22c55e",
width: 4,
opacity: 0.8
},
onNearestFound: (results) => {
console.log("Nearest destinations:", results);
}
}}
/>
);
}
```
---
## π‘ Tips & Best Practices
### Performance Optimization
- **Memoize marker data** to prevent unnecessary re-renders
- **Use `fitBoundsAnimationKey`** to control when bounds recalculate
- **Disable animations** for large datasets: `disableAnimation={true}`
- **Debounce dynamic updates** when tracking real-time data
- **Use `initialRoute` and `initialNearestResults`** to avoid redundant API calls
### UX Improvements
- Combine `openPopupOnHover` and `disableAnimation` for smooth interactions
- Use `fitBoundsPadding` to ensure markers aren't at screen edges
- Set appropriate `popupMaxWidth` for mobile responsiveness
- Provide visual feedback with custom `IconComponent` states
- Use `itineraryLabel` to display route duration or distance
### Routing Best Practices
- **Use OSRM** (free) for basic routing needs
- **Use Mapbox** for production apps requiring SLA and support
- Cache route results with `initialRoute` to minimize API calls
- Handle network errors gracefully with `onRouteComputed` callback
- Combine `findNearestMarker` with `itineraryParams` for optimal routing workflows
---
## π§βπ» Development
### Prerequisites
- **Bun** β₯1.1.0 (recommended) or Node.js 18+
- Git
### Setup
```bash
# Clone the repository
git clone https://github.com/tracktor-tech/tracktor-map.git
cd tracktor-map
# Install dependencies
bun install
# Start development sandbox
bun run sandbox
# or
bun run dev:sandbox
```
### Available Scripts
| Command | Description |
|---------|-------------|
| `bun run sandbox` | Start interactive development playground |
| `bun run build` | Build library for production |
| `bun run build:sandbox` | Build sandbox demo site |
| `bun run deploy:sandbox` | Deploy sandbox to GitHub Pages |
| `bun run lint` | Check code quality and types |
| `bun run lint:fix` | Auto-fix linting issues |
| `bun run test` | Run test suite |
| `bun run test:watch` | Run tests in watch mode |
| `bun run version` | Bump version with changelog |
| `bun run release` | Build and publish to npm |
### Project Structure
```
@tracktor/map/
βββ src/
β βββ components/ # Reusable map and UI components (Marker, Popup, etc.)
β βββ constants/ # Shared configuration values and styling constants
β βββ context/ # React context providers (e.g. map state)
β βββ features/ # Core map features (routes, isochrones, nearest, etc.)
β βββ services/ # External APIs and utility services
β βββ types/ # TypeScript interfaces and types
β βββ utils/ # Generic helpers and formatting functions
β βββ main.ts # Library entry point
β
βββ sandbox/ # Development playground (example app & live demos)
β βββ context/ # Demo context providers
β βββ examples/ # Interactive usage examples
β βββ features/ # Components used in the docs/demo
β βββ public/ # Static assets (images, previews, etc.)
β βββ App.tsx # Sandbox root component
β βββ index.tsx # Sandbox entry file
β
βββ test/ # Unit and integration tests
```
### Testing
```bash
# Run all tests
bun test
# Watch mode
bun test:watch
# Run specific test file
bun test src/components/MapView.test.tsx
```
### Contributing
We welcome contributions! Please:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests and linting (`bun run test && bun run lint`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
**Code Style:**
- Follow existing patterns and conventions
- Use TypeScript for all new code
- Add tests for new features
- Update documentation as needed
---
## π Documentation & Examples
Explore interactive examples and comprehensive API documentation:
π **[Live Documentation & Sandbox](https://tracktor.github.io/map)**
The sandbox includes:
- Interactive code examples
- Live preview of all features
- Copy-paste ready snippets
- API reference with search
---
## π¦ Publishing & Deployment
### Publish to npm
```bash
# Update version and generate changelog
bun run version
# Build and publish
bun run release
```
### Deploy Sandbox to GitHub Pages
```bash
bun run deploy:sandbox
```
This will:
1. Build the sandbox with production optimizations
2. Generate a 404.html for client-side routing
3. Push to the `gh-pages` branch
4. Update the live documentation site
---
## π§ Troubleshooting
### Common Issues
**Map not rendering:**
- Verify your Mapbox token is valid
- Check browser console for errors
- Ensure mapbox-gl CSS is imported
**TypeScript errors:**
- Run `bun install` to update type definitions
- Check peer dependency versions match
**Performance issues:**
- Reduce marker count or use clustering
- Disable animations for large datasets
- Memoize marker data
- Use `initialRoute` and `initialNearestResults` for cached data
**Routing not working:**
- Verify coordinates are in [lng, lat] format (not lat, lng)
- Check that routing engine is accessible
- Ensure profile matches your use case
- Verify maxDistanceMeters is reasonable for nearest search
### Getting Help
- π Check the [documentation](https://github.com/Tracktor/map)
- π [Report bugs](https://github.com/tracktor-tech/tracktor-map/issues)
- π¬ Join discussions in GitHub Discussions
---
## π License
**UNLICENSED** β This package is proprietary software.
Β© [Tracktor β Kevin Graff]
---
## π§ Links
- π¦ **npm**: [@tracktor/map](https://www.npmjs.com/package/@tracktor/map)
- π» **GitHub**: [@tracktor/map](https://github.com/Tracktor/map)
- π **Docs**: [tracktor.github.io/map](https://tracktor.github.io/map)
- π¨ **Design System**: [@tracktor/design-system](https://www.npmjs.com/package/@tracktor/design-system)
- Sandbox Demo: [tracktor.github.io/map/sandbox](https://tracktor.github.io/map)
---
## π Acknowledgments
Built with:
- [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/) β Powerful map rendering
- [react-map-gl](https://visgl.github.io/react-map-gl/) β React wrapper for Mapbox
- [OSRM](http://project-osrm.org/) β Free routing engine
- [@tracktor/design-system](https://www.npmjs.com/package/@tracktor/design-system) β UI components