A web application for displaying interactive routes and locations on a map, built on OpenStreetMap and Leaflet.js. Routes are calculated via the OpenRouteService API; locations are geocoded via Nominatim. Some tile layers require a free API key.
| File | Description |
|---|---|
example-config.js |
Configuration template — copy to config.js and fill in your API keys |
maprouter.php |
Main application — handles single and multiple routes, standalone locations, all tile layers and all URL parameters |
mapbuilder.php |
Browser-based GUI for building maprouter.php URLs — no installation needed |
maprouter-builder.py |
Interactive Python command-line script to build and open a maprouter.php URL |
mapicons.html |
Visual reference sheet of all 854 available map icons with live filter search |
differences.md |
Comparison between the legacy getroute.php and multiroute.php (superseded by maprouter.php) |
staticmap.php |
Static image proxy — returns a PNG map image via Mapbox Static API; accepts the same route/location/color parameters as maprouter.php |
config.php |
Server-side configuration — holds the Mapbox API token for staticmap.php |
The map is rendered with Leaflet.js. The default tile layer is OpenStreetMap; additional layers are available via the layer URL parameter.
Routes are calculated by the OpenRouteService API. Pass one or more route parameters in the URL as JSON arrays. Each point supports:
point(required): address or coordinatestext(optional): popup text (supports full HTML, including<img>tags)icon(optional): URL of a custom marker icon
Example:
[
{"point": "Amsterdam", "text": "Start", "icon": "https://example.com/start.png"},
{"point": "Rotterdam", "text": "End"},
{"point": "Utrecht"}
]maprouter.php detects automatically how many routes are passed:
- Single route (
?route=[...]): total distance, travel time, and estimated arrival are shown in an overlay in the bottom-right corner of the map. - Multiple routes (
?route=[...]&route=[...]&...): each route shows its total distance and travel time in the start marker popup. No arrival time is shown.
Standalone markers (not connected by a route) are passed via the location parameter, using the same format as route points.
Example:
[
{"point": "Utrecht", "text": "Point of interest"}
]If no routes or locations are provided, the app attempts to use the browser's geolocation API. If that fails, the map falls back to Amsterdam.
The mapicons/ folder contains 854 icons for use as custom markers. Open mapicons.html in a browser for a searchable visual overview.
Icons can be referenced by their full URL, for example:
https://yourdomain.com/mapicons/campfire.png
Custom icons can be created with the included .pxd files (compatible with Pixelmator Pro, GIMP, or Photoshop).
Route lines can be customised with the color parameter. Pass a single colour or a JSON array; multiple colours are applied per segment in rotation. In multi-route mode, each ?color= parameter applies to the corresponding route.
Example:
["navy", "orange", "green", "purple"]The scripts are PHP, so a PHP-capable web server is required (e.g. Apache, Nginx, or XAMPP for local development). External API calls are made from the browser, so no server-side proxy is needed.
Ensure config.js and maprouter.php are in the same directory on your server.
Copy example-config.js to config.js and fill in your keys:
const config = {
thunderforestApiKey: "YOUR_THUNDERFOREST_API_KEY",
tracestrackApiKey: "YOUR_TRACESTRACK_API_KEY",
openRouteServiceApiKey: "YOUR_OPENROUTESERVICE_API_KEY"
};See API keys below for where to obtain each key and what the free tiers offer.
To enable static PNG map images via staticmap.php, create a config.php file in the same directory:
<?php
$mapboxToken = "YOUR_MAPBOX_TOKEN";Obtain a free Mapbox token at mapbox.com — see API keys below. The static map endpoint accepts the same route, location, and color parameters as maprouter.php and returns a PNG image.
You can build a maprouter.php URL by hand, or use one of the two builder tools below.
https://yourdomain.com/maprouter.php?route=[...]&location=[...]&layer=[...]§ion=[...]&profile=[...]
mapbuilder.php (recommended). Browser-based GUI, no installation needed — just open it in a browser. Each route stop has ▲ / ▼ buttons to reorder it within the route, and a 📍 button to move it out to a standalone location marker (if this leaves fewer than two stops in the route, the remaining stop is moved too and the route card is removed). Each location marker has a "move into route" control — pick a target route and position (start/end) and click the button to insert it back into a route as a stop.
maprouter-builder.py. An interactive Python command-line alternative. It still works correctly, but mapbuilder.php is recommended instead for ease of use — it's a visual, no-install GUI with live URL preview, whereas the Python script requires a local Python install and a terminal, and lacks the reorder/move controls described above.
python3 maprouter-builder.py
Set your domain in the BASE_URL constant at the top of the script.
Nominatim (geocoding) and the default OpenStreetMap tile layer need no key at all — everything else requires a free account. Only sign up for the ones you actually plan to use: skip Tracestrack/Thunderforest entirely if you're happy with the default OSM tiles, and skip Mapbox if you don't need staticmap.php.
| Key | Used for | Where to get it | Free tier |
|---|---|---|---|
openRouteServiceApiKey |
Route calculation (maprouter.php) |
openrouteservice.org/dev/#/signup — create an account, then generate a key from your dashboard | 2,000 requests/day, 40 requests/minute |
tracestrackApiKey |
Topographic tile layer (&layer=topo) |
tracestrack.com — sign up, then find your key on the account/API page | Free tier available (check current limits on their pricing page) |
thunderforestApiKey |
10 additional tile layers (cycle, transport, outdoors, etc.) | thunderforest.com — sign up, then copy the key from your dashboard | 150,000 tiles/month |
Mapbox token ($mapboxToken in config.php) |
Static PNG map images (staticmap.php only) |
account.mapbox.com/access-tokens — sign up, then use the default public token or create a new one | 50,000 requests/month |
Where each key goes:
openRouteServiceApiKey,tracestrackApiKey,thunderforestApiKey→config.js(see step 3 above)- Mapbox token →
config.php, as$mapboxToken(see step 4 above), only needed if you usestaticmap.php
Running out of quota? If routes stop building or tiles stop loading, you may have hit a provider's rate limit or daily quota. maprouter.php shows a popup naming the specific provider (and whether it's a rate limit or quota problem) when this happens, so check for that first before assuming something else is broken.
JSON array of route points. Repeat the parameter for multiple routes.
[{"point":"Amsterdam","text":"Start"},{"point":"Rotterdam","text":"End"}]JSON array of standalone location markers. Same format as route.
[{"point":"Utrecht","text":"Point of interest"}]Map tile layer. Options:
| Value | Description |
|---|---|
| (default) | OpenStreetMap standard layer |
topo |
Tracestrack topographic layer (API key required) |
cycle |
Thunderforest OpenCycleMap (API key required) |
transport |
Thunderforest Transport (API key required) |
landscape |
Thunderforest Landscape (API key required) |
outdoors |
Thunderforest Outdoors (API key required) |
transport-dark |
Thunderforest Transport Dark (API key required) |
spinal-map |
Thunderforest Spinal Map (API key required) |
pioneer |
Thunderforest Pioneer (API key required) |
mobile-atlas |
Thunderforest Mobile Atlas (API key required) |
neighbourhood |
Thunderforest Neighbourhood (API key required) |
atlas |
Thunderforest Atlas (API key required) |
Set to true to show per-segment distance and travel time in waypoint popups. Default: false.
OpenRouteService routing profile. Options:
| Value | Description |
|---|---|
driving-car |
Car routing (default) |
cycling-regular |
Cycling |
cycling-road |
Road cycling |
cycling-mountain |
Mountain biking |
cycling-electric |
E-bike |
foot-hiking |
Hiking |
foot-walking |
Walking (footpath focus) |
wheelchair |
Wheelchair accessible |
JSON array of one or more CSS colour strings. Multiple colours rotate per segment. In multi-route mode, repeat the parameter — one per route.
["navy", "orange"]Add &table (no value needed) to show a collapsible side panel with a detailed segment breakdown for each route. The panel lists every segment with its start point, end point, distance, and travel time, plus a totals row per route. In multi-route mode all routes appear as separate sections in the same panel.
Point labels use the text value if provided, otherwise the geocoded place name, otherwise a numbered fallback ("Point 1", "Point 2", …).
The panel can be toggled open and closed at any time using the button on the left edge of the map.
Editing controls. When the panel is open it also lets you rearrange the loaded data on the fly:
- Reorder — use the ▲ / ▼ buttons next to a route point to move it up or down within its route.
- Move a route point to a standalone marker — click the 📍 button next to a point. If this leaves fewer than two points in the route, the remaining point is also moved and the route is removed.
- Move a standalone marker into a route — in the "Location markers" section, pick a target route and position (start/end), then click the move button.
Every change updates the browser's address bar in place (via history.replaceState, no page reload) and redraws the map. A "Copy shareable URL" button at the top of the panel copies the current, edited URL to the clipboard so it can be saved or shared. Already-geocoded coordinates are reused for edits (no extra Nominatim requests), and OpenRouteService is only re-queried for the route(s) whose point sequence actually changed.
Sets the browser tab title for the map page. If omitted, the default title "Webwings OpenStreetMap Route" is used.
maprouter.php?route=[...]&title=Fietstocht%20Zeeland
Integer zoom level (1–20). Overrides the automatic fit-to-bounds behaviour.
Map only (current location or Amsterdam fallback):
https://yourdomain.com/maprouter.php
Single route with topo layer and cycling profile:
https://yourdomain.com/maprouter.php?route=[{"point":"Amsterdam","text":"Start"},{"point":"Rotterdam","text":"End"}]&layer=topo&profile=cycling-regular
Standalone location marker:
https://yourdomain.com/maprouter.php?location=[{"point":"Utrecht","text":"Point of interest"}]
Route combined with a location marker:
https://yourdomain.com/maprouter.php?route=[{"point":"Amsterdam","text":"Start"},{"point":"Rotterdam","text":"End"}]&location=[{"point":"Utrecht","text":"Point of interest"}]
Route with segment table panel:
https://yourdomain.com/maprouter.php?route=[{"point":"Amsterdam","text":"Start"},{"point":"Utrecht","text":"Via"},{"point":"Rotterdam","text":"End"}]&table
Multiple routes with individual colours:
https://yourdomain.com/maprouter.php?route=[{"point":"Amsterdam"},{"point":"Utrecht"}]&color=["navy"]&route=[{"point":"Rotterdam"},{"point":"Den Haag"}]&color=["orange"]
| Library / API | Purpose | API key required |
|---|---|---|
| Leaflet.js | Map rendering | No |
| OpenStreetMap | Default tile layer | No |
| Nominatim | Geocoding (address → coordinates) | No |
| OpenRouteService | Route calculation | Yes (free tier available) |
| Tracestrack | Topographic tile layer | Yes (free tier available) |
| Thunderforest | 10 additional map tile layers | Yes (free tier available) |
| Mapbox Static API | Static PNG map images via staticmap.php |
Yes (free tier: 50,000 req/month) |
| Date | Change |
|---|---|
| 2025 | Initial release |
| 2026-06-13 | All code, comments, and UI strings translated to English |
| 2026-06-13 | Added mapicons.html — searchable visual reference for all 854 map icons |
| 2026-06-13 | Added differences.md — detailed comparison between getroute.php and multiroute.php |
| 2026-06-13 | Added maprouter.php — unified script merging getroute.php and multiroute.php |
| 2026-06-13 | Added ?table parameter — collapsible side panel with per-route segment breakdown |
| 2026-06-13 | Replaced openroute.py with maprouter-builder.py — full interactive URL builder |
| 2026-06-13 | Added maprouter-builder.php — browser-based GUI URL builder |
| 2026-06-17 | Fixed Thunderforest tile URL domain (tile → api); added all 10 Thunderforest layers |
| 2026-06-17 | mapbuilder.php: single-stop route blocks auto-promoted to location markers |
| 2026-06-17 | mapicons.html: click popup with 3× icon preview and one-click URL copy |
| 2026-06-17 | Renamed maprouter-builder.php to mapbuilder.php |
| 2026-06-18 | Added staticmap.php — server-side static PNG map proxy using Mapbox Static API |
| 2026-06-18 | Added config.php — server-side config for Mapbox API token |
| 2026-06-18 | mapbuilder.php: added "Open in Staticmap" button alongside "Open in maprouter" |
| 2026-06-18 | staticmap.php: standalone location markers now use a star icon instead of letter L |
| 2026-06-18 | mapbuilder.php: fixed route card insertion order — cards now always appear in the correct sequence |
| 2026-06-18 | maprouter.php: added duration formatting helper; route detail panel now displays travel time correctly |
| 2026-06-18 | mapicons.html: fixed "Copy URL" button — added fallback for non-HTTPS / file:// contexts where Clipboard API is unavailable |
| 2026-06-18 | mapbuilder.php: added "Import URL" section — paste any maprouter.php URL to reverse-populate all routes, locations, and options in the sidebar |
| 2026-06-18 | Added &title= parameter — sets the browser tab title; supported in both maprouter.php and mapbuilder.php |
| 2026-06-18 | mapbuilder.php: fixed URL import — + in query strings now correctly decoded as a space |
| 2026-07-02 | maprouter.php: added a popup naming the provider when a rate limit or quota is hit (OpenRouteService/Nominatim), instead of failing silently |
| 2026-07-02 | maprouter.php: ?table panel now supports live editing — reorder route points, move a point to a standalone marker and back, with the URL kept in sync and a "Copy shareable URL" button |
| 2026-07-02 | mapbuilder.php: added reorder (▲/▼) and move (📍 / "move into route") controls so stops and location markers can be rearranged before generating the URL |
| 2026-07-02 | README: clarified that maprouter-builder.py still works correctly, but mapbuilder.php is recommended for ease of use |
| 2026-07-02 | Changed licence from MIT to PolyForm Noncommercial 1.0.0 — free for noncommercial use, commercial use requires separate permission |
| 2026-07-02 | README: added an "API keys" chapter listing every required key, what it's for, where to sign up, and free-tier limits; also fixed a broken paragraph in the staticmap.php setup step |
This project is licensed under the PolyForm Noncommercial License 1.0.0. Free to use, modify, and share for any noncommercial purpose; commercial use requires separate permission from the licensor.
Copyright © Richard, Webwings 2025
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.