DEV Community

Cover image for How to Draw a Single Turn-by-Turn Direction Step on a Static Map
Casey Rivers for Geoapify Maps API

Posted on

How to Draw a Single Turn-by-Turn Direction Step on a Static Map

This is the third tutorial in our series on building printable route directions. The full example is available on GitHub:

👉 Printable route directions example

In the previous tutorials, we covered how to request a route and extract turn-by-turn directions and generate a route overview image. Now we tackle a more detailed challenge: creating individual step preview images for each navigation maneuver.

When we built the printable directions demo, we wanted each turn instruction to include a small map showing the context: where the driver is coming from, where they're heading, and exactly where to turn. Text instructions like "Turn right onto Oxford Street" are clearer when paired with a visual that shows the intersection from the driver's perspective.

In this tutorial, you will learn how to extract the right coordinates from a route and render them as oriented, color-coded step previews using the Static Maps API.

Try the live demo:

➡️ View on CodePen

APIs used:

What you will learn:

  • Extract coordinates for a single step from the route
  • Draw past/next route segments in different colors
  • Add a direction arrow for the maneuver
  • Orient the map using bearing and pitch

Table of Contents

  1. Why Step Preview Images?
  2. Extract Step Coordinates
  3. Build Geometry Layers
  4. Calculate Map Bearing
  5. Generate the Image URL
  6. Explore the Demo
  7. Summary
  8. FAQ

Step 1: Why Step Preview Images?

Turn-by-turn directions are clearer with visual context. When we tested the printable directions with real users, we found that text instructions alone often left people uncertain, especially at complex intersections or when multiple turns happen in quick succession.

For each maneuver (turn, merge, roundabout), a small map image shows:

Element Purpose
Past route (gray) Where you came from
Next route (pink) Where you are going
Maneuver line (white/black) The turn itself
Map orientation Rotated so "up" is your heading

This is especially useful for:

  • Printed directions that need to be self-explanatory
  • PDF route sheets for delivery drivers
  • Turn-by-turn instruction cards
  • Any scenario where users can't interact with a live map

💡 Design insight

The color coding is intentional: gray for "past" (already traveled) and a bright color for "next" (where you're going) creates an intuitive visual language. Users understand it without explanation.


Step 2: Understand the Step Data

When you get a route from the Routing API, each step looks like this:

{
  from_index: 6338,
  to_index: 6405,
  instruction: {
    text: "Turn right onto Unter den Linden/B 2/B 5."
  },
  distance: 1389,
  time: 102.587
}
Enter fullscreen mode Exit fullscreen mode

The from_index tells you where this step starts in the route's coordinate array. The route geometry contains all the coordinates:

routeData.geometry.coordinates[0] = [
  [11.582, 48.135],  // index 0
  [11.583, 48.136],  // index 1
  ...
  [13.404, 52.520]   // index 6405 (last point)
]
Enter fullscreen mode Exit fullscreen mode

To create a step preview, we need to extract three segments:

// For a route with 2 waypoints (Munich to Berlin), there's only one leg (index 0)
const coordinates = routeData.geometry.coordinates[0];
const turnCoordinate = coordinates[step.from_index];

// Extract coordinate segments around the turn
const past = getRelatedCoordinates(coordinates, step, 'past');    // Where you came from
const next = getRelatedCoordinates(coordinates, step, 'next');    // Where you're going
const maneuver = getRelatedCoordinates(coordinates, step, 'manoeuvre');  // The turn itself
Enter fullscreen mode Exit fullscreen mode

Each segment is a series of coordinates formatted as a comma-separated string for the Static Maps API.

ℹ️ Index handling for multi-leg routes

The example above assumes a single-leg route (one origin to one destination). For routes with multiple waypoints, you'll need to track which leg you're in and adjust the coordinate array accordingly. The from_index values are relative to each leg's geometry, not the entire route.


Step 3: Draw Colored Route Segments

Now we create the visual layers to draw on the static map. Each segment gets a different color:

const geometries = [];

// Gray line - where you came from
geometries.push(`polyline:${past};linewidth:5;linecolor:${encodeURIComponent('#ad9aad')}`);

// Pink line - where you're going
geometries.push(`polyline:${next};linewidth:5;linecolor:${encodeURIComponent('#eb44ea')}`);

// White line with black border - the turn maneuver
geometries.push(`polyline:${maneuver};linewidth:7;linecolor:${encodeURIComponent('#333333')}`);
geometries.push(`polyline:${maneuver};linewidth:5;linecolor:${encodeURIComponent('#ffffff')}`);

// White arrow - shows direction
geometries.push(`polygon:${maneuverArrow};fillcolor:${encodeURIComponent('#ffffff')}`);
Enter fullscreen mode Exit fullscreen mode

The Static Maps API will draw these layers on top of each other, creating the final image with:

  • Gray past route - shows where you came from
  • Pink next route - shows where you're heading
  • White bordered maneuver - highlights the turn
  • Direction arrow - points which way to go

IMAGE_PLACEHOLDER: Step preview showing gray past route, pink next route, and white maneuver line with black border on a tilted map view

Different colored segments help users quickly understand the navigation context.

💡 Layer order matters

The Static Maps API draws layers in the order you specify them. We draw the past route first, then the next route, then the maneuver line on top. This ensures the turn itself is always visible, even if routes overlap.


Step 4: Orient the Map

For a good navigation view, the map should be rotated so "forward" is pointing up. This is how navigation apps work: you're always looking in the direction you're traveling, not at a north-oriented map.

We calculate which direction you're traveling at this turn:

// Calculate bearing (add 180 to rotate map so we're looking forward)
const bearing = calculateBearing(coordinates, step.from_index) + 180;
Enter fullscreen mode Exit fullscreen mode

The bearing is the compass direction (0-360°) you're heading. Adding 180° rotates the map so you're always looking "forward" from the driver's perspective.

💡 Why add 180°?

The bearing tells us which direction the driver is facing. But the Static Maps API's bearing parameter rotates the map itself. If the driver is heading east (bearing 90°), we want east to point up on the map. Adding 180° achieves this rotation.


Step 5: Create the Static Map Image

Finally, build the URL with all the pieces:

const markerCoordinates = `${turnCoordinate[0]},${turnCoordinate[1]}`;

const imageUrl = `https://maps.geoapify.com/v1/staticmap?` +
  `style=osm-bright` +
  `&width=300&height=200` +
  `&center=lonlat:${markerCoordinates}` +
  `&zoom=16` +
  `&bearing=${bearing}` +
  `&pitch=45` +
  `&geometry=${geometries.join('|')}` +
  `&apiKey=${apiKey}`;
Enter fullscreen mode Exit fullscreen mode

This URL creates a 300x200 pixel image:

  • Centered on the turn coordinate
  • Rotated to match the direction of travel
  • Tilted at 45° for a 3D effect
  • With all the colored route segments overlaid

Key parameters

Parameter Description
geometry Pipe-separated polyline layers
center Turn coordinate (map center)
bearing Map rotation angle (0-360°)
pitch Tilt angle (0-60°) for 3D effect
zoom Street-level view (typically 15-17)

The pitch=45 gives a slight 3D tilt, making the step preview more visually engaging.

IMAGE_PLACEHOLDER: Step preview with 45-degree pitch showing 3D perspective view of the street with buildings and route lines

The pitch parameter adds depth to the map, making turns easier to understand.

ℹ️ Choosing the right zoom level

Zoom 16 works well for urban areas where streets are close together. For highway exits or rural roads, you might want zoom 14-15 to show more context. The right zoom depends on your use case, so experiment with different values.


Step 6: Explore the Demo

The live CodePen demo lets you:

  1. Load a route from Munich to Berlin
  2. Select any step from a dropdown
  3. See the step preview image
  4. View the URL breakdown

Experiment with the code

Open the demo in CodePen and try modifying the JavaScript:

  • Change pitch from 0 to 60 to see the 3D effect
  • Adjust zoom between 14-18 for different detail levels
  • Change colors for past/next/maneuver segments
  • Try different map styles (dark-matter, positron)


Summary

Creating step preview images requires more work than a simple route overview, but the result is significantly more useful for printed directions. The key techniques are:

  1. Extract step coordinates from the route geometry using index values
  2. Build layered geometries showing past, next, and maneuver segments with distinct colors
  3. Calculate bearing to orient the map in the direction of travel
  4. Generate step preview URLs with the Static Maps API, including pitch for depth

These images can be generated on-demand or pre-rendered and cached for frequently used routes. In the next tutorial, we'll add elevation profiles to give users insight into the terrain along their route.

Useful links:


FAQ

Q: Why use different colors for past and next routes?

A: Color coding provides instant visual context. Users can quickly see "where I was" (gray) vs "where I am going" (pink) without reading the instruction text.

Q: What is the bearing parameter?

A: Bearing rotates the map so that "up" matches the direction of travel. A bearing of 0° points north, 90° points east, 180° points south, and 270° points west.

Q: Can I add custom icons or text to the step preview?

A: Yes. Use the marker parameter to add icons with custom colors, sizes, and text. See the Map Marker Icon API for options.

Q: What is the pitch parameter?

A: Pitch tilts the map from 0° (flat, top-down) to 60° (maximum tilt). A pitch of 45° gives a slight 3D perspective that makes navigation clearer.

Q: How do I handle roundabouts or complex intersections?

A: For roundabouts, extract more coordinates in the maneuver segment (e.g., -10 to +20 instead of -5 to +10) to show the circular path. The bearing calculation will orient the map correctly.

Q: Can I generate these images server-side?

A: Yes. The Static Maps API works from any HTTP client. Generate the URLs server-side and either serve the images directly or cache them for faster delivery.

Q: Why use two polylines for the maneuver (black + white)?

A: Layering a thinner white line over a thicker black line creates a bordered effect that stands out against both light and dark backgrounds, making the turn more visible.


What's Next?

This tutorial covered individual step preview images. In the next part of the series, we'll add elevation profiles: charts that show the terrain along the route, useful for hiking, cycling, and understanding the difficulty of a journey.

👉 Part 4: Create a Route Elevation Profile with Chart.js


Try It Now

👉 Open the Live Demo

Please sign up at geoapify.com and generate your own API key to start creating printable turn-by-turn directions.

Top comments (1)

Collapse
 
dasfluchen profile image
DasFluchen

In no way am I discounting this post, tech, or the author in anyway!!

However, I'd like to point out that this post is circling back to a B.C.* custom ... AAA paper Triptiks. magazine.northeast.aaa.com/daily/t...

Reminds me of the girl who came up with the idea of having a mobile phone just for the home.

  • Before Computers

This post sponsored by Carl's Jr.