# CLAUDE
Source: https://maps.solvice.io/CLAUDE
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is the documentation website for Solvice Maps API, built with Mintlify. The project contains API documentation, integration examples, and usage guides for Solvice's routing and mapping services.
## Development Commands
```bash theme={null}
# Install Mintlify CLI globally (required)
npm i -g mintlify
# Run local development server
mintlify dev
# Reinstall dependencies if needed
mintlify install
```
## Project Structure
The documentation is organized by API service:
* `/cube/` - Cube API (matrix routing) documentation
* `/table/` - Table API (distance matrix) documentation
* `/route/` - Route API (directions) documentation
* `/tiles/` - Map tile service documentation
* `/examples/` - Integration examples for Leaflet and MapLibre GL
* `/snippets/` - Reusable documentation components
## Key Files
* `docs.json` - Mintlify configuration defining navigation structure, theme, and API settings
* `openapi.yaml` - OpenAPI 3.0.3 specification for the Solvice Maps Routing API
* MDX files - Documentation content written in Markdown with JSX support
## Architecture Notes
1. **Documentation Framework**: Uses Mintlify which renders MDX files into a modern docs site
2. **API Documentation**: Powered by OpenAPI spec with interactive playground enabled
3. **No Build Process**: Mintlify handles all compilation; no webpack/vite config needed
4. **Navigation Structure**: Two main tabs defined in docs.json - "Routing" and "Tiles"
## Working with Documentation
* All documentation files use MDX format (Markdown + JSX components)
* Images go in `/images/` directory
* API endpoint docs are auto-generated from `openapi.yaml`
* Navigation structure is controlled by the `docs.json` file
* Mintlify hot-reloads changes during development
## Git Workflow
* Main branch is `main`
* The project uses GitHub App for automatic deployment
* Always check for uncommitted changes before starting work
# Api services guide
Source: https://maps.solvice.io/api-services-guide
# Solvice Maps: API Services Guide
## API Architecture Overview
Solvice Maps provides a comprehensive suite of RESTful APIs designed for high-performance routing, distance calculations, and geospatial analysis. The API architecture is built around three core service categories, each optimized for specific use cases and performance requirements.
## Base URL and Authentication
**Production API Base URL:**\
`https://routing.solvice.io`
**Staging API Base URL:**\
`https://mapr-gateway-staging-181354976021.europe-west1.run.app`
**Authentication:**
All API requests require authentication via API key in the header:
```http theme={null}
X-API-Key: your-api-key-here
Content-Type: application/json
```
## Core API Services
### 1. Route API - Turn-by-Turn Directions
**Purpose:** Generate detailed turn-by-turn directions with geometry between two or more points.
#### Single Route Calculation
**Endpoint:** `POST /route`
**Request Body:**
```json theme={null}
{
"coordinates": [
[4.3517, 50.8503], // Brussels (longitude, latitude)
[2.3522, 48.8566] // Paris
],
"profile": "car",
"options": {
"steps": true,
"geometries": "geojson",
"overview": "full",
"continue_straight": true
}
}
```
**Response:**
```json theme={null}
{
"routes": [
{
"geometry": {
"type": "LineString",
"coordinates": [[4.3517, 50.8503], ...]
},
"legs": [
{
"distance": 264.1,
"duration": 87.9,
"steps": [
{
"distance": 50.3,
"duration": 12.1,
"geometry": {
"type": "LineString",
"coordinates": [[4.3517, 50.8503], ...]
},
"name": "Rue de la Loi",
"maneuver": {
"type": "turn",
"modifier": "left",
"location": [4.3517, 50.8503]
}
}
]
}
],
"distance": 264100,
"duration": 8790,
"weight": 8790
}
],
"waypoints": [
{
"hint": "...",
"location": [4.3517, 50.8503],
"name": "Rue de la Loi"
}
]
}
```
#### Batch Route Processing
**Endpoint:** `POST /route/batch`
**Request Body:**
```json theme={null}
{
"requests": [
{
"coordinates": [[4.3517, 50.8503], [2.3522, 48.8566]],
"profile": "car"
},
{
"coordinates": [[2.3522, 48.8566], [3.0686, 50.6365]],
"profile": "car"
}
],
"options": {
"steps": false,
"geometries": "geojson"
}
}
```
**Response:**
```json theme={null}
{
"routes": [
{
"success": true,
"route": {
"distance": 264100,
"duration": 8790,
"geometry": {...}
}
},
{
"success": true,
"route": {
"distance": 123400,
"duration": 5420,
"geometry": {...}
}
}
]
}
```
### 2. Table API - Distance Matrix
**Purpose:** Calculate travel times and distances between multiple origins and destinations.
#### Synchronous Table (Real-time)
**Endpoint:** `POST /table/sync`
**Use Case:** Small matrices requiring immediate response (\< 1000 coordinate pairs)
**Request Body:**
```json theme={null}
{
"sources": [
[4.3517, 50.8503], // Brussels
[2.3522, 48.8566], // Paris
[3.0686, 50.6365] // Lille
],
"destinations": [
[1.0952, 49.4431], // Rouen
[7.7521, 48.5734], // Strasbourg
[5.3698, 43.2965] // Marseille
],
"profile": "car",
"annotations": ["duration", "distance"]
}
```
**Response:**
```json theme={null}
{
"durations": [
[6420, 12300, 25200], // From Brussels
[4320, 9840, 22680], // From Paris
[5280, 11400, 24120] // From Lille
],
"distances": [
[324000, 567000, 1032000], // From Brussels (meters)
[256000, 487000, 923000], // From Paris
[298000, 534000, 978000] // From Lille
],
"sources": [
{"hint": "...", "location": [4.3517, 50.8503]},
{"hint": "...", "location": [2.3522, 48.8566]},
{"hint": "...", "location": [3.0686, 50.6365]}
],
"destinations": [
{"hint": "...", "location": [1.0952, 49.4431]},
{"hint": "...", "location": [7.7521, 48.5734]},
{"hint": "...", "location": [5.3698, 43.2965]}
]
}
```
#### Asynchronous Table (Large datasets)
**Endpoint:** `POST /table`
**Use Case:** Large matrices requiring background processing (> 1000 coordinate pairs)
**Request Body:**
```json theme={null}
{
"sources": [...], // Array of up to 10,000 coordinates
"destinations": [...], // Array of up to 10,000 coordinates
"profile": "car",
"annotations": ["duration", "distance"],
"engine": "osm",
"fallback_speed": 50.0
}
```
**Response:**
```json theme={null}
{
"id": "table_123456789",
"status": "IN_PROGRESS",
"created_at": "2024-01-15T10:30:00Z",
"estimated_completion": "2024-01-15T10:35:00Z",
"progress_url": "/table/123456789/progress",
"result_url": "/table/123456789/response"
}
```
#### Monitor Table Progress
**Endpoint:** `GET /table/{id}/progress`
**Response:**
```json theme={null}
{
"id": "table_123456789",
"status": "IN_PROGRESS",
"progress": {
"completed_requests": 45,
"total_requests": 100,
"percentage": 45.0,
"estimated_completion": "2024-01-15T10:33:00Z"
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:32:15Z"
}
```
#### Retrieve Table Results
**Endpoint:** `GET /table/{id}/response`
**Response:** Same format as synchronous table response, but potentially much larger.
**For very large responses (> 10MB):**
**Endpoint:** `GET /table/{id}/response/signed-url`
**Response:**
```json theme={null}
{
"signed_url": "https://storage.googleapis.com/mapr-results/table_123456789.json?X-Goog-Algorithm=...",
"expires_at": "2024-01-15T11:30:00Z",
"size_bytes": 52428800,
"content_type": "application/json"
}
```
### 3. Cube API - Time-Dependent Travel Matrix
**Purpose:** Generate travel time matrices across multiple time periods throughout the day.
#### Create Cube Request
**Endpoint:** `POST /cube`
**Request Body:**
```json theme={null}
{
"sources": [
[4.3517, 50.8503],
[2.3522, 48.8566]
],
"destinations": [
[1.0952, 49.4431],
[7.7521, 48.5734],
[5.3698, 43.2965]
],
"profile": "car",
"time_slices": [
{"slice": 0, "time": "06:00"},
{"slice": 1, "time": "08:00"},
{"slice": 2, "time": "10:00"},
{"slice": 3, "time": "12:00"},
{"slice": 4, "time": "14:00"},
{"slice": 5, "time": "16:00"},
{"slice": 6, "time": "18:00"},
{"slice": 7, "time": "20:00"}
],
"day_type": "weekday",
"generate_polynomials": true
}
```
**Response:**
```json theme={null}
{
"id": "cube_987654321",
"status": "IN_PROGRESS",
"created_at": "2024-01-15T10:30:00Z",
"time_slices": 8,
"sources_count": 2,
"destinations_count": 3,
"total_tables": 8,
"estimated_completion": "2024-01-15T10:45:00Z",
"progress_url": "/cube/987654321/progress",
"result_url": "/cube/987654321/response"
}
```
#### Monitor Cube Progress
**Endpoint:** `GET /cube/{id}/progress`
**Response:**
```json theme={null}
{
"id": "cube_987654321",
"status": "IN_PROGRESS",
"progress": {
"completed_tables": 3,
"total_tables": 8,
"percentage": 37.5,
"current_slice": 3,
"estimated_completion": "2024-01-15T10:42:00Z"
},
"table_progress": [
{"slice": 0, "status": "SUCCEEDED", "duration": 45.2},
{"slice": 1, "status": "SUCCEEDED", "duration": 52.1},
{"slice": 2, "status": "SUCCEEDED", "duration": 48.7},
{"slice": 3, "status": "IN_PROGRESS", "progress": 0.6},
{"slice": 4, "status": "PENDING"},
{"slice": 5, "status": "PENDING"},
{"slice": 6, "status": "PENDING"},
{"slice": 7, "status": "PENDING"}
]
}
```
#### Retrieve Cube Results
**Endpoint:** `GET /cube/{id}/response`
**Response:**
```json theme={null}
{
"id": "cube_987654321",
"status": "SUCCEEDED",
"sources": [...],
"destinations": [...],
"time_slices": [
{
"slice": 0,
"time": "06:00",
"durations": [
[6420, 12300, 25200],
[4320, 9840, 22680]
],
"distances": [
[324000, 567000, 1032000],
[256000, 487000, 923000]
]
},
// ... additional time slices
],
"polynomials": {
"duration_coefficients": [
[
// Polynomial coefficients for source 0 → destination 0
[6420, 1200, -300, 50], // a₀ + a₁t + a₂t² + a₃t³
// Coefficients for source 0 → destination 1
[12300, 2400, -600, 100],
// ... more destinations
],
// ... more sources
]
},
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:43:22Z",
"processing_duration": 802.3
}
```
## Advanced Features
### 1. Request Splitting and Optimization
**Automatic Request Splitting:**
The API automatically splits large requests that exceed routing engine limits:
* **OSRM**: 1000 coordinates per request
* **TomTom**: 1000 coordinates per request
* **AnyMap**: 100 coordinates per request
* **Google Maps**: 1000 coordinates per request
**Split Strategy:**
```json theme={null}
// Original request: 2000 sources × 1500 destinations = 3M combinations
{
"sources": [...], // 2000 coordinates
"destinations": [...] // 1500 coordinates
}
// Automatically split into 6 child requests:
// Child 1: 1000 sources × 1000 destinations
// Child 2: 1000 sources × 500 destinations
// Child 3: 1000 sources × 1000 destinations
// Child 4: 1000 sources × 500 destinations
// etc.
```
### 2. Content-Based Caching
**Cache Key Generation:**
The system generates cache keys based on request content, enabling efficient deduplication:
```json theme={null}
// These requests will use the same cached result:
{
"sources": [[4.3517, 50.8503], [2.3522, 48.8566]],
"destinations": [[1.0952, 49.4431], [7.7521, 48.5734]],
"profile": "car"
}
{
"sources": [[2.3522, 48.8566], [4.3517, 50.8503]], // Different order
"destinations": [[7.7521, 48.5734], [1.0952, 49.4431]], // Different order
"profile": "car"
}
```
**Cache Benefits:**
* Immediate response for duplicate requests (\< 10ms)
* 60-80% cache hit rate for typical workloads
* Significant cost reduction for repeated calculations
### 3. Multi-Engine Support
**Engine Selection:**
```json theme={null}
{
"sources": [...],
"destinations": [...],
"engine": "osm", // Explicitly specify engine
"fallback_engine": "tomtom", // Fallback if primary fails
"profile": "car"
}
```
**Available Engines:**
* **`osm`**: OpenStreetMap/OSRM (free, good coverage)
* **`tomtom`**: TomTom API (commercial, real-time traffic)
* **`anymap`**: AnyMap service (European focus)
* **`google`**: Google Maps API (premium coverage)
### 4. Time-Dependent Routing
**Traffic Slice Selection:**
```json theme={null}
{
"sources": [...],
"destinations": [...],
"profile": "car",
"traffic_slice": 2.5, // Decimal slice (interpolated)
"day_type": "weekday" // "weekday" or "weekend"
}
```
**Traffic Slice Mapping:**
```
Slice 0: 06:00 (Early morning)
Slice 1: 07:00 (Morning commute start)
Slice 2: 08:00 (Peak morning traffic)
Slice 3: 09:00 (Late morning)
Slice 4: 10:00 (Mid-morning)
Slice 5: 11:00 (Pre-lunch)
Slice 6: 12:00 (Lunch hour)
Slice 7: 13:00 (Post-lunch)
Slice 8: 14:00 (Afternoon)
Slice 9: 15:00 (Pre-evening)
Slice 10: 16:00 (Evening commute start)
Slice 11: 17:00 (Peak evening traffic)
Slice 12: 18:00 (Late evening)
```
## Error Handling
### Standard Error Response Format
```json theme={null}
{
"error": {
"code": "INVALID_COORDINATES",
"message": "One or more coordinates are invalid or unreachable",
"details": {
"invalid_coordinates": [
{"index": 5, "coordinate": [0.0, 0.0], "reason": "Ocean location"}
]
},
"timestamp": "2024-01-15T10:30:00Z",
"request_id": "req_123456789"
}
}
```
### Common Error Codes
**Authentication Errors:**
* `INVALID_API_KEY`: API key is missing or invalid
* `RATE_LIMIT_EXCEEDED`: Request rate limit exceeded
* `QUOTA_EXCEEDED`: Monthly quota exceeded
**Request Errors:**
* `INVALID_REQUEST`: Malformed request body
* `INVALID_COORDINATES`: Invalid coordinate format or unreachable locations
* `REQUEST_TOO_LARGE`: Request exceeds maximum size limits
* `INVALID_PROFILE`: Unsupported transportation profile
**Processing Errors:**
* `ROUTING_ENGINE_ERROR`: External routing engine failure
* `TIMEOUT`: Request processing timeout
* `INTERNAL_ERROR`: Unexpected server error
**Resource Errors:**
* `RESOURCE_NOT_FOUND`: Requested table/cube ID not found
* `RESOURCE_EXPIRED`: Results have expired and been deleted
### Retry Logic
**Recommended Retry Strategy:**
```python theme={null}
import time
import random
def retry_with_backoff(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise e
if e.status_code in [429, 502, 503, 504]: # Retriable errors
backoff = (2 ** attempt) + random.uniform(0, 1)
time.sleep(backoff)
else:
raise e # Don't retry client errors
```
## Rate Limits and Quotas
### Request Rate Limits
**Standard Limits:**
* **Authenticated requests**: 1000 requests/minute
* **Table requests**: 100 requests/minute
* **Cube requests**: 10 requests/minute
**Enterprise Limits:**
* **Custom rate limits**: Configurable per customer
* **Burst allowance**: Handle traffic spikes
* **Priority queues**: Faster processing for enterprise customers
### Usage Quotas
**Monthly Quotas:**
* **Route calculations**: Unlimited for standard plans
* **Table requests**: Based on coordinate combinations
* **Storage**: 30-day retention for results
**Quota Headers:**
```http theme={null}
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1642251600
X-Quota-Limit: 1000000
X-Quota-Remaining: 876543
X-Quota-Reset: 1644843600
```
## Performance Optimization
### Request Optimization Tips
1. **Batch Similar Requests:**
```json theme={null}
// Instead of multiple single requests
POST /route (request 1)
POST /route (request 2)
// Use batch endpoint
POST /route/batch (both requests)
```
2. **Use Appropriate Endpoints:**
* Small tables (\< 100 coords): Use `/table/sync`
* Large tables (> 1000 coords): Use `/table` (async)
* Time analysis: Use `/cube` for multiple time periods
3. **Leverage Caching:**
```json theme={null}
// Order coordinates consistently for better cache hits
{
"sources": [[2.3522, 48.8566], [4.3517, 50.8503]], // Sorted
"destinations": [[1.0952, 49.4431], [7.7521, 48.5734]] // Sorted
}
```
4. **Choose Optimal Engines:**
* **Development/Testing**: Use `osm` (free)
* **Production (Europe)**: Use `anymap` for best accuracy
* **Global Coverage**: Use `google` or `tomtom`
* **Real-time Traffic**: Use `tomtom` or `google`
### Response Size Optimization
**Large Response Handling:**
* Responses > 10MB automatically use signed URLs
* Use compression for data transfer
* Consider pagination for very large datasets
**Selective Data Retrieval:**
```json theme={null}
{
"annotations": ["duration"], // Only duration, not distance
"geometries": "false", // Skip geometry data
"steps": false // Skip turn-by-turn steps
}
```
## SDK and Integration Examples
### JavaScript/Node.js
```javascript theme={null}
const SolviceMaps = require('@solvice/maps-sdk');
const client = new SolviceMaps({
apiKey: 'your-api-key',
baseURL: 'https://routing.solvice.io'
});
// Simple route
const route = await client.route.calculate({
coordinates: [[4.3517, 50.8503], [2.3522, 48.8566]],
profile: 'car',
steps: true
});
// Distance matrix
const table = await client.table.calculate({
sources: [[4.3517, 50.8503], [2.3522, 48.8566]],
destinations: [[1.0952, 49.4431], [7.7521, 48.5734]],
profile: 'car'
});
// Async table with polling
const largeTable = await client.table.calculateAsync({
sources: largeSourceArray,
destinations: largeDestinationArray,
profile: 'car'
});
// Wait for completion
const result = await client.table.waitForCompletion(largeTable.id, {
pollInterval: 5000, // 5 seconds
timeout: 300000 // 5 minutes
});
```
### Python
```python theme={null}
from solvice_maps import SolviceMapsClient
client = SolviceMapsClient(
api_key='your-api-key',
base_url='https://routing.solvice.io'
)
# Simple route
route = client.route.calculate(
coordinates=[[4.3517, 50.8503], [2.3522, 48.8566]],
profile='car',
steps=True
)
# Distance matrix
table = client.table.calculate(
sources=[[4.3517, 50.8503], [2.3522, 48.8566]],
destinations=[[1.0952, 49.4431], [7.7521, 48.5734]],
profile='car'
)
# Async table processing
large_table = client.table.calculate_async(
sources=large_source_array,
destinations=large_destination_array,
profile='car'
)
# Poll for results
result = client.table.wait_for_completion(
large_table.id,
poll_interval=5,
timeout=300
)
```
### cURL Examples
**Simple Route:**
```bash theme={null}
curl -X POST https://routing.solvice.io/route \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"coordinates": [[4.3517, 50.8503], [2.3522, 48.8566]],
"profile": "car",
"steps": true
}'
```
**Distance Matrix:**
```bash theme={null}
curl -X POST https://routing.solvice.io/table/sync \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"sources": [[4.3517, 50.8503], [2.3522, 48.8566]],
"destinations": [[1.0952, 49.4431], [7.7521, 48.5734]],
"profile": "car",
"annotations": ["duration", "distance"]
}'
```
**Check Progress:**
```bash theme={null}
curl -X GET https://routing.solvice.io/table/123456789/progress \
-H "X-API-Key: your-api-key"
```
This comprehensive API guide provides all the technical details needed to effectively integrate with and utilize the Solvice Maps routing services.
# Changelog
Source: https://maps.solvice.io/changelog
Powering route optimization systems with blazing fast distance matrix calculations
## Changelog
## Traffic Patterns on Real-time Routing
### New `includeTrafficPatterns` parameter
`TOMTOM_REAL_TIME` requests now combine live traffic with TomTom **historical
traffic patterns** by default. Patterns improve duration estimates outside peak
hours and cover road segments that have no live traffic data.
* Applies to `POST /route` and `POST /table/sync` with `"engine": "TOMTOM_REAL_TIME"`.
* **Default: enabled.** To compute against live traffic only, set
`"includeTrafficPatterns": false` on the request.
* Duration estimates are additionally calibrated against observed drive times.
## Real-time Traffic Routing
### New `TOMTOM_REAL_TIME` Engine
A new routing engine that reflects **live traffic conditions** at the moment of the request, powered by TomTom's real-time feed.
* Available on `POST /route` and `POST /table/sync`.
* `departureTime` is ignored — every request uses *now* as the departure time.
* Not supported on `POST /route/batch` or the async `POST /table` (real-time matrices are sync-only and not cacheable).
### Usage
Set `"engine": "TOMTOM_REAL_TIME"` on the request:
```json theme={null}
{
"coordinates": [
[4.35171, 50.85034],
[4.40269, 50.83712],
[4.44216, 50.63650]
],
"sources": [0, 1],
"annotations": ["duration"],
"engine": "TOMTOM_REAL_TIME"
}
```
### Matrix Constraints (`/table/sync`)
* `sources` is required and must be contiguous starting from `0`.
* The result has shape `sources × (coordinates − sources)` — sources first, destinations next; no square padding.
* `annotations` must not include `"distance"`; the real-time feed does not return distances.
* The `destinations` filter field is not supported.
* Billing is per actual destination count.
See the [Route](/route/intro) and [Matrix](/table/intro) introductions for full examples.
## New Engines, Synchronous Cube & Road Exclusions
### New Routing Engines
Support for additional routing engines beyond the default OSM-based engine:
* **Google** — leverage Google's routing for comparison or production use
* **Anymap** — Solvice's proprietary engine with advanced traffic modeling
* **Custom** — bring your own engine via a custom integration
### Synchronous `/cube/sync` Endpoint
A new synchronous cube endpoint for smaller, real-time cube requests — no polling required. Ideal for on-the-fly time-dependent matrix lookups.
### Road Type Exclusions
Fine-grained control over which road types to avoid:
* `toll` — avoid toll roads
* `motorway` — avoid motorways / highways
* `ferry` — avoid ferry crossings
* `tunnel` — avoid tunnels
* `bridge` — avoid bridges
### `departureTime` & `interpolate` Parameters
Route and table endpoints now accept `departureTime` to incorporate time-dependent traffic data, and an `interpolate` flag for smooth linear interpolation between traffic time slices.
### Richer OpenAPI Metadata
The OpenAPI specification has been updated with improved endpoint descriptions, contact information, and semantic tags for better discoverability and tooling support.
## Batch Route Processing & Performance Enhancements
### 🚀 New Batch Route Endpoint
Introducing the new `/route/batch` endpoint for processing multiple route requests efficiently in a single API call. This enables:
* Reduced API overhead for multiple route calculations
* Optimized network usage and faster processing
### ⚡ Performance Breakthroughs
Major performance improvements across all routing services:
* **Sub-50ms response times** for simple route calculations
* **Sub-quadratic scaling** for large distance matrices - up to 3,836 table cells processed per millisecond
* **Production-grade reliability** with 99.9% uptime SLA
### 🔄 Advanced Traffic Interpolation
Enhanced time-dependent routing with decimal slice support:
* **Granular traffic modeling** using decimal slices (e.g., 2.5, 4.7) for smooth transitions
* **Linear interpolation** between time periods for more accurate travel time predictions
* **Simplified architecture** reduces processing complexity while improving accuracy
### Demo page
Added a demo page to showcase the new features and performance improvements.
### 📊 Performance Scaling
```mermaid theme={null}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor': '#0f62fe', 'primaryTextColor': '#000000', 'primaryBorderColor': '#0f62fe', 'lineColor': '#000000', 'secondaryColor': '#0f62fe', 'tertiaryColor': '#ffffff', 'background': 'transparent', 'mainBkg': 'transparent', 'secondaryBkg': 'transparent', 'cScale0': 'transparent', 'cScale1': 'transparent', 'cScale2': 'transparent', 'xyChart': {'backgroundColor': 'transparent', 'plotColorPalette': '#0f62fe, #0f62fe, #0f62fe'}}}}%%
xychart-beta
title "Table Operation Performance Scaling"
x-axis ["10", "100", "1000"]
y-axis "Throughput (cells/ms)" 0 --> 4000
bar [17, 1060, 3836]
```
*Performance demonstrates exceptional sub-quadratic scaling: as matrix size increases 100x, throughput increases 225x*
## Introducing the `/cube` endpoint
We all know about distance matrices (our `/table` endpoint), but Route Optimization systems require multiple slices throughout the day.
Ideally one for every 5 minutes in the day in order to get the travel time as granular and accurately as possible.
However, if you would want to solve a 1000-jobs VRP request, you would need 288 matrices of 1000x1000 elements.
That would be quite cumbersome to first calculate and then secondly hold in memory during the solve.
That is why we are introducing a /cube endpoint to fetch the entire 3-D matrix or what we call cube. 288 slices is too much but what if we can approximate it with a function?
A polynomial that reduces the necessary slices. Read more in the [`/cube` introduction](cube/intro)
## Predictive traffic integration
Thanks to our partnership with TomTom, we can now provide predictive traffic information to our routing engine.
## Publicly launching Solvice Maps
Solvice Maps is now publicly available since December 2024. We believe that it is one of the most powerful and fastest routing engines available.
We're launching on OpenStreetMap data initially and will soon expand to other map data providers such as TomTom.
Next to the routing engine, we also provide a tile server that can integrate with any map application and framework such as Leaflet, Mapbox GL JS, OpenLayers, etc.
# /cube/{id}
Source: https://maps.solvice.io/cube/get-cube
GET /cube/{id}
Retrieve the status and metadata of a cube request by its ID.
# /cube/{id}/progress
Source: https://maps.solvice.io/cube/get-cube-progress
get /cube/{id}/progress
Get the computation progress of a cube request. Useful for tracking large matrix calculations.
After sending the cube request to the API, you can follow the progress in this endpoint.
The progress will list the created submatrices (children).
Average calculation time of matrix :
| Matrix Size | Calculation time |
| ------------- | ---------------- |
| `10x10` | 0.1s |
| `100x100` | 0.2s |
| `250x250` | 0.8s |
| `1000x1000` | 4.6s |
| `10000x10000` | 24.9s |
# /cube/{id}/response
Source: https://maps.solvice.io/cube/get-cube-response
GET /cube/{id}/response
Fetch the computed cube result containing polynomial coefficients for time-dependent travel times.
# /cube/{id}/response/url
Source: https://maps.solvice.io/cube/get-cube-response-signed-url
GET /cube/{id}/response/url
Get a signed URL that redirects to the cube response stored in Cloud Storage. Useful for large responses.
# Introduction
Source: https://maps.solvice.io/cube/intro
The Solvice Maps Distance Matrix service is specifically made for products that use route optimization to power scheduling operations.
The `/cube` endpoint provides access to Solvice's high-performance travel time prediction system. This endpoint is designed to support route optimization at scale, handling millions of requests in parallel with exceptional performance characteristics.
### Traffic Profile Processing
TomTom provides us with 1000 distinct traffic profiles. Each traffic profile represents a curve that measures travel time variations throughout the day in 288 time blocks (one block per 5 minutes).
In our graph representation, each way (road segment) is assigned a specific traffic profile pattern for different days of the week:
```
8|3|3|3|5|12|12
M T W T F S S
```
This notation indicates that way segments use profile #8 on Mondays, profile #3 on Tuesday through Thursday, profile #5 on Fridays, and profile #12 on weekends.
### Optimal Time Slices
Before fitting polynomials, we first identify the optimal time segments to model separately using the Pruned Exact Linear Time (PELT) changepoint detection algorithm. PELT is particularly well-suited for this task because:
1. It efficiently identifies significant changes in the pattern of travel times
2. It runs in linear time (O(n)) making it suitable for processing large datasets
3. It automatically determines the optimal number of changepoints
The PELT algorithm identifies critical transition points in the traffic profiles, such as:
* Morning rush hour start/end times
* Evening congestion periods
* Transitions between weekend and weekday patterns
Based on this analysis, we use 6 time slices per day type. A typical **weekday** uses:
* (19:30-6:30) - Night / free flow
* (6:30-9:30) - Morning rush hour
* (9:30-12:00) - Late morning
* (12:00-14:30) - Midday
* (14:30-16:30) - Afternoon
* (16:30-19:30) - Evening rush hour
**Weekend** days use a similar 6-slice structure with adjusted boundaries.
This approach allows us to:
1. Model each segment with a tailored polynomial fit
2. Reduce the complexity of each polynomial (fewer coefficients needed per slice)
3. Improve overall accuracy by focusing on homogeneous time periods
The changepoint detection results are visualized in our traffic profiles as red horizontal lines, showing where PELT has identified significant transitions in traffic patterns.
### Travel Time Curve Processing
The raw TomTom data shows how travel times fluctuate throughout the day, with characteristic patterns like:
* Morning rush hour peaks
* Midday plateaus
* Evening rush hour peaks
* Overnight free flow
Rather than storing 288 individual time values per profile per road segment (which would be inefficient), we apply polynomial fitting techniques to represent these curves mathematically.
## Implementation with Time Slices
### Polynomial Fitting Approach
We transform the raw time series data into polynomial coefficients using least squares regression. For a given route:
1. The original travel time data might look like: `(25, 28, 26, 30, 28, 25)` measured at 6 time slices throughout the day
2. We transform this into a polynomial equation: Y = c\_0 t^5 + c\_1 t^4 + c\_2 t^3 + c\_3 t^2 + c\_4 t + c\_5
3. This polynomial can then be evaluated for any time value `t` (where t is the time of day in hours, 0–24)
### Benefits of the Coefficient Method
This approach provides several key advantages:
* **Storage efficiency**: Instead of storing travel times for each time slice per source-destination pair, we store just 6 polynomial coefficients
* **Continuous representation**: We can calculate travel time for any arbitrary time of day, not just at the sampled time slices
* **Smoothed interpolation**: The polynomial curves filter out noise while preserving important patterns
* **Computational efficiency**: Evaluating a polynomial is extremely fast
## The /cube Endpoint
The `/cube` endpoint provides access to a 3D matrix (cube) of travel coefficients for route planning. This is our solution for efficiently handling time-dependent routing at massive scale.
### Available Endpoints
```
POST /cube Create a cube request
GET /cube/{id} Get cube status
GET /cube/{id}/progress Check processing progress (for polling)
GET /cube/{id}/response Fetch the cube response
GET /cube/{id}/response/url Get a signed URL for the response
```
### Usage Pattern
When you need to calculate travel time for a specific departure time:
1. Request a cube using `POST /cube` with a date (YYYY-MM-DD). The system selects 6 time slices based on the day of week (midweek or weekend).
2. Poll `GET /cube/{id}/progress` until processing is complete
3. Retrieve the data via `GET /cube/{id}/response`
4. With the default `responseType='coefficients'`, use the polynomial to calculate travel times:
Y = c\_0 t^5 + c\_1 t^4 + c\_2 t^3 + c\_3 t^2 + c\_4 t + c\_5 where `t` is the time of day in hours (0–24).
Alternatively, set `responseType='matrix'` to receive the raw travel time matrix for each of the 6 time slices.
### Request Format
The cube request follows the same format as the table endpoint, with an additional `responseType` parameter (`'coefficients'` or `'matrix'`).
### Response Format
With `responseType='coefficients'` (default), the response contains a 3D array where each element is a length-6 array of polynomial coefficients for calculating travel time between source-destination pairs at any time of day.
With `responseType='matrix'`, the response contains the raw duration matrices for each of the 6 time slices.
## Integration with Solvice Maps Platform
The `/cube` endpoint is part of our broader high-performance routing solution that includes:
* Multiple vehicle profiles (CAR, BIKE, TRUCK, ELECTRIC\_CAR, ELECTRIC\_BIKE)
* Traffic profile integration from TomTom
* Directional routing capability
* Synchronized distance matrix computation
All of these components leverage our advanced routing technology based on contraction hierarchies, which can be 10,000× faster than traditional Dijkstra's algorithm on continental-scale road networks.
# /cube
Source: https://maps.solvice.io/cube/post-cube
POST /cube
Request a time-dependent 3D distance matrix (cube) asynchronously. Returns a cube ID to poll status via /cube/{id} and fetch results via /cube/{id}/response. Use /cube/{id}/progress for progress on large matrices.
# /cube/sync
Source: https://maps.solvice.io/cube/sync-cube
POST /cube/sync
Request a cube synchronously. Returns the cube response directly instead of creating an asynchronous job.
# Introduction
Source: https://maps.solvice.io/examples/introduction
# Route
Source: https://maps.solvice.io/examples/routes/route
Polyline route example (ONLY Europe demo)
Fullscreen example [here](https://map-viewer.solvice.io)
Note: This demo is only linked to the European routing server.
# Leaflet
Source: https://maps.solvice.io/examples/tiles/leaflet/simple
Coming soon...
# Display marker
Source: https://maps.solvice.io/examples/tiles/maplibre/01-marker
Display simple marker on the map
This comprehensive step-by-step tutorial provides a detailed explanation of how to incorporate a default Marker onto a map. By following this tutorial you will be able to create a map with a pin.
```bash theme={null}
npm install maplibre-gl
```
These are instructions or content that only pertain to the second step.
Do you prefer a light or dark theme? Choose one of the following styles: `light`, `dark` or `color`.
```javascript theme={null}
var map = new maplibregl.Map({
container: "map",
hash: true,
center: [-122.4194, 37.7749],
zoom: 12,
style: 'https://cdn.solvice.io/styles/light.json',
});
```
Create a new marker using the `marker` function. Set Lng/Lat of the marker using `setLngLat()` function, and
finally add it to the current map using `addTo()` function.
```javascript theme={null}
const marker = new maplibregl.Marker()
.setLngLat([12.550343, 55.665957])
.addTo(map);
```
```html index.html theme={null}
Display a Solvice Map on a webpage
```
# GeoJSON Linelayer
Source: https://maps.solvice.io/examples/tiles/maplibre/02-geojson
Show line data from GeoJSON on the map"
```html theme={null}
Display a Solvice Map on a webpage
```
# Follow a camera
Source: https://maps.solvice.io/examples/tiles/maplibre/03-camera
Follow a camera on a Maplibre map
```html theme={null}
Display a Solvice Map on a webpage: camera
MOVE CAMERA
```
# Hover over GeoJSON Line Data
Source: https://maps.solvice.io/examples/tiles/maplibre/04-hover
Show line data from GeoJSON on the map
```html theme={null}
Display a Solvice Map on a webpage: hover
```
# User location on a dynamic Maplibre map
Source: https://maps.solvice.io/examples/tiles/maplibre/05-location
User location on a dynamic Maplibre map
```html theme={null}
Display a Solvice Map on a webpage: location
```
# Infrastructure deployment
Source: https://maps.solvice.io/infrastructure-deployment
# Solvice Maps: Infrastructure and Deployment Guide
## Infrastructure Overview
Solvice Maps runs on Google Cloud Platform (GCP) using a modern cloud-native architecture designed for high availability, scalability, and operational excellence. The infrastructure supports both real-time routing services and batch processing workloads with automatic scaling and comprehensive monitoring.
## Cloud Architecture
### Platform: Google Cloud Platform
**Project Structure:**
* **Primary Project**: `solver-285414`
* **Primary Region**: `europe-west1` (Belgium)
* **Availability Zone**: `europe-west1-b`
* **Secondary Regions**: Available for multi-region deployment
**Key GCP Services Used:**
* **Compute**: Google Compute Engine + Google Kubernetes Engine
* **Storage**: Cloud Storage for large results and OSRM map data
* **Database**: Cloud SQL (PostgreSQL) for request metadata
* **Messaging**: Cloud Pub/Sub for event-driven processing
* **Networking**: Global Load Balancer with Cloud CDN
* **Monitoring**: Cloud Monitoring + Cloud Logging
* **Security**: Cloud IAM + Secret Manager
## Service Deployment Architecture
### 1. MapR Gateway Service (Primary API)
**Deployment Platform**: Google Cloud Run
* **Runtime**: JVM 17 with Quarkus native compilation
* **Container**: Distroless base image for security
* **Scaling**: 0-100 instances with request-based auto-scaling
* **Cold Start**: \< 100ms with native compilation
**Resource Configuration:**
```yaml theme={null}
resources:
limits:
cpu: "2"
memory: "4Gi"
requests:
cpu: "0.5"
memory: "1Gi"
concurrency: 100
timeout: 300s
```
**Environment Variables:**
```bash theme={null}
# Database Connection
DATABASE_URL=postgresql://user:pass@host:5432/mapr_gateway
DB_MAX_POOL_SIZE=20
# External Service Endpoints
OSRM_SERVICE_URL=https://osrm-europe.solvice.io
TOMTOM_API_KEY=${TOMTOM_API_KEY}
GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}
# Pub/Sub Configuration
PUBSUB_PROJECT_ID=solver-285414
PUBSUB_TABLE_TOPIC=mapr-table-requests
PUBSUB_RESPONSE_TOPIC=mapr-table-responses
# Storage Configuration
STORAGE_BUCKET=mapr-gateway-results
STORAGE_SIGNED_URL_DURATION=3600
# Authentication
JWT_SECRET=${JWT_SECRET}
JWT_ISSUER=solvice-maps
```
### 2. OSRM Service (Routing Engine)
**Deployment Platform**: Google Kubernetes Engine (GKE)
* **Cluster**: `osrm-cluster` (3 nodes, n1-highmem-2)
* **Node Pool**: Container-Optimized OS with SSD persistent disks
* **Scaling**: Horizontal Pod Autoscaler with custom metrics
**Kubernetes Deployment:**
```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-mapr-europe-car
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: nodejs-mapr-europe-car
template:
metadata:
labels:
app: nodejs-mapr-europe-car
spec:
initContainers:
- name: map-downloader
image: gcr.io/solver-285414/map-downloader:latest
volumeMounts:
- name: osrm-maps
mountPath: /maps
env:
- name: MAPS_BUCKET
value: "osrm-maps-europe"
- name: MAP_REGION
value: "europe"
containers:
- name: osrm-service
image: gcr.io/solver-285414/nodejs-mapr:latest
ports:
- containerPort: 3000
env:
- name: OSRM_MAPS
value: |
[{
"map": "europe",
"vehicle": "car",
"path": "/maps/europe-{{slice}}.osrm",
"slices": [0,1,2,3,4,5,6,7,8,9,10,11,12],
"mmap": true
}]
- name: PUBSUB_TABLE_SUBSCRIPTIONS
value: |
[{
"id": "europe-car-subscription",
"weight": 10,
"maxMessages": 2
}]
volumeMounts:
- name: osrm-maps
mountPath: /maps
readOnly: true
resources:
requests:
memory: "8Gi"
cpu: "2"
limits:
memory: "12Gi"
cpu: "4"
livenessProbe:
httpGet:
path: /v1/health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /v1/health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: osrm-maps
persistentVolumeClaim:
claimName: osrm-maps-pvc
```
**Auto-Scaling Configuration:**
```yaml theme={null}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: osrm-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nodejs-mapr-europe-car
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- type: External
external:
metric:
name: pubsub.googleapis.com/subscription/num_undelivered_messages
selector:
matchLabels:
resource.labels.subscription_id: "europe-car-subscription"
target:
type: AverageValue
averageValue: "30"
```
## Infrastructure as Code (Terraform)
### Terraform Configuration Structure
```
terraform/
├── backend.tf # Remote state configuration
├── provider.tf # GCP provider configuration
├── variables.tf # Input variables
├── container.tf # GCE + Container configuration
├── instance_template.tf # VM instance template
├── instance_group.tf # Managed instance group
├── autoscaler.tf # Auto-scaling configuration
├── loadbalancer.tf # Global load balancer
├── health_check.tf # Health check configuration
├── bucket.tf # Cloud Storage buckets
└── pubsub.tf # Pub/Sub topics and subscriptions
```
### Key Terraform Resources
**Instance Template:**
```hcl theme={null}
resource "google_compute_instance_template" "osrm_template" {
name_prefix = "osrm-template-"
description = "Template for OSRM service instances"
machine_type = var.machine_type # n1-highmem-2
disk {
source_image = "cos-cloud/cos-stable"
disk_type = "pd-ssd"
disk_size_gb = 280
auto_delete = true
boot = true
}
disk {
source_image = var.data_disk_image # Custom image with OSRM data
disk_type = "pd-ssd"
disk_size_gb = 1400
auto_delete = false
boot = false
}
network_interface {
network = "default"
access_config {
nat_ip = null # Ephemeral IP
}
}
metadata = {
"gce-container-declaration" = module.gce-container.metadata_value
"google-logging-enabled" = "true"
"enable-guest-attributes" = "TRUE"
}
service_account {
email = var.service_account_email
scopes = ["https://www.googleapis.com/auth/cloud-platform"]
}
tags = ["http-server", "https-server"]
lifecycle {
create_before_destroy = true
}
}
```
**Global Load Balancer:**
```hcl theme={null}
resource "google_compute_global_forwarding_rule" "default" {
name = "osrm-global-forwarding-rule"
target = google_compute_target_http_proxy.default.id
port_range = "80"
ip_address = google_compute_global_address.default.address
}
resource "google_compute_target_http_proxy" "default" {
name = "osrm-target-proxy"
url_map = google_compute_url_map.default.id
}
resource "google_compute_url_map" "default" {
name = "osrm-url-map"
default_service = google_compute_backend_service.default.id
}
resource "google_compute_backend_service" "default" {
name = "osrm-backend-service"
protocol = "HTTP"
timeout_sec = 30
enable_cdn = true
load_balancing_scheme = "EXTERNAL"
backend {
group = google_compute_instance_group_manager.default.instance_group
balancing_mode = "UTILIZATION"
max_utilization = 0.8
}
health_checks = [google_compute_health_check.default.id]
}
```
**Pub/Sub Configuration:**
```hcl theme={null}
# Dynamic topic creation from JSON configuration
locals {
pubsub_config = jsondecode(var.pubsub_subscriptions_json)
}
resource "google_pubsub_topic" "table_topics" {
for_each = { for sub in local.pubsub_config : sub.id => sub }
name = "mapr-table-${each.value.id}"
message_retention_duration = "604800s" # 7 days
}
resource "google_pubsub_topic" "dead_letter_topics" {
for_each = { for sub in local.pubsub_config : sub.id => sub }
name = "mapr-table-${each.value.id}-dead-letter"
}
resource "google_pubsub_subscription" "table_subscriptions" {
for_each = { for sub in local.pubsub_config : sub.id => sub }
name = "mapr-table-${each.value.id}-subscription"
topic = google_pubsub_topic.table_topics[each.key].name
ack_deadline_seconds = 600
message_retention_duration = "604800s"
retain_acked_messages = false
retry_policy {
minimum_backoff = "10s"
maximum_backoff = "600s"
}
dead_letter_policy {
dead_letter_topic = google_pubsub_topic.dead_letter_topics[each.key].id
max_delivery_attempts = 5
}
}
```
## Deployment Processes
### 1. CI/CD Pipeline (GitLab CI)
**Pipeline Structure:**
```yaml theme={null}
stages:
- validate
- test
- build
- deploy-staging
- integration-test
- deploy-production
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
# Terraform Validation
validate:
stage: validate
image: hashicorp/terraform:1.9
script:
- cd terraform
- terraform init -backend=false
- terraform validate
- terraform fmt -check
# Application Testing
test:
stage: test
image: node:22
script:
- cd osrm-service
- npm ci
- npm run test:unit
- npm run test:integration
# Container Build
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
# Staging Deployment
deploy-staging:
stage: deploy-staging
image: google/cloud-sdk:alpine
script:
- gcloud auth activate-service-account --key-file $GOOGLE_APPLICATION_CREDENTIALS
- gcloud config set project $GCP_PROJECT_ID
- cd terraform
- terraform init
- terraform workspace select staging
- terraform plan -var="image_tag=$CI_COMMIT_SHA"
- terraform apply -auto-approve -var="image_tag=$CI_COMMIT_SHA"
environment:
name: staging
url: https://staging-api.solvice.io
# Production Deployment (Manual)
deploy-production:
stage: deploy-production
image: google/cloud-sdk:alpine
script:
- gcloud auth activate-service-account --key-file $GOOGLE_APPLICATION_CREDENTIALS
- gcloud config set project $GCP_PROJECT_ID
- cd terraform
- terraform init
- terraform workspace select production
- terraform plan -var="image_tag=$CI_COMMIT_SHA"
- terraform apply -auto-approve -var="image_tag=$CI_COMMIT_SHA"
environment:
name: production
url: https://routing.solvice.io
when: manual
only:
- main
```
### 2. Zero-Downtime Deployment Strategy
**Rolling Update Process:**
1. **Health Check**: Ensure all current instances are healthy
2. **New Instance Launch**: Launch new instances with updated configuration
3. **Health Validation**: Wait for new instances to pass health checks
4. **Traffic Migration**: Gradually shift traffic to new instances
5. **Old Instance Termination**: Terminate old instances after validation
6. **Rollback Plan**: Automated rollback if health checks fail
**Blue-Green Deployment for Critical Updates:**
```bash theme={null}
#!/bin/bash
# Blue-Green deployment script
# Deploy to blue environment
terraform workspace select blue
terraform apply -var="image_tag=$NEW_VERSION"
# Run health checks
./scripts/health-check.sh blue
# Switch traffic to blue
gcloud compute url-maps set-default-service $URL_MAP \
--default-service=$BLUE_BACKEND_SERVICE
# Monitor for 10 minutes
sleep 600
# If successful, cleanup green environment
if [ $? -eq 0 ]; then
terraform workspace select green
terraform destroy -auto-approve
echo "Deployment successful"
else
# Rollback to green
gcloud compute url-maps set-default-service $URL_MAP \
--default-service=$GREEN_BACKEND_SERVICE
echo "Deployment failed, rolled back"
exit 1
fi
```
### 3. Map Data Deployment
**OSRM Map Update Process:**
```bash theme={null}
#!/bin/bash
# Map data update script
# Build new map data
./build-osrm-maps.sh $REGION $VERSION
# Create disk image
gcloud compute images create osrm-$REGION-$VERSION \
--source-disk=osrm-build-disk \
--source-disk-zone=europe-west1-b
# Update Terraform variable
export TF_VAR_data_disk_image="osrm-$REGION-$VERSION"
# Deploy with rolling update
terraform plan -var="data_disk_image=$TF_VAR_data_disk_image"
terraform apply -auto-approve
```
## Monitoring and Alerting
### 1. Infrastructure Monitoring
**Cloud Monitoring Metrics:**
```yaml theme={null}
# Custom metric for OSRM request latency
- name: "osrm/request_duration_seconds"
description: "OSRM request processing time"
type: "histogram"
labels: ["method", "status", "region"]
# Custom metric for queue depth
- name: "pubsub/queue_depth"
description: "Number of undelivered messages"
type: "gauge"
labels: ["subscription", "topic"]
# Infrastructure metrics
- name: "compute/cpu_utilization"
- name: "compute/memory_utilization"
- name: "compute/disk_utilization"
```
**Alerting Policies:**
```yaml theme={null}
alertPolicy:
displayName: "High Response Time"
conditions:
- displayName: "Response time > 100ms"
conditionThreshold:
threshold: 0.1
comparison: COMPARISON_GREATER_THAN
metric: "osrm/request_duration_seconds"
aggregations:
- alignmentPeriod: "300s"
perSeriesAligner: ALIGN_RATE
notificationChannels:
- "projects/solver-285414/notificationChannels/slack-alerts"
- "projects/solver-285414/notificationChannels/pager-duty"
```
### 2. Application-Level Monitoring
**Health Check Endpoints:**
```typescript theme={null}
// Comprehensive health checks
@Get('/health')
async getHealth(): Promise {
return {
status: 'healthy',
timestamp: new Date().toISOString(),
services: {
database: await this.checkDatabase(),
osrm: await this.checkOSRM(),
pubsub: await this.checkPubSub(),
storage: await this.checkStorage()
},
metrics: {
activeConnections: this.getActiveConnections(),
queueDepth: await this.getQueueDepth(),
memoryUsage: process.memoryUsage()
}
};
}
```
**Performance Metrics:**
```typescript theme={null}
// Custom metrics collection
@Histogram('request_duration_seconds', ['method', 'status'])
private requestDuration: Histogram;
@Counter('requests_total', ['method', 'status'])
private requestsTotal: Counter;
@Gauge('active_requests', [])
private activeRequests: Gauge;
```
## Security Configuration
### 1. Network Security
**VPC Configuration:**
```hcl theme={null}
resource "google_compute_network" "solvice_vpc" {
name = "solvice-maps-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "private_subnet" {
name = "private-subnet"
ip_cidr_range = "10.0.1.0/24"
region = "europe-west1"
network = google_compute_network.solvice_vpc.id
private_ip_google_access = true
}
resource "google_compute_firewall" "allow_internal" {
name = "allow-internal"
network = google_compute_network.solvice_vpc.name
allow {
protocol = "tcp"
ports = ["80", "443", "3000"]
}
source_ranges = ["10.0.0.0/8"]
}
```
**SSL/TLS Configuration:**
```hcl theme={null}
resource "google_compute_managed_ssl_certificate" "default" {
name = "solvice-maps-ssl-cert"
managed {
domains = [
"routing.solvice.io",
"api.solvice.io"
]
}
}
resource "google_compute_target_https_proxy" "default" {
name = "solvice-https-proxy"
url_map = google_compute_url_map.default.id
ssl_certificates = [google_compute_managed_ssl_certificate.default.id]
}
```
### 2. IAM and Access Control
**Service Account Configuration:**
```hcl theme={null}
resource "google_service_account" "osrm_service_account" {
account_id = "osrm-service"
display_name = "OSRM Service Account"
description = "Service account for OSRM compute instances"
}
resource "google_project_iam_member" "osrm_storage_access" {
project = var.project_id
role = "roles/storage.objectViewer"
member = "serviceAccount:${google_service_account.osrm_service_account.email}"
}
resource "google_project_iam_member" "osrm_pubsub_access" {
project = var.project_id
role = "roles/pubsub.subscriber"
member = "serviceAccount:${google_service_account.osrm_service_account.email}"
}
```
**Secret Management:**
```hcl theme={null}
resource "google_secret_manager_secret" "api_keys" {
secret_id = "external-api-keys"
replication {
user_managed {
replicas {
location = "europe-west1"
}
}
}
}
resource "google_secret_manager_secret_version" "api_keys_version" {
secret = google_secret_manager_secret.api_keys.id
secret_data = jsonencode({
tomtom_api_key = var.tomtom_api_key
google_maps_api_key = var.google_maps_api_key
})
}
```
## Disaster Recovery and Backup
### 1. Data Backup Strategy
**Database Backups:**
```bash theme={null}
# Automated PostgreSQL backups
gcloud sql backups create \
--instance=mapr-gateway-db \
--description="Daily automated backup $(date +%Y-%m-%d)"
# Point-in-time recovery enabled
gcloud sql instances patch mapr-gateway-db \
--backup-start-time=02:00 \
--enable-bin-log
```
**Configuration Backups:**
```bash theme={null}
# Terraform state backup
gsutil cp gs://terraform-state-bucket/terraform.tfstate \
gs://disaster-recovery-bucket/terraform-$(date +%Y%m%d).tfstate
# Container images backup
gcloud container images list-tags gcr.io/solver-285414/nodejs-mapr \
--limit=10 --format='get(digest)' | \
xargs -I {} gcloud container images add-tag \
gcr.io/solver-285414/nodejs-mapr@{} \
gcr.io/backup-project/nodejs-mapr:backup-$(date +%Y%m%d)
```
### 2. Multi-Region Deployment
**Regional Failover Configuration:**
```hcl theme={null}
# Primary region: europe-west1
# Secondary region: us-central1
resource "google_compute_instance_group_manager" "osrm_primary" {
name = "osrm-primary"
location = "europe-west1-b"
# ... primary configuration
}
resource "google_compute_instance_group_manager" "osrm_secondary" {
name = "osrm-secondary"
location = "us-central1-b"
# ... secondary configuration (standby)
}
resource "google_compute_health_check" "regional_failover" {
name = "regional-failover-check"
http_health_check {
port = 80
request_path = "/health"
}
check_interval_sec = 10
timeout_sec = 5
healthy_threshold = 2
unhealthy_threshold = 3
}
```
## Cost Optimization
### 1. Resource Optimization
**Preemptible Instances:**
```hcl theme={null}
resource "google_compute_instance_template" "preemptible_template" {
name = "osrm-preemptible-template"
scheduling {
preemptible = true
automatic_restart = false
on_host_maintenance = "TERMINATE"
}
# Use preemptible instances for batch processing
machine_type = "n1-highmem-2"
}
```
**Auto-Scaling Configuration:**
```hcl theme={null}
resource "google_compute_autoscaler" "osrm_autoscaler" {
name = "osrm-autoscaler"
target = google_compute_instance_group_manager.default.id
autoscaling_policy {
max_replicas = 10
min_replicas = 1 # Scale to zero during off-hours
cooldown_period = 300
cpu_utilization {
target = 0.7
}
scaling_schedules {
name = "scale-down-nights"
description = "Scale down during off-hours"
schedule = "0 22 * * *" # 10 PM
time_zone = "Europe/Brussels"
min_required_replicas = 0
duration_sec = 28800 # 8 hours
}
}
}
```
This infrastructure provides a robust, scalable, and cost-effective foundation for the Solvice Maps platform, with comprehensive monitoring, security, and disaster recovery capabilities.
# Integration examples
Source: https://maps.solvice.io/integration-examples
# Solvice Maps: Integration Examples and Patterns
## Integration Overview
This guide provides practical examples and integration patterns for implementing Solvice Maps in various applications and use cases. Each example includes complete code samples, best practices, and performance considerations.
## Quick Start Integration
### 1. Basic Route Calculation
**Use Case:** Simple A-to-B navigation with turn-by-turn directions.
```javascript theme={null}
// JavaScript/Node.js Example
const axios = require('axios');
const SOLVICE_API_KEY = 'your-api-key';
const BASE_URL = 'https://routing.solvice.io';
async function calculateRoute(start, end) {
try {
const response = await axios.post(`${BASE_URL}/route`, {
coordinates: [start, end],
profile: 'car',
steps: true,
geometries: 'geojson'
}, {
headers: {
'X-API-Key': SOLVICE_API_KEY,
'Content-Type': 'application/json'
}
});
const route = response.data.routes[0];
return {
distance: route.distance,
duration: route.duration,
geometry: route.geometry,
steps: route.legs[0].steps
};
} catch (error) {
console.error('Route calculation failed:', error.response?.data || error.message);
throw error;
}
}
// Usage
const start = [4.3517, 50.8503]; // Brussels
const end = [2.3522, 48.8566]; // Paris
calculateRoute(start, end)
.then(route => {
console.log(`Distance: ${route.distance/1000} km`);
console.log(`Duration: ${route.duration/60} minutes`);
console.log(`First instruction: ${route.steps[0].name}`);
});
```
### 2. Distance Matrix for Multiple Locations
**Use Case:** Calculate travel times between delivery locations and warehouses.
```python theme={null}
# Python Example
import requests
import numpy as np
from typing import List, Tuple
class SolviceMapsClient:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = 'https://routing.solvice.io'
self.headers = {
'X-API-Key': api_key,
'Content-Type': 'application/json'
}
def calculate_matrix(self, sources: List[Tuple[float, float]],
destinations: List[Tuple[float, float]]) -> dict:
"""Calculate distance matrix between sources and destinations."""
payload = {
'sources': sources,
'destinations': destinations,
'profile': 'car',
'annotations': ['duration', 'distance']
}
# Use sync endpoint for small matrices
if len(sources) * len(destinations) <= 1000:
response = requests.post(
f'{self.base_url}/table/sync',
json=payload,
headers=self.headers
)
else:
# Use async endpoint for large matrices
response = requests.post(
f'{self.base_url}/table',
json=payload,
headers=self.headers
)
response.raise_for_status()
return response.json()
def find_nearest_warehouse(self, customer_location: Tuple[float, float],
warehouses: List[Tuple[float, float]]) -> dict:
"""Find the nearest warehouse to a customer location."""
result = self.calculate_matrix([customer_location], warehouses)
durations = result['durations'][0]
distances = result['distances'][0]
nearest_index = np.argmin(durations)
return {
'warehouse_index': nearest_index,
'warehouse_location': warehouses[nearest_index],
'travel_time': durations[nearest_index],
'distance': distances[nearest_index]
}
# Usage Example
client = SolviceMapsClient('your-api-key')
# Warehouse locations
warehouses = [
[4.3517, 50.8503], # Brussels
[2.3522, 48.8566], # Paris
[3.0686, 50.6365], # Lille
]
# Customer location
customer = [1.0952, 49.4431] # Rouen
nearest = client.find_nearest_warehouse(customer, warehouses)
print(f"Nearest warehouse: {nearest['warehouse_location']}")
print(f"Travel time: {nearest['travel_time']/60:.1f} minutes")
print(f"Distance: {nearest['distance']/1000:.1f} km")
```
## Advanced Integration Patterns
### 1. Real-Time Delivery Optimization
**Use Case:** Dynamic route optimization for delivery fleet with real-time updates.
```javascript theme={null}
// Advanced JavaScript Example with WebSocket updates
class DeliveryOptimizer {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://routing.solvice.io';
this.activeCalculations = new Map();
}
async optimizeDeliveryRoutes(vehicles, deliveries) {
const results = [];
for (const vehicle of vehicles) {
const route = await this.calculateOptimalRoute(
vehicle.location,
deliveries.filter(d => d.assignedVehicle === vehicle.id),
vehicle.capacity
);
results.push({ vehicleId: vehicle.id, route });
}
return results;
}
async calculateOptimalRoute(startLocation, deliveries, capacity) {
// Create distance matrix for all locations
const locations = [startLocation, ...deliveries.map(d => d.location)];
const matrixResponse = await fetch(`${this.baseUrl}/table`, {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
sources: locations,
destinations: locations,
profile: 'car',
annotations: ['duration', 'distance']
})
});
const matrixData = await matrixResponse.json();
if (matrixData.id) {
// Async processing - poll for results
const matrix = await this.pollForResults(`/table/${matrixData.id}/response`);
return this.solveTSP(matrix, deliveries, capacity);
} else {
// Sync response
return this.solveTSP(matrixData, deliveries, capacity);
}
}
async pollForResults(url, maxWaitTime = 300000) {
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
try {
const response = await fetch(`${this.baseUrl}${url}`, {
headers: { 'X-API-Key': this.apiKey }
});
if (response.ok) {
return await response.json();
} else if (response.status === 404) {
// Still processing
await new Promise(resolve => setTimeout(resolve, 5000));
continue;
} else {
throw new Error(`Request failed: ${response.status}`);
}
} catch (error) {
console.error('Polling error:', error);
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
throw new Error('Request timeout');
}
solveTSP(matrix, deliveries, capacity) {
// Simplified TSP solver using nearest neighbor heuristic
const durations = matrix.durations;
const distances = matrix.distances;
let currentLocation = 0; // Start location
const unvisited = new Set(deliveries.map((_, i) => i + 1)); // +1 because index 0 is start
const route = [0];
let totalDistance = 0;
let totalDuration = 0;
let currentCapacity = 0;
while (unvisited.size > 0) {
let nearest = null;
let nearestDistance = Infinity;
for (const location of unvisited) {
const delivery = deliveries[location - 1];
// Check capacity constraint
if (currentCapacity + delivery.weight <= capacity) {
const distance = durations[currentLocation][location];
if (distance < nearestDistance) {
nearestDistance = distance;
nearest = location;
}
}
}
if (nearest === null) {
// No more deliveries fit - return to depot
totalDistance += distances[currentLocation][0];
totalDuration += durations[currentLocation][0];
route.push(0);
currentCapacity = 0;
currentLocation = 0;
continue;
}
// Visit nearest location
unvisited.delete(nearest);
route.push(nearest);
totalDistance += distances[currentLocation][nearest];
totalDuration += durations[currentLocation][nearest];
currentCapacity += deliveries[nearest - 1].weight;
currentLocation = nearest;
}
// Return to depot
totalDistance += distances[currentLocation][0];
totalDuration += durations[currentLocation][0];
route.push(0);
return {
route,
totalDistance,
totalDuration,
deliveries: route.slice(1, -1).map(i => deliveries[i - 1])
};
}
}
// Usage
const optimizer = new DeliveryOptimizer('your-api-key');
const vehicles = [
{ id: 'v1', location: [4.3517, 50.8503], capacity: 1000 },
{ id: 'v2', location: [2.3522, 48.8566], capacity: 1500 }
];
const deliveries = [
{ id: 'd1', location: [1.0952, 49.4431], weight: 100, assignedVehicle: 'v1' },
{ id: 'd2', location: [7.7521, 48.5734], weight: 200, assignedVehicle: 'v1' },
{ id: 'd3', location: [5.3698, 43.2965], weight: 300, assignedVehicle: 'v2' }
];
optimizer.optimizeDeliveryRoutes(vehicles, deliveries)
.then(results => {
console.log('Optimized routes:', results);
});
```
### 2. Time-Dependent Route Planning
**Use Case:** Plan routes considering traffic patterns throughout the day.
```python theme={null}
# Time-dependent routing example
import requests
import datetime
from typing import List, Dict, Any
class TrafficAwareRouter:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = 'https://routing.solvice.io'
self.headers = {
'X-API-Key': api_key,
'Content-Type': 'application/json'
}
def get_traffic_slice(self, departure_time: datetime.datetime) -> float:
"""Convert departure time to traffic slice."""
hour = departure_time.hour
minute = departure_time.minute
# Map hour to slice (0-12 representing different traffic conditions)
if hour < 6:
return 0 # Early morning
elif hour < 8:
return 1 + (hour - 6) + (minute / 60) # Morning buildup
elif hour < 10:
return 2 + (hour - 8) + (minute / 60) # Peak morning
elif hour < 16:
return 4 + ((hour - 10) / 6) * 6 # Midday (interpolated)
elif hour < 19:
return 10 + (hour - 16) + (minute / 60) # Evening peak
else:
return 12 # Evening/night
def plan_time_dependent_route(self,
start: List[float],
destinations: List[List[float]],
departure_time: datetime.datetime) -> Dict[str, Any]:
"""Plan route considering traffic at departure time."""
traffic_slice = self.get_traffic_slice(departure_time)
day_type = 'weekday' if departure_time.weekday() < 5 else 'weekend'
# Calculate route with traffic consideration
payload = {
'sources': [start],
'destinations': destinations,
'profile': 'car',
'traffic_slice': traffic_slice,
'day_type': day_type,
'annotations': ['duration', 'distance']
}
response = requests.post(
f'{self.base_url}/table/sync',
json=payload,
headers=self.headers
)
response.raise_for_status()
result = response.json()
# Find optimal route considering traffic
durations = result['durations'][0]
distances = result['distances'][0]
# Sort destinations by travel time
destination_data = [
{
'index': i,
'location': destinations[i],
'duration': durations[i],
'distance': distances[i],
'arrival_time': departure_time + datetime.timedelta(seconds=durations[i])
}
for i in range(len(destinations))
]
destination_data.sort(key=lambda x: x['duration'])
return {
'departure_time': departure_time,
'traffic_slice': traffic_slice,
'day_type': day_type,
'optimal_sequence': destination_data
}
def create_cube_for_day(self,
start: List[float],
destinations: List[List[float]],
date: datetime.date) -> str:
"""Create a cube showing travel times throughout the day."""
day_type = 'weekday' if date.weekday() < 5 else 'weekend'
# Define time slices for the entire day
time_slices = [
{'slice': i, 'time': f'{6 + i}:00'}
for i in range(13) # 6 AM to 6 PM
]
payload = {
'sources': [start],
'destinations': destinations,
'profile': 'car',
'time_slices': time_slices,
'day_type': day_type,
'generate_polynomials': True
}
response = requests.post(
f'{self.base_url}/cube',
json=payload,
headers=self.headers
)
response.raise_for_status()
cube_data = response.json()
return cube_data['id']
def get_optimal_departure_times(self, cube_id: str) -> Dict[str, Any]:
"""Analyze cube results to find optimal departure times."""
response = requests.get(
f'{self.base_url}/cube/{cube_id}/response',
headers=self.headers
)
response.raise_for_status()
cube_data = response.json()
# Analyze time slices to find optimal departure times
optimal_times = {}
for dest_idx in range(len(cube_data['destinations'])):
min_duration = float('inf')
optimal_slice = None
for slice_data in cube_data['time_slices']:
duration = slice_data['durations'][0][dest_idx]
if duration < min_duration:
min_duration = duration
optimal_slice = slice_data['slice']
optimal_times[f'destination_{dest_idx}'] = {
'optimal_departure_hour': 6 + optimal_slice,
'minimum_duration': min_duration,
'destination': cube_data['destinations'][dest_idx]
}
return optimal_times
# Usage Example
router = TrafficAwareRouter('your-api-key')
start_location = [4.3517, 50.8503] # Brussels
destinations = [
[2.3522, 48.8566], # Paris
[3.0686, 50.6365], # Lille
[1.0952, 49.4431] # Rouen
]
# Plan route for specific departure time
departure = datetime.datetime(2024, 1, 15, 8, 30) # Monday 8:30 AM
route = router.plan_time_dependent_route(start_location, destinations, departure)
print(f"Departure time: {route['departure_time']}")
print(f"Traffic slice: {route['traffic_slice']}")
print("\nOptimal sequence:")
for dest in route['optimal_sequence']:
print(f" → {dest['location']} (arrival: {dest['arrival_time']}, {dest['duration']/60:.1f} min)")
# Create cube for entire day analysis
cube_id = router.create_cube_for_day(start_location, destinations, departure.date())
print(f"\nCube created: {cube_id}")
# Wait for processing and get optimal departure times
import time
time.sleep(30) # Wait for cube processing
optimal_times = router.get_optimal_departure_times(cube_id)
print("\nOptimal departure times:")
for dest, info in optimal_times.items():
print(f" {dest}: {info['optimal_departure_hour']}:00 ({info['minimum_duration']/60:.1f} min)")
```
### 3. React Web Application Integration
**Use Case:** Interactive web application with map visualization.
```jsx theme={null}
// React Component Example
import React, { useState, useEffect } from 'react';
import { MapContainer, TileLayer, Marker, Polyline, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
const SolviceMapsComponent = () => {
const [route, setRoute] = useState(null);
const [loading, setLoading] = useState(false);
const [startPoint, setStartPoint] = useState([4.3517, 50.8503]); // Brussels
const [endPoint, setEndPoint] = useState([2.3522, 48.8566]); // Paris
const calculateRoute = async () => {
setLoading(true);
try {
const response = await fetch('https://routing.solvice.io/route', {
method: 'POST',
headers: {
'X-API-Key': process.env.REACT_APP_SOLVICE_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
coordinates: [startPoint, endPoint],
profile: 'car',
steps: true,
geometries: 'geojson'
})
});
const data = await response.json();
if (data.routes && data.routes.length > 0) {
const routeData = data.routes[0];
setRoute({
geometry: routeData.geometry.coordinates,
distance: routeData.distance,
duration: routeData.duration,
steps: routeData.legs[0].steps
});
}
} catch (error) {
console.error('Route calculation error:', error);
} finally {
setLoading(false);
}
};
const handleMapClick = (e) => {
const { lat, lng } = e.latlng;
if (!startPoint || (startPoint && endPoint)) {
setStartPoint([lng, lat]);
setEndPoint(null);
setRoute(null);
} else {
setEndPoint([lng, lat]);
}
};
useEffect(() => {
if (startPoint && endPoint) {
calculateRoute();
}
}, [startPoint, endPoint]);
// Convert Solvice Maps coordinate format [lng, lat] to Leaflet format [lat, lng]
const toLeafletCoords = (coords) => coords ? [coords[1], coords[0]] : null;
const routeCoords = route?.geometry?.map(coord => [coord[1], coord[0]]) || [];
return (
{startPoint && (
Start Point
)}
{endPoint && (
End Point
)}
{route && routeCoords.length > 0 && (
)}
);
};
export default SolviceMapsComponent;
```
### 4. Mobile Application Integration (React Native)
**Use Case:** Mobile navigation app with offline capability.
```javascript theme={null}
// React Native Example
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert, AsyncStorage } from 'react-native';
import MapView, { Marker, Polyline } from 'react-native-maps';
import NetInfo from '@react-native-async-storage/async-storage';
class SolviceMapsService {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://routing.solvice.io';
this.cache = new Map();
}
async calculateRoute(start, end, options = {}) {
const cacheKey = `${start.join(',')}-${end.join(',')}`;
// Check cache first
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
try {
const response = await fetch(`${this.baseUrl}/route`, {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
coordinates: [start, end],
profile: options.profile || 'car',
steps: options.steps || true,
geometries: 'geojson'
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
if (data.routes && data.routes.length > 0) {
const route = data.routes[0];
const result = {
coordinates: route.geometry.coordinates.map(coord => ({
latitude: coord[1],
longitude: coord[0]
})),
distance: route.distance,
duration: route.duration,
steps: route.legs[0].steps
};
// Cache the result
this.cache.set(cacheKey, result);
// Store in AsyncStorage for offline access
await AsyncStorage.setItem(`route_${cacheKey}`, JSON.stringify(result));
return result;
}
} catch (error) {
console.error('Route calculation failed:', error);
// Try to load from offline cache
try {
const cachedRoute = await AsyncStorage.getItem(`route_${cacheKey}`);
if (cachedRoute) {
Alert.alert('Offline Mode', 'Using cached route data');
return JSON.parse(cachedRoute);
}
} catch (cacheError) {
console.error('Cache error:', cacheError);
}
throw error;
}
}
async calculateMatrix(sources, destinations) {
const networkState = await NetInfo.fetch();
if (!networkState.isConnected) {
throw new Error('No internet connection');
}
const response = await fetch(`${this.baseUrl}/table/sync`, {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
sources,
destinations,
profile: 'car',
annotations: ['duration', 'distance']
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}
}
const NavigationApp = () => {
const [route, setRoute] = useState(null);
const [loading, setLoading] = useState(false);
const [startLocation, setStartLocation] = useState({
latitude: 50.8503,
longitude: 4.3517
});
const [endLocation, setEndLocation] = useState({
latitude: 48.8566,
longitude: 2.3522
});
const mapsService = new SolviceMapsService('your-api-key');
const calculateRoute = async () => {
setLoading(true);
try {
const start = [startLocation.longitude, startLocation.latitude];
const end = [endLocation.longitude, endLocation.latitude];
const routeData = await mapsService.calculateRoute(start, end, {
profile: 'car',
steps: true
});
setRoute(routeData);
} catch (error) {
Alert.alert('Error', 'Failed to calculate route: ' + error.message);
} finally {
setLoading(false);
}
};
const onMapPress = (event) => {
const { coordinate } = event.nativeEvent;
if (!route) {
setStartLocation(coordinate);
} else {
setEndLocation(coordinate);
setRoute(null);
}
};
useEffect(() => {
if (startLocation && endLocation) {
calculateRoute();
}
}, [startLocation, endLocation]);
return (
{route && (
)}
Solvice Maps Navigation
{loading && Calculating route...}
{route && (
Distance: {(route.distance / 1000).toFixed(1)} kmDuration: {Math.round(route.duration / 60)} minutes
)}
);
};
export default NavigationApp;
```
## Enterprise Integration Patterns
### 1. Microservices Architecture Integration
**Use Case:** Integrate routing service into existing microservices architecture.
```javascript theme={null}
// Express.js Microservice Example
const express = require('express');
const redis = require('redis');
const axios = require('axios');
class RoutingMicroservice {
constructor(config) {
this.app = express();
this.solviceApiKey = config.solviceApiKey;
this.solviceBaseUrl = config.solviceBaseUrl;
this.redis = redis.createClient(config.redisUrl);
this.setupMiddleware();
this.setupRoutes();
}
setupMiddleware() {
this.app.use(express.json());
this.app.use((req, res, next) => {
// Add request ID for tracing
req.requestId = require('uuid').v4();
console.log(`[${req.requestId}] ${req.method} ${req.path}`);
next();
});
}
setupRoutes() {
// Health check
this.app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});
// Route calculation with caching
this.app.post('/routes/calculate', async (req, res) => {
try {
await this.calculateRoute(req, res);
} catch (error) {
console.error(`[${req.requestId}] Route calculation error:`, error);
res.status(500).json({
error: 'Internal server error',
requestId: req.requestId
});
}
});
// Batch route calculation
this.app.post('/routes/batch', async (req, res) => {
try {
await this.calculateBatchRoutes(req, res);
} catch (error) {
console.error(`[${req.requestId}] Batch calculation error:`, error);
res.status(500).json({
error: 'Internal server error',
requestId: req.requestId
});
}
});
// Distance matrix
this.app.post('/matrix/calculate', async (req, res) => {
try {
await this.calculateMatrix(req, res);
} catch (error) {
console.error(`[${req.requestId}] Matrix calculation error:`, error);
res.status(500).json({
error: 'Internal server error',
requestId: req.requestId
});
}
});
}
async calculateRoute(req, res) {
const { start, end, profile = 'car', useTraffic = false } = req.body;
// Validate input
if (!start || !end || start.length !== 2 || end.length !== 2) {
return res.status(400).json({
error: 'Invalid coordinates',
requestId: req.requestId
});
}
// Generate cache key
const cacheKey = `route:${start.join(',')}:${end.join(',')}:${profile}`;
// Check cache
const cachedResult = await this.redis.get(cacheKey);
if (cachedResult) {
console.log(`[${req.requestId}] Cache hit`);
return res.json({
...JSON.parse(cachedResult),
cached: true,
requestId: req.requestId
});
}
// Call Solvice Maps API
const solviceResponse = await axios.post(`${this.solviceBaseUrl}/route`, {
coordinates: [start, end],
profile,
steps: true,
geometries: 'geojson'
}, {
headers: {
'X-API-Key': this.solviceApiKey,
'Content-Type': 'application/json'
}
});
const route = solviceResponse.data.routes[0];
const result = {
distance: route.distance,
duration: route.duration,
geometry: route.geometry,
steps: route.legs[0].steps.map(step => ({
instruction: step.maneuver.type,
name: step.name,
distance: step.distance,
duration: step.duration
}))
};
// Cache result for 1 hour
await this.redis.setex(cacheKey, 3600, JSON.stringify(result));
res.json({
...result,
cached: false,
requestId: req.requestId
});
}
async calculateBatchRoutes(req, res) {
const { routes } = req.body;
if (!Array.isArray(routes) || routes.length === 0) {
return res.status(400).json({
error: 'Routes array is required',
requestId: req.requestId
});
}
// Process routes in parallel with concurrency limit
const CONCURRENCY_LIMIT = 5;
const results = [];
for (let i = 0; i < routes.length; i += CONCURRENCY_LIMIT) {
const batch = routes.slice(i, i + CONCURRENCY_LIMIT);
const batchPromises = batch.map(async (route, index) => {
try {
const mockReq = { body: route, requestId: `${req.requestId}-${i + index}` };
const mockRes = {
json: (data) => data,
status: () => mockRes
};
return await this.calculateRoute(mockReq, mockRes);
} catch (error) {
return {
error: error.message,
route: route
};
}
});
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
res.json({
results,
requestId: req.requestId
});
}
async calculateMatrix(req, res) {
const { sources, destinations, profile = 'car' } = req.body;
if (!Array.isArray(sources) || !Array.isArray(destinations)) {
return res.status(400).json({
error: 'Sources and destinations must be arrays',
requestId: req.requestId
});
}
// Determine if we should use sync or async endpoint
const totalCombinations = sources.length * destinations.length;
const useAsync = totalCombinations > 1000;
try {
const endpoint = useAsync ? '/table' : '/table/sync';
const solviceResponse = await axios.post(`${this.solviceBaseUrl}${endpoint}`, {
sources,
destinations,
profile,
annotations: ['duration', 'distance']
}, {
headers: {
'X-API-Key': this.solviceApiKey,
'Content-Type': 'application/json'
}
});
if (useAsync) {
// Return job ID for async processing
res.json({
jobId: solviceResponse.data.id,
status: 'processing',
estimatedCompletion: solviceResponse.data.estimated_completion,
checkUrl: `/matrix/status/${solviceResponse.data.id}`,
requestId: req.requestId
});
} else {
// Return immediate results
res.json({
durations: solviceResponse.data.durations,
distances: solviceResponse.data.distances,
sources: solviceResponse.data.sources,
destinations: solviceResponse.data.destinations,
requestId: req.requestId
});
}
} catch (error) {
throw error;
}
}
start(port = 3000) {
this.app.listen(port, () => {
console.log(`Routing microservice running on port ${port}`);
});
}
}
// Usage
const service = new RoutingMicroservice({
solviceApiKey: process.env.SOLVICE_API_KEY,
solviceBaseUrl: 'https://routing.solvice.io',
redisUrl: process.env.REDIS_URL
});
service.start();
```
### 2. Event-Driven Architecture
**Use Case:** Process routing requests through message queues.
```javascript theme={null}
// Event-driven processing with RabbitMQ
const amqp = require('amqplib');
const axios = require('axios');
class RoutingEventProcessor {
constructor(config) {
this.config = config;
this.connection = null;
this.channel = null;
}
async connect() {
this.connection = await amqp.connect(this.config.rabbitmqUrl);
this.channel = await this.connection.createChannel();
// Setup queues
await this.channel.assertQueue('routing.requests', { durable: true });
await this.channel.assertQueue('routing.responses', { durable: true });
await this.channel.assertQueue('routing.errors', { durable: true });
console.log('Connected to RabbitMQ');
}
async processRoutingRequests() {
await this.channel.consume('routing.requests', async (msg) => {
if (msg) {
try {
const request = JSON.parse(msg.content.toString());
console.log('Processing routing request:', request.id);
const result = await this.calculateRoute(request);
// Send success response
await this.channel.sendToQueue(
'routing.responses',
Buffer.from(JSON.stringify({
requestId: request.id,
status: 'completed',
result: result,
timestamp: new Date().toISOString()
})),
{ persistent: true }
);
this.channel.ack(msg);
console.log('Routing request completed:', request.id);
} catch (error) {
console.error('Routing request failed:', error);
// Send error response
await this.channel.sendToQueue(
'routing.errors',
Buffer.from(JSON.stringify({
requestId: JSON.parse(msg.content.toString()).id,
status: 'failed',
error: error.message,
timestamp: new Date().toISOString()
})),
{ persistent: true }
);
this.channel.nack(msg, false, false); // Don't requeue
}
}
});
console.log('Started processing routing requests');
}
async calculateRoute(request) {
const { coordinates, profile, options = {} } = request;
const response = await axios.post(`${this.config.solviceBaseUrl}/route`, {
coordinates,
profile,
steps: options.includeSteps || true,
geometries: 'geojson'
}, {
headers: {
'X-API-Key': this.config.solviceApiKey,
'Content-Type': 'application/json'
},
timeout: 30000 // 30 second timeout
});
const route = response.data.routes[0];
return {
distance: route.distance,
duration: route.duration,
geometry: route.geometry,
steps: options.includeSteps ? route.legs[0].steps : undefined
};
}
async publishRoutingRequest(request) {
const message = {
id: require('uuid').v4(),
...request,
timestamp: new Date().toISOString()
};
await this.channel.sendToQueue(
'routing.requests',
Buffer.from(JSON.stringify(message)),
{ persistent: true }
);
return message.id;
}
async close() {
if (this.channel) {
await this.channel.close();
}
if (this.connection) {
await this.connection.close();
}
}
}
// Usage
const processor = new RoutingEventProcessor({
rabbitmqUrl: process.env.RABBITMQ_URL,
solviceApiKey: process.env.SOLVICE_API_KEY,
solviceBaseUrl: 'https://routing.solvice.io'
});
// Start processing
processor.connect()
.then(() => processor.processRoutingRequests())
.catch(console.error);
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Shutting down...');
await processor.close();
process.exit(0);
});
```
## Best Practices Summary
### 1. Performance Optimization
* **Cache frequently requested routes** using content-based keys
* **Use appropriate endpoints** (sync vs async) based on request size
* **Implement request batching** for multiple similar requests
* **Set reasonable timeouts** for external API calls
### 2. Error Handling
* **Implement retry logic** with exponential backoff
* **Handle rate limits** gracefully with queueing
* **Provide fallback mechanisms** for offline scenarios
* **Log errors comprehensively** for debugging
### 3. Security
* **Store API keys securely** using environment variables or secret management
* **Validate all input data** before sending to API
* **Implement rate limiting** in your application layer
* **Use HTTPS** for all API communications
### 4. Monitoring
* **Track API usage** and costs
* **Monitor response times** and error rates
* **Set up alerts** for service degradation
* **Log request/response data** for analysis
### 5. Scalability
* **Design stateless services** for horizontal scaling
* **Use event-driven patterns** for heavy processing
* **Implement circuit breakers** for external service protection
* **Cache results appropriately** to reduce API calls
These integration examples provide a solid foundation for implementing Solvice Maps in various application architectures and use cases.
# Introduction
Source: https://maps.solvice.io/introduction
Welcome to the Solvice Maps docs
## Tile Server
Solvice Maps is a platform that allows you to create and manage your own maps. Serve Tiles through the most popular map clients:
If you're looking to get started with interactive maps on the web, you're in the right place! Let's get started with the best libraries for based on your preferred tech stack and use case.
Supporting older browsers, *Leaflet* is a great choice for your next project. Lightweight and easy to use.
A modern open-source JavaScript library that renders interactive maps. It is a fork of the great Mapbox GL JS.
## Routing Server
Solvice Maps Routing is built on top of OpenStreetMap (OSM), which is a collaborative mapping project that provides free and open geographic data.
It combines sophisticated routing algorithms and uses speed-up techniques called Contraction Hierarchies or Multi-level Dijkstra to compute and output the shortest path between any origin and destination within a few milliseconds on a continental-sized network.
A-B routing
Large scale distance matrices
# Interactive Maps: Navigating the Modern World
Source: https://maps.solvice.io/lp/dynamic_map
Google Maps API integration
# Dynamic Map Example: Navigating the Modern World
In an ever-evolving world, the way we navigate and understand our surroundings is undergoing a significant transformation.
Traditional maps, with their static nature, often fall short in capturing the dynamic essence of our modern environment, leading to inefficiencies and missed insights.
Dynamic maps, by contrast, offer real-time, interactive visualizations that adapt to display relevant data, from traffic patterns to energy sources, making them a cornerstone of modern navigation and analysis.
This blog post will delve into the revolutionary shift towards dynamic mapping, explore various impactful examples ranging from New York City's pulse to the distribution of minimum wage workers, and discuss how these innovative maps are reshaping our understanding of the world.
## Digital Navigation: Revolutionizing Mapmaking
The advent of digital navigation has ushered in a revolutionary change in the field of mapmaking, fundamentally altering how we visualize and interact with geographical information. This transformation is characterized by the transition from traditional static maps, which offer a fixed view of the world, to dynamic, interactive maps. These modern maps are not just tools for finding directions; they are comprehensive platforms that integrate real-time data, offering a more accurate and interactive experience. This shift has expanded the role of maps from mere navigational aids to powerful tools for data analysis and decision-making.
Dynamic maps have become indispensable in various sectors, including urban planning, transportation, and environmental monitoring, by providing detailed, up-to-date information that adapts to the user's needs. **Interactive features** such as zooming, panning, and filtering allow users to explore data layers and gain insights that were previously inaccessible with static maps. Furthermore, the ability to integrate user-generated content and real-time updates has made these maps more relevant and useful, marking a significant leap forward in how we understand and navigate our world.
### Interactive Maps: Bringing Data to Life
Interactive maps have transformed the way we interact with data, making complex information more accessible and engaging. By incorporating features like **clickable icons, pop-up windows, and animations**, these maps bring data to life, allowing users to explore and understand detailed information with ease. Whether it's tracking live traffic updates, visualizing environmental changes, or exploring demographic statistics, interactive maps provide a dynamic platform that enhances user engagement and improves data comprehension. This level of interactivity not only makes information more memorable but also encourages further exploration and discovery.
### Platform Benefits: Saving Time and Cutting Costs
Adopting dynamic map platforms offers significant benefits, notably in **saving time and reducing costs**. For businesses, integrating indoor and outdoor navigation with space management systems streamlines operations, optimizes resource allocation, and enhances customer experience. Employees and visitors benefit from easy-to-navigate, real-time information, reducing confusion and improving satisfaction. Additionally, the ability to manage hybrid work environments and efficiently utilize office space can lead to considerable cost savings, proving that dynamic maps are a valuable investment for modern businesses looking to stay ahead in a rapidly changing world.
## NYC's Heartbeat: A Dynamic Map Example
Imagine a map that captures the essence of New York City - not just its streets and landmarks, but its living, breathing energy. This is exactly what a dynamic map of NYC's "invisible heartbeat" does. By visualizing data like traffic flow, population movement, and energy consumption, this map offers a glimpse into the city's dynamic rhythm. **Interactive elements** allow users to see how these patterns change throughout the day, making the invisible visible.
The beauty of this dynamic map lies in its ability to:
* Highlight peak traffic times and areas of congestion
* Show fluctuations in energy use as the city wakes up and winds down
* Track the movement of people, revealing the city's most vibrant hubs at different times
By providing these insights, the map not only serves as a tool for urban planning and management but also enriches our understanding of urban life, illustrating the complex interplay of factors that keep NYC pulsing.
## Visualizing Public Holidays with Dynamic Maps
Dynamic maps have revolutionized the way we visualize complex datasets, and their application in showcasing public holidays across different countries is a fascinating example. By using dynamic mapping, users can interactively explore which countries are celebrating on any given day of the year. This not only highlights the cultural diversity of global celebrations but also allows for an engaging comparison of statutory holidays. **Key features** such as clickable regions and pop-up information make these maps a powerful tool for understanding the world's myriad of public holidays at a glance.
For instance, selecting a specific holiday on a dynamic map could reveal which countries recognize this day as a public holiday and provide additional context about its significance. **Benefits of using dynamic maps for this purpose include**:
* Easy identification of holidays celebrated worldwide or in specific regions
* Insights into the cultural significance behind various holidays
* The ability to track holiday observances over time, enhancing our appreciation of global traditions. This dynamic approach to visualizing public holidays not only educates users about international cultures but also fosters a greater sense of global community by connecting us through our shared celebrations.
## Mapping U.S. Electricity Sources Dynamically
Dynamic maps bring a new level of understanding to the complex landscape of U.S. electricity production, highlighting the diverse sources that power the nation. By visualizing data in real-time, these maps allow users to see how electricity generation varies from state to state, driven by factors such as natural resources, policy decisions, and technological advancements. **Key features** of dynamic electricity maps include the ability to filter by energy source—such as solar, wind, nuclear, and fossil fuels—and to view the operational status of power plants, making it easy to grasp the country's energy mix at a glance.
Beyond just identifying where electricity comes from, dynamic maps offer insights into trends and shifts in energy production. For instance, users can track the growth of renewable energy over time or see how certain events, like natural disasters or policy changes, impact electricity generation. **Benefits of using dynamic maps for visualizing U.S. electricity sources** include:
* Enhanced decision-making for policy makers and energy professionals
* Increased public awareness of renewable energy adoption
* Real-time updates on energy infrastructure developments. This dynamic approach not only educates but also engages the public in discussions about sustainable energy and the future of electricity in the U.S.
## Dynamic Mapping of Minimum Wage Workers
Dynamic mapping has taken a significant leap forward by shedding light on the economic landscape of minimum wage workers across the U.S. This innovative approach provides a vivid, real-time illustration of wage disparities, highlighting areas where workers struggle the most to meet their basic needs. By integrating data from various sources, such as the U.S. Department of Labor and the Living Wage Calculator, these maps offer a comprehensive view of:
* The distribution of minimum wage workers by state or congressional district
* The gap between minimum wages and living wages in different regions
* The impact of recent legislation on wage levels
**The benefits of using dynamic maps for this purpose are manifold**, enhancing public awareness and informing policy debates. By visualizing the economic challenges faced by minimum-wage workers, these maps serve as a powerful tool for advocates pushing for higher wages. They not only highlight the regions where legislative action has made a positive impact but also pinpoint where further efforts are needed. **Key advantages include**:
* Facilitating a better understanding of the geographic distribution of low-wage workers
* Highlighting the effectiveness of wage increases in various states
* Offering a dynamic, easily accessible platform for engaging with complex wage data
## World Cup Ratings: A Dynamic Visualization
The 2018 FIFA World Cup not only captivated soccer fans worldwide but also provided a unique opportunity to visualize global interest through dynamic mapping. By combining player ratings from FIFA 18 with team performance data, an interactive map was created to show how players at each position compared to their competitors across the globe. This dynamic visualization offered insights into:
* The distribution of top-rated players in different countries
* Comparisons between teams based on player ratings
* Trends in player performance throughout the tournament
**The benefits of using a dynamic map for visualizing World Cup ratings** are significant, enhancing the experience for fans and analysts alike. It allowed users to interact with the data, uncovering detailed information about players and teams with just a click or hover. **Key advantages include**:
* Engaging fans in a deeper analysis of the game
* Providing a global perspective on the talent distribution
* Offering real-time updates as player ratings changed throughout the tournament. This approach not only made the vast amount of statistical data comprehensible but also added an engaging layer to the World Cup experience, demonstrating the power of dynamic mapping in sports analytics.
## Mapping the World's Languages Dynamically
Imagine a map that doesn't just show you where you are, but also speaks your language. This is the reality with dynamic maps showcasing the world's languages. These maps are not static; they change and adapt to display the languages spoken in different regions, highlighting the incredible diversity of our planet. By selecting a country or region, users can discover:
* The primary language spoken
* Various dialects present in the area
* Minority languages that contribute to the cultural richness
The benefits of such dynamic language maps are profound, especially in emphasizing global cultural diversity. They serve as educational tools, helping users understand and appreciate the linguistic landscape of the world. **Key advantages include**:
* Enhancing linguistic awareness and cultural sensitivity
* Providing a valuable resource for language learners and educators
* Offering insights into language distribution and prevalence, which can be crucial for sociolinguistic research. These maps transform our perception of language, from a mere means of communication to a vibrant indicator of human diversity and culture.
## Visualizing Net Migration with Dynamic Maps
Dynamic maps have the unique ability to **visualize complex global phenomena** like net migration in a way that is both engaging and informative. By mapping the flow of people between countries, users can gain insights into patterns of migration, including which countries are experiencing an influx of people and which are seeing more people leave. These maps often use color coding—such as blue for positive net migration and red for negative—to provide a clear, at-a-glance understanding of global migration trends.
The benefits of using dynamic maps for visualizing net migration are numerous. They allow users to:
* Easily identify hotspots of immigration and emigration
* Understand the impact of political, economic, or environmental factors on migration
* Explore data in an interactive manner, drilling down for more detailed information on specific countries or regions. This approach not only enhances our comprehension of global migration patterns but also fosters a deeper awareness of the forces driving these movements.
## Star Mapper: Navigating the Night Sky
Imagine a tool that not only shows you the constellations but also guides you through the night sky as it changes throughout the year. That's exactly what "Star Mapper" does. This dynamic map is an incredible example of how technology can enhance our understanding of astronomy. By selecting different dates and locations, users can see how the night sky shifts, identifying constellations, planets, and celestial events with ease. **Key features** of Star Mapper include:
* Interactive constellation guides
* Real-time celestial event tracking
* Customizable views based on user location and date
The benefits of using "Star Mapper" for exploring the night sky are numerous. It not only serves as an educational tool for those new to astronomy but also provides seasoned stargazers with detailed information about celestial bodies and events. **Benefits include**:
* Enhancing users' knowledge of the night sky
* Encouraging exploration and discovery of new celestial phenomena
* Offering an accessible way to plan stargazing sessions based on specific astronomical events. This dynamic approach to mapping the stars makes astronomy more engaging and accessible to everyone, turning a simple glance at the night sky into an informative and interactive experience.
## Mapping Solar Eclipses Dynamically Until 2080
Imagine a dynamic map that not only pinpoints where and when every solar eclipse will occur up until 2080 but also offers an interactive journey through these celestial events. This map is a brilliant example of how dynamic mapping transforms our interaction with astronomical events, making it possible to visualize the path of totality for each eclipse on a global scale. Users can **explore various dates** to see how the shadows of the moon cast upon the Earth will change, providing a vivid, educational experience that static maps simply cannot match.
The benefits of such a dynamic eclipse map are vast, enhancing both educational and planning purposes. For instance, educators can use the map to **show students the science behind eclipses** in an engaging way, while eclipse chasers can plan their next adventure to witness this awe-inspiring natural phenomenon. Key features include:
* **Interactive timelines** that allow users to scroll through future eclipses
* **Filter options** to view eclipses by type, such as total, annular, or partial
* **Location-specific details** about the best viewing times and conditions. This dynamic approach not only educates but also excites, making the cosmic dance of the sun, moon, and Earth accessible to all.
## Graffiti Around the World: A Dynamic Map View
Imagine a dynamic map that takes you on a global tour of street art, from the vibrant graffiti-covered walls of Berlin to the intricate murals of São Paulo. This map isn't just a collection of locations; it's an interactive journey through the world's urban landscapes, showcasing the rich tapestry of street art that adorns cities across continents. **Key features** of this dynamic map include:
* Interactive markers for each piece of graffiti, offering details about the artist and the artwork's significance
* Filters to explore street art by region, style, or artist, making it easy to find specific types of work or discover new favorites
* Real-time updates, allowing users to contribute new finds and ensuring the map remains an up-to-date reflection of the street art scene worldwide.
The benefits of using a dynamic map to visualize global graffiti are profound, as it highlights not only the beauty and diversity of street art but also its cultural implications. Through this interactive platform, users can:
* Gain insights into the social and political messages often conveyed through graffiti
* Appreciate the role of street art in urban beautification and cultural expression
* Connect with a global community of street art enthusiasts, fostering a deeper understanding and appreciation of this art form. **This dynamic approach** not only educates but also inspires, turning the exploration of street art into an engaging and immersive experience.
## Frequently Asked Questions
### What is an example of a dynamic map?
An example of a dynamic map is a visualization that captures New York City's "invisible heartbeat" by displaying real-time data such as traffic flow, population movement, and energy consumption. This map uses interactive elements to show how these patterns change throughout the day, providing insights into the city's dynamic rhythm.
### What is a dynamic map?
A dynamic map offers real-time, interactive visualizations that adapt to display relevant data, such as traffic patterns or energy sources. Unlike traditional static maps that offer a fixed view, dynamic maps integrate real-time data and interactive features, making them a powerful tool for navigation, analysis, and understanding complex information.
### What is the difference between static and dynamic maps?
The difference between static and dynamic maps lies in their nature and functionality. Static maps offer a fixed view of the world, capturing a moment in time without the ability to adapt or interact with the data. Dynamic maps, on the other hand, provide real-time, interactive visualizations that can adapt to display relevant data, integrate real-time updates, and offer interactive features such as zooming, panning, and filtering, making them more relevant and useful for a wide range of applications.
# Google Maps TILE API
Source: https://maps.solvice.io/lp/google-maps
Google Maps API integration
# OpenAPI spec
Source: https://maps.solvice.io/openapi
View the Solvice Maps Routing API OpenAPI spec file
# Demo
Source: https://maps.solvice.io/route/demo
# Introduction
Source: https://maps.solvice.io/route/intro
High-performance routing API for turn-by-turn directions and batch route calculations
# Route API Overview
The Route API provides fast, accurate turn-by-turn directions between multiple waypoints. Built for high-performance applications, it delivers sub-50ms response times with detailed geometry data for map visualization.
## Key Features
### 🚗 Turn-by-Turn Directions
Generate detailed navigation instructions with:
* **Precise geometry** in GeoJSON format for map rendering
* **Step-by-step instructions** with maneuver details
* **Distance and duration** calculations for each route segment
* **Multi-waypoint support** for complex routing scenarios
### ⚡ Batch Processing
Process multiple route requests efficiently:
* **Single API call** for multiple route calculations
* **Reduced network overhead** and improved performance
* **Concurrent processing** with optimized resource usage
### 🌍 Multi-Engine Support
Choose from multiple routing map data:
* **OpenStreetMap** — open data, traffic-unaware
* **TomTom** — predictive traffic via historical patterns. Pair with `departureTime` to get an estimate for any future moment.
* **TomTom real-time** — live traffic conditions on the road *right now*. Set `"engine": "TOMTOM_REAL_TIME"`; `departureTime` is ignored.
Contact [sales@solvice.io](mailto:sales@solvice.io) for commercial TomTom access.
## Real-Time Traffic
Set `"engine": "TOMTOM_REAL_TIME"` on `POST /route` to compute a route that reflects current traffic. The request always uses *now* as the departure time — any client-supplied `departureTime` is ignored.
Live traffic is combined with TomTom **historical traffic patterns** by default: patterns improve estimates outside peak hours and cover road segments without live traffic data. To compute against live traffic only, set `"includeTrafficPatterns": false`.
```json theme={null}
{
"coordinates": [
[4.3517, 50.8503],
[2.3522, 48.8566]
],
"engine": "TOMTOM_REAL_TIME"
}
```
Real-time traffic is **not supported** on `POST /route/batch`. Use the standard `TOMTOM` engine with predictive traffic for batch scenarios.
## Available Endpoints
Calculate a single route between 2 or more coordinates with full turn-by-turn directions
Process multiple route requests efficiently in a single API call
## Quick Start
### Basic Route Request
```json theme={null}
{
"coordinates": [
[4.3517, 50.8503], // Brussels
[2.3522, 48.8566] // Paris
]
}
```
### Response Structure
```json theme={null}
{
"routes": [{
"distance": 264100, // meters
"duration": 8790, // seconds
"legs": [...], // detailed route segments
"weight": 8790
}]
}
```
# /route
Source: https://maps.solvice.io/route/post-route
POST /route
Request a route for 2 or more coordinates. Returns total distance, travel time, and the geometry to plot the route on a map.
# /route/batch
Source: https://maps.solvice.io/route/post-route-batch
POST /route/batch
Process multiple route requests in a single API call. Each route in the batch is calculated independently, reducing API overhead for bulk operations.
# Solvice maps overview
Source: https://maps.solvice.io/solvice-maps-overview
# Solvice Maps: Product Overview
## What is Solvice Maps?
Solvice Maps is a high-performance, enterprise-grade routing and location intelligence platform that provides sophisticated mapping services for businesses requiring accurate, scalable, and fast geospatial calculations. Built specifically for logistics, delivery, field service, and supply chain optimization use cases, Solvice Maps combines multiple routing engines with advanced algorithms to deliver unparalleled routing intelligence.
## Core Value Proposition
### For Business Leaders
* **Operational Efficiency**: Reduce delivery times, fuel costs, and resource utilization through optimized routing
* **Scalability**: Handle operations from single routes to massive enterprise-scale logistics networks
* **Cost Control**: Transparent, usage-based pricing with predictable costs
* **Competitive Advantage**: Advanced features like time-dependent routing and multi-engine optimization
### For Technical Teams
* **Developer-Friendly**: RESTful APIs with comprehensive documentation and SDKs
* **High Performance**: Sub-50ms response times for routing calculations
* **Reliability**: Enterprise-grade infrastructure with 99.9% uptime SLA
* **Flexibility**: Support for multiple data sources, routing engines, and integration patterns
## Product Capabilities
### 1. Advanced Routing Services
**Directions API**
* Turn-by-turn navigation with detailed geometry
* Multiple transportation modes (car, truck, bicycle, walking)
* Real-time traffic integration
* Time-dependent routing for optimal timing
**Distance Matrix API**
* Calculate travel times and distances between multiple points
* Supports matrices up to 100,000+ coordinate combinations
* Automatic request optimization and parallel processing
* Batch processing for large datasets
**Cube API (Time-Dependent Matrices)**
* Generate travel time patterns across different time periods
* Traffic-aware routing throughout the day
* Weekend vs. weekday optimization
* Polynomial approximation for smooth time transitions
### 2. Multiple Routing Engines
**OpenStreetMap (OSM)**
* Free, community-driven global coverage
* Ideal for general-purpose routing
* Regular data updates from community contributions
**TomTom Integration**
* Premium commercial routing with real-time traffic
* High accuracy for urban environments
* Global coverage with local optimizations
**AnyMap Services**
* European-focused routing with local expertise
* Specialized for European logistics requirements
* Advanced traffic modeling
**Google Maps Integration**
* Industry-leading accuracy and coverage
* Comprehensive points of interest data
* Global real-time traffic information
### 3. Map Visualization Services
**Vector Tiles**
* High-quality, customizable map rendering
* Support for Mapbox GL JS and Leaflet
* Optimized for performance and bandwidth
**Custom Styling**
* Brand-aligned map appearances
* Industry-specific visualizations
* Light and dark theme support
## Technical Architecture
### High-Level System Design
```
Client Applications
↓
API Gateway (Authentication, Rate Limiting)
↓
MapR Gateway (Request Processing, Caching)
↓ ↙ ↘
OSM Engine TomTom API Google Maps API
↓
Response Aggregation & Optimization
↓
Client Response
```
### Key Technical Features
**Performance Optimization**
* Intelligent request splitting for large matrices
* Content-based caching to avoid duplicate calculations
* Asynchronous processing for heavy computational workloads
* Sub-50ms response times for typical routing requests
**Scalability**
* Auto-scaling infrastructure on Google Cloud Platform
* Event-driven architecture using Google Cloud Pub/Sub
* Horizontal scaling across multiple regions
* Load balancing with weighted round-robin distribution
**Reliability**
* Multi-engine redundancy and automatic failover
* Comprehensive monitoring and alerting
* Circuit breaker patterns for external service protection
* 99.9% uptime SLA with proactive monitoring
## Use Cases and Industries
### Logistics and Delivery
* **Last-mile delivery optimization**: Route drivers efficiently to minimize costs
* **Fleet management**: Optimize vehicle utilization and reduce fuel consumption
* **Supply chain planning**: Calculate optimal distribution center locations
* **Real-time dispatch**: Dynamic routing based on current traffic conditions
### Field Service Management
* **Technician routing**: Optimize service calls and minimize travel time
* **Territory planning**: Define service areas based on travel time analysis
* **Emergency response**: Fastest route calculation for urgent situations
* **Resource allocation**: Balance workload across service teams
### Retail and E-commerce
* **Store location analysis**: Identify optimal locations based on customer accessibility
* **Delivery time estimation**: Provide accurate delivery windows to customers
* **Service area definition**: Define delivery zones and pricing tiers
* **Click-and-collect optimization**: Route customers to nearest pickup points
### Transportation and Mobility
* **Route planning applications**: Power consumer and professional navigation apps
* **Public transport integration**: Combine different transportation modes
* **Ride-sharing optimization**: Match drivers and passengers efficiently
* **Traffic analysis**: Understand traffic patterns for urban planning
## Integration Approaches
### REST API Integration
* Simple HTTP-based integration
* JSON request/response format
* Comprehensive error handling
* Rate limiting and authentication
### SDK and Libraries
* JavaScript/TypeScript SDK for web applications
* Mobile SDKs for iOS and Android
* Server-side libraries for major programming languages
### Webhook Integration
* Asynchronous processing with callback notifications
* Ideal for batch processing and heavy computational workloads
* Secure webhook verification and retry mechanisms
## Pricing and Plans
### Usage-Based Pricing
* Pay only for actual API calls
* No minimum commitments or setup fees
* Volume discounts for high-usage customers
* Transparent pricing with detailed usage analytics
### Enterprise Features
* Custom SLA agreements
* Dedicated technical support
* Priority processing queues
* White-label solutions
## Getting Started
### Quick Start (5 minutes)
1. **Sign up** for a Solvice Maps account
2. **Get API key** from the developer dashboard
3. **Make first API call** using our interactive documentation
4. **Integrate** with our SDKs or direct REST API calls
### Development Resources
* **Interactive API documentation** with live examples
* **Code samples** in multiple programming languages
* **SDKs** for popular frameworks and platforms
* **Developer forum** and technical support
### Production Deployment
* **Staging environment** for testing and validation
* **Performance monitoring** and analytics dashboard
* **Technical consultation** for optimization
* **24/7 support** for enterprise customers
## Why Choose Solvice Maps?
### Technical Excellence
* **Sub-50ms performance**: Industry-leading response times
* **99.9% uptime**: Enterprise-grade reliability
* **Global scale**: Handle millions of requests per day
* **Advanced algorithms**: Sophisticated optimization techniques
### Business Value
* **Reduce operational costs**: Optimize routes to minimize fuel and time
* **Improve customer satisfaction**: Faster deliveries and accurate ETAs
* **Scale efficiently**: Grow your operations without infrastructure concerns
* **Gain competitive advantage**: Advanced routing capabilities
### Developer Experience
* **Comprehensive documentation**: Clear, detailed API references
* **Multiple integration options**: REST APIs, SDKs, webhooks
* **Testing tools**: Sandbox environment and testing utilities
* **Responsive support**: Technical assistance when you need it
***
*Ready to optimize your location intelligence? Start with our [interactive documentation](https://docs.solvice.io/routing) or [contact our team](mailto:support@solvice.io) for a personalized consultation.*
# Demo
Source: https://maps.solvice.io/table/demo
# /table/{id}
Source: https://maps.solvice.io/table/get-table
GET /table/{id}
Retrieve the status and metadata of a table request by its ID.
# /table/{id}/progress
Source: https://maps.solvice.io/table/get-table-progress
get /table/{id}/progress
Get the computation progress of a table request. Useful for tracking large matrix calculations.
After sending the table request to the API, you can follow the progress in this endpoint.
The progress will list the created submatrices (children).
Average calculation time of matrix :
| Matrix Size | Calculation time |
| ------------- | ---------------- |
| `10x10` | 0.1s |
| `100x100` | 0.2s |
| `250x250` | 0.8s |
| `1000x1000` | 4.6s |
| `10000x10000` | 24.9s |
# /table/{id}/response
Source: https://maps.solvice.io/table/get-table-response
GET /table/{id}/response
Fetch the computed distance matrix result for a completed table request.
# /table/{id}/response/url
Source: https://maps.solvice.io/table/get-table-response-signed-url
GET /table/{id}/response/url
Get a signed URL that redirects to the table response stored in Cloud Storage. Useful for large responses.
# Introduction
Source: https://maps.solvice.io/table/intro
The Solvice Maps Distance Matrix service is specifically made for products that use route optimization to power scheduling operations.
Route optimization (TSP, PDP, VRP, ...) requires the usage of a distance matrix detailing the travel time and distance from every location to every other location in the solve request.
## Traffic unaware (OSM)
> POST `/table`
```json theme={null}
{
"coordinates": [
[4.912342132, 50.2123123],
[4.812341234, 50.4123421],
[5.012341342, 50.9124312],
[5.057869730, 50.4940923]
]
}
```
```json theme={null}
{
"createdAt": "2025-05-15T12:43:45.756Z",
"id": 4309631,
"status": "IN_PROGRESS",
"tableResponseId": 4309631,
"updatedAt": "2025-05-15T12:43:45.758Z"
}
```
If you have small matrices and want to integrate a fast synchronous service, use the `/table/sync` endpoint.
> GET `/table/4309631/status`
```json theme={null}
{
"createdAt": "2025-05-15T12:43:45.756Z",
"id": 4309631,
"status": "COMPLETED",
"tableResponseId": 4309631,
"updatedAt": "2025-05-15T12:43:46.012Z"
}
```
Make sure you test this endpoint with a browser or that supports gzip compression.
> GET `/table/4309631/response`
```json [expandable] theme={null}
{
"destinations": [
{
"hint": "eX5AgduYjYYsAAAAlAAAAHIAAAAAAAAAxBf6QX7HzELIz51CAAAAACwAAACUAAAAcgAAAAAAAAAVtwAAlMNKAIj__QKgxEoAwP39AgEAXwRPrDVe",
"distance": 54.20808645369059,
"name": "Avenue de la Restauration",
"location": [
4.899732,
50.200456
]
},
{
"hint": "o_v2gPMxk4KXAAAAQwAAAJsAAADWAwAA4YOXQ0HtBEOh05pDGnn1RJcAAABDAAAAmwAAANYDAAAVtwAAEyhJAG0BAQMAPkkAAAsBAwMAXxRPrDVe",
"distance": 483.4921257061384,
"name": "Route des Trois Communes",
"location": [
4.794387,
50.397549
]
},
{
"hint": "WvwMgrAseoRNAAAAAAAAALwAAAAAAAAAvYZVQgAAAAAK0AFDAAAAAE0AAAAAAAAAvAAAAAAAAAAVtwAAI1ZMAPmtCANAS0wAIKwIAwYAXw9PrDVe",
"distance": 202.89814553989248,
"name": "Wittebos",
"location": [
5.002787,
50.900473
]
},
{
"hint": "GtVBggTgEIkDAAAADwAAAMUAAAAAAAAAbWKjQHMstUHaEZlDAAAAAAMAAAAPAAAAxQAAAAAAAAAVtwAA-Q5NAN2rCAOQDk0AIKwIAwsAHw5PrDVe",
"distance": 10.496399718824598,
"name": "Tiensestraat",
"location": [
5.050105,
50.899933
]
}
],
"durations": [
[
0,
1976.1,
5772.3,
5642.6
],
[
2010.2,
0,
4783.9,
4654.2
],
[
5817.2,
4797.4,
0,
508.6
],
[
5705.3,
4685.5,
530.2,
0
]
],
"sources": [
{
"hint": "eX5AgduYjYYsAAAAlAAAAHIAAAAAAAAAxBf6QX7HzELIz51CAAAAACwAAACUAAAAcgAAAAAAAAAVtwAAlMNKAIj__QKgxEoAwP39AgEAXwRPrDVe",
"distance": 54.20808645369059,
"name": "Avenue de la Restauration",
"location": [
4.899732,
50.200456
]
},
{
"hint": "o_v2gPMxk4KXAAAAQwAAAJsAAADWAwAA4YOXQ0HtBEOh05pDGnn1RJcAAABDAAAAmwAAANYDAAAVtwAAEyhJAG0BAQMAPkkAAAsBAwMAXxRPrDVe",
"distance": 483.4921257061384,
"name": "Route des Trois Communes",
"location": [
4.794387,
50.397549
]
},
{
"hint": "WvwMgrAseoRNAAAAAAAAALwAAAAAAAAAvYZVQgAAAAAK0AFDAAAAAE0AAAAAAAAAvAAAAAAAAAAVtwAAI1ZMAPmtCANAS0wAIKwIAwYAXw9PrDVe",
"distance": 202.89814553989248,
"name": "Wittebos",
"location": [
5.002787,
50.900473
]
},
{
"hint": "GtVBggTgEIkDAAAADwAAAMUAAAAAAAAAbWKjQHMstUHaEZlDAAAAAAMAAAAPAAAAxQAAAAAAAAAVtwAA-Q5NAN2rCAOQDk0AIKwIAwsAHw5PrDVe",
"distance": 10.496399718824598,
"name": "Tiensestraat",
"location": [
5.050105,
50.899933
]
}
]
}
```
Prefer the signed url approach through the `/table/{id}/response/url` which redirects to Google Cloud Storage over our API.
## Traffic aware (TomTom)
Solvice Maps has integrated TomTom road segment information in order to produce Traffic-aware routing. It is based on slices of adjusted travel time information.
To get Traffic aware distance matrices, you need to set the `departureTime` as well as setting the routing `"engine": TOMTOM`.
At Solvice, we use the following slices:
| Period | From | To |
| ------------- | ----- | ----- |
| NIGHT | 20:30 | 06:30 |
| MORNING\_RUSH | 06:30 | 09:30 |
| MORNING | 09:30 | 12:00 |
| MIDDAY | 12:00 | 14:30 |
| AFTERNOON | 14:30 | 16:30 |
| EVENING\_RUSH | 16:30 | 19:30 |
In between slices, you can get interpolated values by setting the property `"interpolate": true`.
Like so:
> POST `/table`
```json theme={null}
{
"coordinates": [
[4.912342132, 50.2123123],
[4.812341234, 50.4123421],
[5.012341342, 50.9124312],
[5.057869730, 50.4940923]
],
"departureTime": "2025-06-25T14:30:00",
"engine": "TOMTOM",
"interpolate": true
}
```
```json theme={null}
{
"createdAt": "2025-05-15T12:43:45.756Z",
"id": 4309631,
"status": "IN_PROGRESS",
"tableResponseId": 4309631,
"updatedAt": "2025-05-15T12:43:45.758Z"
}
```
## Real-time traffic (TomTom)
For matrices that should reflect *current* traffic conditions, use the `TOMTOM_REAL_TIME` engine on the synchronous `/table/sync` endpoint. Real-time matrices are computed against live road conditions and are not cacheable, so they are sync-only. Live traffic is combined with TomTom **historical traffic patterns** by default; set `"includeTrafficPatterns": false` to use live traffic only.
> POST `/table/sync`
```json theme={null}
{
"coordinates": [
[4.35171, 50.85034],
[4.40269, 50.83712],
[4.44216, 50.63650],
[4.63099, 50.58673]
],
"sources": [0, 1],
"annotations": ["duration"],
"engine": "TOMTOM_REAL_TIME"
}
```
With this request, coordinates `0` and `1` are sources and coordinates `2` and `3` are destinations, so the response is a 2 × 2 matrix:
```json theme={null}
{
"durations": [
[1875.2, 2654.1],
[1812.4, 2598.9]
]
}
```
### Constraints
* **Endpoint** — `POST /table/sync` only. `TOMTOM_REAL_TIME` is rejected on the async `POST /table` endpoint with HTTP 400.
* **`sources` is required** and must be a contiguous range starting at `0` (e.g. `[0,1,2]`). The first `sources.size` coordinates are sources, the remainder are destinations. The result matrix has shape `sources.size × (coordinates.size − sources.size)` — it is **not** padded to a square matrix.
* **`destinations` is not supported** — leave it out and rely on the `sources` split above.
* **`annotations`** must not include `"distance"`; the upstream provider does not return distances in real-time matrix mode. Use `["duration"]` (or omit the field).
* **`departureTime` is ignored** — every request uses the current time.
* **Billing** is per destination (`coordinates.size − sources.size`), not per matrix cell.
# /table
Source: https://maps.solvice.io/table/post-table
POST /table
Request a distance matrix asynchronously. Returns a table ID to poll status via /table/{id} and fetch results via /table/{id}/response. Use /table/{id}/progress for progress on large matrices (e.g. 2000x2000+).
# /table/sync
Source: https://maps.solvice.io/table/sync-table
POST /table/sync
Request a distance matrix synchronously. Returns the matrix response directly instead of creating an asynchronous job. Limited to 50x50 matrices.
# /table/upload
Source: https://maps.solvice.io/table/upload-table
POST /table/upload
Upload a pre-computed distance matrix. Use the returned ID to fetch the table via /table/{id}.
# Technical architecture
Source: https://maps.solvice.io/technical-architecture
# Solvice Maps: Technical Architecture
## Architecture Overview
Solvice Maps implements a sophisticated, cloud-native microservices architecture designed for high performance, scalability, and reliability. The system combines multiple routing engines with intelligent request processing, caching, and optimization to deliver sub-50ms routing calculations at enterprise scale.
## System Architecture Diagram
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Client Apps │────│ Load Balancer │────│ API Gateway │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
┌───────────────────────────────┼───────────────────────────────┐
│ │ │
┌───────▼────────┐ ┌──────▼──────┐ ┌────────▼────────┐
│ MapR Gateway │ │ OSM Service │ │ External APIs │
│ (Quarkus) │ │ (Node.js) │ │ (TomTom/Google) │
└───────┬────────┘ └──────┬──────┘ └─────────────────┘
│ │
┌───────────────┼──────────────────────────────┼───────────────┐
│ │ │ │
┌───────▼──────┐ ┌──────▼──────┐ ┌───────▼───────┐ ┌─────▼─────┐
│ PostgreSQL │ │ Pub/Sub │ │ Storage │ │Monitoring │
│ Database │ │ (Events) │ │ (Results) │ │& Metrics │
└──────────────┘ └─────────────┘ └───────────────┘ └───────────┘
```
## Core Components
### 1. MapR Gateway (Primary API Service)
**Technology Stack:**
* **Framework**: Quarkus (Java/Kotlin)
* **Language**: Kotlin 1.9 with JVM 17
* **Database**: PostgreSQL with Hibernate ORM
* **Authentication**: JWT (HS256) tokens
* **Messaging**: Google Cloud Pub/Sub
**Responsibilities:**
* Primary API endpoint for all routing requests
* Request validation, authentication, and rate limiting
* Intelligent request splitting for large matrices
* Content-based caching and deduplication
* Multi-engine routing coordination
* Result aggregation and response formatting
**Key Architectural Patterns:**
* **Layered Architecture**: Controllers → Services → Repositories
* **Event-Driven Processing**: Pub/Sub for asynchronous operations
* **Proxy Pattern**: Dynamic client creation for external APIs
* **Circuit Breaker**: Fault tolerance for external services
### 2. OSRM Integration Service
**Technology Stack:**
* **Framework**: NestJS (Node.js/TypeScript)
* **Runtime**: Node.js 22 with clustering
* **OSRM**: Native C++ bindings (Project-OSRM)
* **Load Balancing**: Weighted round-robin
* **Deployment**: Google Kubernetes Engine
**Responsibilities:**
* Direct integration with OSRM routing engines
* Traffic slice management (time-dependent routing)
* Interpolation for smooth traffic transitions
* High-performance routing calculations
* Pub/Sub message processing for batch operations
**Advanced Features:**
* **Multi-Instance Management**: Load multiple OSRM instances per region
* **Traffic Slice Interpolation**: Decimal slice support (e.g., 2.3, 4.7)
* **Memory Optimization**: Memory-mapped files for OSRM data
* **Batch Processing**: Configurable concurrency limits
### 3. Infrastructure Layer
**Cloud Platform**: Google Cloud Platform (GCP)
* **Compute**: Google Compute Engine with Container-Optimized OS
* **Container Orchestration**: Google Kubernetes Engine (GKE)
* **Load Balancing**: Global HTTP(S) Load Balancer
* **Storage**: Cloud Storage for large results
* **Messaging**: Cloud Pub/Sub for event processing
* **Monitoring**: Cloud Monitoring with custom metrics
## Data Flow Architecture
### 1. Synchronous Request Flow (Routes, Small Tables)
```
1. Client Request → API Gateway (Auth/Validation)
2. Gateway → MapR Gateway (Request Processing)
3. MapR Gateway → Routing Engine Selection
4. Engine Processing → Response Generation
5. Response → Gateway → Client
Timeline: 10-50ms end-to-end
```
### 2. Asynchronous Request Flow (Large Tables, Cubes)
```
1. Client Request → MapR Gateway (Validation)
2. Gateway → Request Splitting (if needed)
3. Child Requests → Pub/Sub Publishing
4. Pub/Sub → OSRM Service Processing
5. Results → Cloud Storage
6. Completion Event → Response Aggregation
7. Client Polling/Webhook → Final Results
Timeline: 30 seconds to 10+ minutes depending on size
```
### 3. Caching and Optimization Flow
```
1. Request → Hash Generation (Content-based)
2. Cache Lookup → PostgreSQL
3. Cache Hit → Direct Response (sub-10ms)
4. Cache Miss → Engine Processing → Cache Store
5. Future Identical Requests → Cache Hit
Cache Hit Rate: 60-80% for typical workloads
```
## Routing Engine Integration
### Engine Architecture
**Multi-Engine Support:**
```kotlin theme={null}
interface RoutingEngine {
fun calculateRoute(request: RouteRequest): RouteResponse
fun calculateTable(request: TableRequest): TableResponse
fun isAvailable(): Boolean
fun getRegionSupport(): List
}
```
**Implemented Engines:**
* **OSM/OSRM**: Self-hosted, high-performance, free
* **TomTom**: Commercial API with real-time traffic
* **AnyMap**: European-focused routing service
* **Google Maps**: Global coverage with comprehensive data
### Engine Selection Logic
**Automatic Engine Selection:**
```kotlin theme={null}
fun selectEngine(request: RoutingRequest): RoutingEngine {
val region = detectRegion(request.coordinates)
val engines = getAvailableEngines(region)
return engines.filter { it.isAvailable() }
.sortedBy { it.getPriority() }
.first()
}
```
**Selection Criteria:**
1. **Geographic Coverage**: Engine support for request region
2. **Request Size**: Engine limits and capabilities
3. **Performance Requirements**: Response time vs. accuracy
4. **Cost Optimization**: Usage-based routing decisions
### Traffic Slice Management
**Time-Dependent Routing:**
* **Integer Slices (0-12)**: Direct OSRM instance calls
* **Decimal Slices (2.3, 4.7)**: Linear interpolation between adjacent slices
* **Traffic Patterns**: Different profiles for weekdays vs. weekends
* **Real-time Selection**: Current time-based slice selection
**Interpolation Algorithm:**
```typescript theme={null}
function interpolateSlice(slice: number, lowerResult: Result, upperResult: Result): Result {
const weight = slice % 1; // Decimal portion
return {
duration: lowerResult.duration * (1 - weight) + upperResult.duration * weight,
distance: lowerResult.distance * (1 - weight) + upperResult.distance * weight,
geometry: lowerResult.geometry // Use lower slice geometry
};
}
```
## Database Architecture
### Schema Design
**Core Entities:**
```sql theme={null}
-- Main request tracking
CREATE TABLE table_requests (
id BIGSERIAL PRIMARY KEY,
hash VARCHAR(64) UNIQUE, -- Content-based caching
parent_id BIGINT REFERENCES table_requests(id), -- Request splitting
status VARCHAR(20), -- IN_PROGRESS, SUCCEEDED, FAILED
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Cube (time-dependent) requests
CREATE TABLE cubes (
id BIGSERIAL PRIMARY KEY,
hash VARCHAR(64) UNIQUE,
nr_time_slices INTEGER,
status VARCHAR(20),
created_at TIMESTAMP
);
-- External storage references
CREATE TABLE table_responses (
id BIGSERIAL PRIMARY KEY,
table_id BIGINT REFERENCES table_requests(id),
storage_path VARCHAR(255), -- Cloud Storage path
size_bytes BIGINT
);
```
**Indexing Strategy:**
* **Hash-based lookups**: B-tree index on content hash
* **Status queries**: Index on (status, created\_at)
* **Hierarchical queries**: Index on parent\_id for request splitting
### Data Storage Strategy
**Hot Data (PostgreSQL):**
* Request metadata and status
* Small responses (\< 1MB)
* User authentication and rate limiting data
**Cold Data (Cloud Storage):**
* Large matrix results (> 1MB)
* Binary OSRM data files
* Historical analytics data
## Performance Architecture
### Performance Targets
**Response Time SLAs:**
* **Simple Routes**: \< 50ms P95
* **Small Tables (\< 100 coords)**: \< 100ms P95
* **Large Tables**: Asynchronous processing
* **API Overhead**: \< 10ms for cached responses
### Optimization Strategies
**1. Request Splitting:**
```kotlin theme={null}
fun splitLargeRequest(request: TableRequest): List {
val engineLimit = getEngineLimit(request.engine)
val sourceBatches = request.sources.chunked(engineLimit.sources)
val destBatches = request.destinations.chunked(engineLimit.destinations)
return sourceBatches.flatMap { sources →
destBatches.map { destinations →
request.copy(sources = sources, destinations = destinations)
}
}
}
```
**2. Content-Based Caching:**
```kotlin theme={null}
fun generateCacheKey(request: TableRequest): String {
val content = listOf(
request.sources.sorted(),
request.destinations.sorted(),
request.engine,
request.profile
).joinToString("|")
return SHA256.hash(content)
}
```
**3. Parallel Processing:**
```kotlin theme={null}
suspend fun processTableRequests(requests: List): List {
return requests.map { request →
async { processRequest(request) }
}.awaitAll()
}
```
## Scalability Architecture
### Horizontal Scaling
**Stateless Services:**
* All services designed for horizontal scaling
* No local state storage
* Session data in external stores (PostgreSQL, Redis)
**Auto-Scaling Triggers:**
* CPU utilization > 70%
* Request queue depth > 100
* Response time P95 > SLA threshold
**Load Distribution:**
```yaml theme={null}
# Kubernetes HPA Configuration
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mapr-gateway-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mapr-gateway
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
```
### Event-Driven Scaling
**Pub/Sub Message Processing:**
* Dynamic subscription scaling based on queue depth
* Weighted round-robin message distribution
* Dead letter queues for failed processing
**Asynchronous Processing Benefits:**
* Decouples API response time from computation time
* Natural backpressure handling
* Enables batch optimization strategies
## Security Architecture
### Authentication & Authorization
**Multi-Layer Security:**
```
1. API Gateway → API Key Validation
2. JWT Token → Claims Validation
3. Rate Limiting → Per-key limits
4. IP Whitelisting → Enterprise customers
```
**JWT Token Structure:**
```json theme={null}
{
"iss": "solvice-maps",
"sub": "user-id",
"aud": "maps-api",
"exp": 1640995200,
"iat": 1640991600,
"scope": ["routing:read", "tables:write"]
}
```
### Data Protection
**Encryption:**
* **In Transit**: TLS 1.3 for all API communication
* **At Rest**: Google Cloud Storage encryption
* **Database**: PostgreSQL transparent data encryption
**Access Controls:**
* **Service Accounts**: GCP IAM with minimal permissions
* **Network Segmentation**: VPC isolation
* **Secrets Management**: Google Secret Manager
## Monitoring and Observability
### Metrics Collection
**Custom Metrics:**
```typescript theme={null}
// OSRM service metrics
@Counter('osrm_requests_total', ['method', 'status', 'team'])
osrmRequestsTotal: Counter;
@Histogram('osrm_request_duration_seconds', ['method', 'team'])
osrmRequestDuration: Histogram;
@Gauge('osrm_active_connections', ['engine'])
osrmActiveConnections: Gauge;
```
**Infrastructure Metrics:**
* Request throughput and latency
* Database connection pool utilization
* Memory and CPU usage per service
* External API response times and error rates
### Distributed Tracing
**OpenTelemetry Integration:**
```kotlin theme={null}
@WithSpan("process-table-request")
suspend fun processTableRequest(request: TableRequest): TableResponse {
val span = Span.current()
span.setAttribute("table.size", request.sources.size * request.destinations.size)
span.setAttribute("routing.engine", request.engine.toString())
return try {
val result = routingEngine.calculateTable(request)
span.setStatus(StatusCode.OK)
result
} catch (e: Exception) {
span.recordException(e)
span.setStatus(StatusCode.ERROR)
throw e
}
}
```
### Health Monitoring
**Health Check Endpoints:**
* `/health/live`: Basic service liveness
* `/health/ready`: Service readiness (dependencies available)
* `/health/engines`: Routing engine status
* `/health/database`: Database connectivity
## Error Handling and Resilience
### Circuit Breaker Pattern
```kotlin theme={null}
@Component
class RoutingEngineCircuitBreaker {
private val circuitBreaker = CircuitBreaker.ofDefaults("routing-engine")
fun callEngine(request: RoutingRequest): RoutingResponse {
return circuitBreaker.executeSupplier {
externalRoutingEngine.process(request)
}
}
}
```
### Retry Strategies
**Exponential Backoff:**
```kotlin theme={null}
val retryConfig = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofSeconds(1))
.exponentialBackoffMultiplier(2.0)
.retryOnException { it is TemporaryRoutingException }
.build()
```
### Graceful Degradation
**Fallback Mechanisms:**
1. **Engine Fallback**: Switch to alternative routing engine
2. **Cached Response**: Return stale cached data with warnings
3. **Simplified Response**: Return basic distance calculations
4. **Error Response**: Structured error with retry guidance
## Data Consistency and Reliability
### Event Sourcing for Request Tracking
**Event Log:**
```kotlin theme={null}
sealed class TableEvent {
data class TableRequestCreated(val tableId: Long, val request: TableRequest)
data class TableProcessingStarted(val tableId: Long, val engine: RoutingEngine)
data class TableProcessingCompleted(val tableId: Long, val response: TableResponse)
data class TableProcessingFailed(val tableId: Long, val error: String)
}
```
### Idempotency
**Idempotent Request Processing:**
```kotlin theme={null}
@Transactional
fun processTableRequest(request: TableRequest): TableResponse {
val existingResponse = findByHash(request.hash)
if (existingResponse != null && existingResponse.isComplete()) {
return existingResponse
}
return executeNewRequest(request)
}
```
## Development and Deployment Architecture
### CI/CD Pipeline
**Automated Deployment:**
```yaml theme={null}
# GitLab CI/CD Pipeline
stages:
- test
- build
- deploy-staging
- integration-test
- deploy-production
test:
script:
- ./gradlew test
- npm test
build:
script:
- docker build -t $IMAGE_TAG .
- docker push $REGISTRY/$IMAGE_TAG
deploy-production:
script:
- kubectl set image deployment/mapr-gateway app=$IMAGE_TAG
- kubectl rollout status deployment/mapr-gateway
```
### Blue-Green Deployment
**Zero-Downtime Deployments:**
1. Deploy new version to blue environment
2. Run health checks and integration tests
3. Switch load balancer to blue environment
4. Monitor for issues, rollback if necessary
5. Decommission green environment
This technical architecture provides a robust foundation for Solvice Maps, enabling high performance, scalability, and reliability while maintaining developer productivity and operational simplicity.
# Leaflet
Source: https://maps.solvice.io/tiles/leaflet
Tutorial on how to use Leaflet JS to create a map in a web page with Solvice Maps.
```bash theme={null}
npm install --save leaflet
```
Unfortunately, we need the MapLibre GL JS library to handle the rendering of the map. You can install it using
the following command:
```bash theme={null}
npm install --save maplibre-gl
```
Copy the API key.
Do you prefer a light or dark theme? Choose one of the following styles: `light`, `dark` or `color`.
```javascript theme={null}
L.maplibreGL({
style: 'https://cdn.solvice.io/styles/light.json', // Style URL; see our documentation for more options
}).addTo(map);
```
```javascript theme={null}
style: 'https://cdn.solvice.io/styles/light.json?key=API_KEY'
```
And change API\_KEY with your own key.
## Full example
```html index.html theme={null}
Vector Map Demo (Leaflet)
```
# MapLibre
Source: https://maps.solvice.io/tiles/maplibre
Tutorial on how to use MapLibre GL JS to create a map in a web page with Solvice Maps.
```bash theme={null}
npm install maplibre-gl
```
Copy the API key.
Do you prefer a light or dark theme? Choose one of the following styles: `light`, `dark` or `color`.
```javascript theme={null}
var map = new maplibregl.Map({
container: "map",
hash: true,
center: [-122.4194, 37.7749],
zoom: 12,
style: 'https://cdn.solvice.io/styles/light.json',
});
```
```javascript theme={null}
style: 'https://cdn.solvice.io/styles/light.json?key=API_KEY'
```
And change API\_KEY with your own key.
## Full example
```html index.html theme={null}
Display a Solvice Map on a webpage
```
# Map styles
Source: https://maps.solvice.io/tiles/styles
Solvice map styles
Changing the stylo is only changing the style URL in the map object.
Check out the [Maplibre GL docs](/tiles/maplibre) or the [Leaflet docs](/tiles/leaflet) for more information.
`https://cdn.solvice.io/styles/{styleId}.json`
## Solvice White
## Solvice Light
If you really want color... 😞
## Solvice Gray
## Solvice Dark
## Solvice Black
# Vector Tiles
Source: https://maps.solvice.io/tiles/vector
If you've ever zoomed in and out of a map quickly, you probably noticed little blank squares that filled in like a mosaic. This is a map tile. Most interactive electronic maps are constructed from tiles, which are then stitched together to form a complete interactive map.
Map tiles for interactive maps come in two main flavors: raster and vector. Raster tiles have been around for decades, but are gradually being replaced by a vector tiles, a newer format which has unique advantages for most applications.
Whether you're a seasoned developer looking to improve your maps or planning your first maps integration from scratch, this guide is for you. Let's get started!
Looking to migrate your Maps to vector?
Solvice vector styles are available today! We're still finalizing a few details, but they are now usable in your
applications. To get started, jump to our switching to MapLibre section and plug in the vector style URL for your
favorite style.
## Background
### What are raster tiles?
As we mentioned in the intro, map tiles can be broadly classified as either raster or vector. Raster tiles are the simplest to understand, as they are just PNG or JPG images that get stitched together in a grid, so we'll start here.
#### Advantages
Raster tiles have been around for a long time, and even today, they have a number of unique advantages.
* Simplicity - Just about every device can render a PNG or JPG image, and it's very easy to build a performant renderer around these. Additionally, raster tiles don't require any other resources (such as fonts or icons) or special logic to render.
* Specificity - Map tiles simplify a lot of data into a form that's useful for the user. If you are working with a minimalist map style (like our Alidade Smooth family), raster tiles can be more bandwidth efficient, as they only contain the raw visible pixels.
#### Limitations
While raster tiles are battle tested and supported pretty much everywhere, they also have some inherent limitations.
* Inflexibility - You cannot change much about raster tiles. If you want to hide a certain layer, change the language of labels, or do just about anything else to alter the appearance of the tiles, you're out of luck.
* Scalability - Raster map tiles cannot be scaled up and down, meaning you need to send 4x as many pixels over the network for your maps to look good on modern "high DPI" displays. This problem also shows up when zooming in to a raster map as the tiles become blurry until the new ones are loaded.
### What are vector tiles?
Vector tiles are a newer development, and are broadly viewed as the future of digital interactive maps. Rather than pixels, vector tiles contain a mathematical description of the geometry as well as structured data about each feature on the map.
#### Advantages
* Flexibility - Vector tiles aren't just raw pixels; the actual data is preserved in the tile. This means that you can seamlessly change the language of text labels, change the color scheme to suit your brand, or even switch styles dynamically based on the time of day or the user's device preferences. You can even completely change the camera angle for a 3D perspective.
* Scalability - Since vector shapes are expressed in mathematical terms, they can be scaled up and down smoothly. For example, when the user zooms in, they'll get a smooth scale-in without any pixelation. This is especially relevant on mobile devices, where users are used to continuous zoom for most applications rather than discrete steps.
* Cost - Many vendors, including Stadia Maps, charge per tile request. Switching to vector tiles can mean significant savings, as vector tiles cover a larger area. On average, we see users switching to vector making approximately 60% fewer tile requests.
#### Limitations
* Complexity - While vector tiles provide a lot of flexibility, this comes at the cost of complexity. Tiles need to be rendered client-side, and this may create performance concerns for older or embedded applications. Additionally, since vector maps require additional resources to be present, the initial map load typically involves a greater number of network requests.
* Size - For applications only requiring a low level of detail that don't utilize any of the 3D perspective features of vector tiles, the vector tiles can weigh a bit more. However, this is somewhat offset since all zooming past level 14 can use the information in the z14 tile.
### Which should I use?
We've covered a lot of ground. To summarize, vector tiles offer greater flexibility, look great on any screen, and typically reduce costs by around 60%. However, if your application is targeting older devices or doesn't need a high level of detail (for example, our Alidade Smooth and Alidade Smooth Dark styles), raster tiles are not necessarily a bad option.
Now, let's dive into some practical tips on switching to vector tiles.
## How can I switch to vector tiles?
Switching is easy. The first step is to select a style from our gallery (you can also build your own). You'll want to copy the vector style URL. Let's dive into using vector styles with the most popular frameworks!
### Switching to MapLibre
MapLibre provides the greatest flexibility and feature support when it comes to vector tile rendering. If you want the best looking maps, a 3D camera, or dynamic styling, MapLibre is the way to go.