This is the second tutorial in our series on building printable route directions. The full example is available on GitHub:
π Printable route directions example
In the first tutorial, we covered how to request a route and extract turn-by-turn directions from the Geoapify Routing API. Now we move to the visual side: generating a route preview image that can be printed, shared, or embedded in documents.
Interactive maps are great for exploration, but they fall short when you need to print directions, attach them to an email, or embed them in a PDF. A static map image solves this problem: it renders consistently everywhere, loads instantly, and requires no JavaScript on the viewer's side.
In this tutorial, you will learn how to take a route geometry and render it as a shareable PNG using the Geoapify Static Maps API.
Try the live demo:
β‘οΈ View on CodePen
APIs used:
- Static Maps API - generate map images with custom overlays
- Routing API - get route geometry
- Map Marker Icon API - waypoint markers
What you will learn:
- How to request a static map with route geometry
- Why POST requests are needed for large routes
- Adding waypoint markers to the map image
- Customizing line colors and map styles
Table of Contents
- Why Static Maps for Routes?
- Get the Route Geometry
- Build the Static Map Request
- Send a POST Request
- Display the Result
- Explore the Demo
- Summary
- FAQ
Step 1: Why Static Maps for Routes?
Interactive maps (Leaflet, MapLibre, Google Maps) are excellent for exploration, but they don't fit every use case. When we built the printable directions demo, we needed a way to include a route overview that would look the same whether viewed on screen or printed on paper.
| Use Case | Interactive Map | Static Map |
|---|---|---|
| Print to PDF | No | Yes |
| Email attachment | No | Yes |
| Offline viewing | Limited | Yes |
| Embed in documents | No | Yes |
| Fast page load | Slower | Faster |
Static maps are images (PNG or JPEG files) that render identically on any device without JavaScript. They're also significantly lighter: a single image request vs. loading a map library, tiles, and rendering logic.
π‘ When to choose static maps
Use static maps when the user doesn't need to pan or zoom. Route previews, confirmation screens, and printed materials are ideal candidates. If users need to explore the map interactively, stick with Leaflet or MapLibre.
The Geoapify Static Maps API lets you overlay GeoJSON geometries (like routes) and markers onto map images.
Step 2: Get the Route Geometry
First, fetch the route from the Routing API. The response includes a GeoJSON geometry we can pass to the Static Maps API.
const apiKey = "YOUR_API_KEY";
const waypoints = "48.1351,11.5820|52.5200,13.4050"; // Munich to Berlin
const routingUrl = `https://api.geoapify.com/v1/routing?waypoints=${waypoints}&mode=drive&apiKey=${apiKey}`;
// Fetch the route
const response = await fetch(routingUrl);
const data = await response.json();
const route = data.features[0];
// The route object contains:
// - route.geometry - the line coordinates
// - route.properties.waypoints - start/end locations
// - route.properties.distance - total distance
// - route.properties.time - total time
The route geometry is a MultiLineString containing all the coordinates that make up the route line.
βΉοΈ Reusing route data
If you already fetched the route for turn-by-turn directions (as shown in the first tutorial), you don't need to call the Routing API again. The same response contains everything you need for the static map.
Step 3: Build the Static Map Request
The Static Maps API accepts GeoJSON in the request body. Here is the structure:
const staticMapUrl = `https://maps.geoapify.com/v1/staticmap?apiKey=${apiKey}`;
const params = {
style: "osm-bright", // Map style
width: 800, // Image width in pixels
height: 400, // Image height in pixels
scaleFactor: 2, // 2x for retina displays
geojson: route, // The route GeoJSON
markers: [...] // Waypoint markers (optional)
};
Customize the route line
Add properties to the GeoJSON to style the route:
// Style the route line before sending
route.properties.linecolor = "#6699ff"; // Blue color
route.properties.linewidth = "5"; // 5px width
Add waypoint markers
The route response includes waypoint locations. Convert them to marker format:
const markers = route.properties.waypoints.map((waypoint) => {
return {
lat: waypoint.location[1],
lon: waypoint.location[0],
color: "#ff0000", // Red markers
size: "medium",
type: "awesome" // Pin style
};
});
Marker types include awesome (pin), material (Google style), and circle.
π‘ Choosing marker colors
For printed materials, use high-contrast colors that remain visible on grayscale printers. Red (
#ff0000) and dark blue (#003366) work well. Avoid light colors like yellow or light gray.
Step 4: Send a POST Request
Why POST instead of GET?
Route geometries can contain thousands of coordinates. URL query strings have length limits (around 2000 characters in some browsers). A POST request sends the data in the body, avoiding this limitation.
In our experience, any route longer than a few kilometers will exceed the GET limit. We recommend defaulting to POST for all route visualizations. It's more reliable and the code is just as simple.
async function getMapPreview(route) {
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
// Style the route line
route.properties.linecolor = "#6699ff";
route.properties.linewidth = "5";
// Build request body
const params = {
style: "osm-bright",
width: 800,
height: 400,
scaleFactor: 2,
geojson: route,
markers: route.properties.waypoints.map((waypoint) => ({
lat: waypoint.location[1],
lon: waypoint.location[0],
color: "#ff0000",
size: "medium",
type: "awesome"
}))
};
const requestOptions = {
method: "POST",
headers: myHeaders,
body: JSON.stringify(params)
};
// Fetch the image
const response = await fetch(
`https://maps.geoapify.com/v1/staticmap?apiKey=${apiKey}`,
requestOptions
);
return response.blob();
}
The API automatically calculates the bounding box and zoom level to fit the entire route in the image.
Step 5: Display the Result
The API returns a binary image. Convert it to a data URL for display in the browser:
const blob = await getMapPreview(route);
// Convert blob to data URL
const reader = new FileReader();
reader.onload = function() {
const img = document.createElement("img");
img.src = this.result; // data:image/png;base64,...
document.body.appendChild(img);
};
reader.readAsDataURL(blob);
For server-side use, you can save the blob directly to a file.
The generated static map shows the complete route with waypoint markers. This image can be printed or embedded anywhere.
π‘ Working with blobs
The Static Maps API returns binary image data. If you're building a server-side application, you can write the blob directly to a file. For client-side use, converting to a data URL (as shown above) lets you display or download the image without additional server infrastructure.
Step 6: Explore the Demo
The live CodePen demo shows the complete workflow:
- Fetches a route from Munich to Berlin
- Sends the geometry to the Static Maps API
- Displays the resulting image
Experiment with the code
Open the demo in CodePen and try modifying the JavaScript:
- Change
styletodark-matterorpositronfor different looks - Adjust
widthandheightfor different aspect ratios - Change marker colors or use
type: "material"for different pin styles
Available map styles
| Style | Description |
|---|---|
osm-bright |
Colorful, detailed |
osm-bright-grey |
Muted colors |
positron |
Light, minimal |
dark-matter |
Dark theme |
klokantech-basic |
Simple, clean design |
See the Map Tiles documentation for the full list.
Summary
In this tutorial, we moved from raw routing data to a visual representation that works everywhere: print, email, or embedded documents. The key steps were:
- Fetch route geometry from the Routing API (or reuse an existing response)
- Style the route line with custom color and width for visibility
- Add waypoint markers at start and end points
- Use POST requests to handle large geometries reliably
- Display the static image in the browser or save it for later use
This approach gives you a printable route preview without any mapping library on the client side. In the next tutorial, we'll go further and generate individual step preview images for each turn in the route, useful for detailed printed directions.
Useful links:
FAQ
Q: What image formats are supported?
A: The API returns JPEG by default (faster for large images). Add format=png for PNG output if you need transparency.
Q: How large can the image be?
A: Default dimensions are 1024x768 pixels. Maximum dimensions are 4096x4096 pixels (greater values available on request). Use scaleFactor=2 for high-DPI displays.
Q: Can I add multiple routes to one image?
A: Yes! Pass an array of GeoJSON features or a FeatureCollection.
Q: How do I control the zoom level?
A: By default, the API auto-fits the geometry. You can override with center (lon,lat) and zoom parameters if needed.
What's Next?
This tutorial covered the route overview image. In the next part of the series, we'll create individual step preview images: small maps that show each turn with context about where you came from and where you're going.
π Part 3: Draw a Single Turn-by-Turn Direction Step on a Static Map
Try It Now
π Open the Live Demo
Please sign up at geoapify.com and generate your own API key to start generating printable route maps.

Top comments (0)