System Design (High-Level Design) Module 25 – Case studies: location, mobility and logistics
Design maps, routing and ETA
Design a maps service: requirements, estimates and a first design for map tiles, routes on a road graph and arrival times from live traffic.
What you will learn
- Estimate tile traffic, edge bandwidth, tile storage and the probe stream for a national maps service
- Publish vector map tiles through a CDN without emptying the edge caches on every map update
- Compare Dijkstra, A* and contraction hierarchies by the work of a query, and apply live traffic without a rebuild
- Estimate arrival times from probe speeds, usual speeds and corrections learned from finished trips
- Convert addresses that rely on PIN codes, landmarks and DIGIPIN grid codes into points
Before you start
What you’ll learn · Before you start
What you will learn
- Estimate tile traffic, edge bandwidth, tile storage and the probe stream for a national maps service
- Publish vector map tiles through a CDN without emptying the edge caches on every map update
- Compare Dijkstra, A* and contraction hierarchies by the work of a query, and apply live traffic without a rebuild
- Estimate arrival times from probe speeds, usual speeds and corrections learned from finished trips
- Convert addresses that rely on PIN codes, landmarks and DIGIPIN grid codes into points
Before you start
On this page
A national maps service answers about 174,000 tile requests a second at its peak and takes in 400,000 speed reports a second, yet a route across 50 million road segments must come back within 300 ms. To design maps for a country, you build three systems that share one map database.
Tiles draw the map: squares at every zoom level, built ahead of time and served from a CDN, so a phone downloads only the squares on its screen. Routing finds the fastest path through a graph of junctions and road segments, preprocessed once so that a query touches only a few hundred junctions. The ETA, the estimated time of arrival, adds up the route’s segments at the speeds phones report now and the speeds usual for the hour each is reached, then applies corrections learned from finished trips. Whoever has to design maps needs all three.
About 174,000 tiles a second, and 400,000 probe points
Settle the scope first, writing down each assumption:
- Which journeys? Driving, two-wheelers and walking in one country of about 3 million km², turn by turn.
- Who calls it? 60 million daily users, and partner services, such as ride matching, that ask for many arrival times at once.
- How fresh? A jam or a closed road reaches routes within minutes, a new road the tiles within a day.
- How fast? A tile within 100 ms from the edge (CDN): One of a content delivery network's servers, placed close to users. The user's connection ends at the edge, which answers from its cache or fetches the response from the origin. More in “The life of a web request, hop by hop” and a route within 300 ms, at the p99 (99th percentile): The latency within which 99 % of requests finish, so the slowest 1 % take longer. Tail latency is read from such high percentiles, because an average hides the slowest requests. More in “Latency, throughput and percentiles”.
- How good? Most arrival times within 10 %, with no steady lean towards early or late.
So the service draws the map, geocodes, routes and predicts arrival times from live speed reports, and keeps serving its last good data when a pipeline fails. A common starting point is OpenStreetMap data, under the Open Database License: a product must credit OpenStreetMap and its contributors, and a database built from the data may be shared only under the same licence (OpenStreetMap). Its community tile servers may block heavy use and allow no offline use, so an app this size runs its own tile pipeline (OpenStreetMap Foundation).
| Quantity | Working | Result |
|---|---|---|
| Tile requests, busiest hour | 60 million × 5 map views × 20 tiles ÷ 86,400 s × 2.5 | about 174,000 a second |
| Edge bandwidth at that peak | 174,000 tiles × 25 KB × 8 bits | about 35 Gbit/s |
| Vector tiles to zoom 14 | 3 million km² ÷ 5.3 km² a tile × 4/3 for the zooms above × 25 KB | about 19 GB |
| Route requests, busiest hour | 60 million × 0.4 ÷ 86,400 s × 2.5 | about 690 a second |
| Probe points from navigating phones | 2 million phones, one report every 5 s | 400,000 a second |
| Road graph in memory | 50 million segments, as many shortcuts, 40 bytes each | about 4 GB a copy |
Two facts shape the design. Tiles carry the most traffic but cache perfectly, because a tile is the same for everyone. The heavy writing is the probe stream: 400,000 reports a second become one speed per segment every minute.
Exercise · Easy · Python
Size tile traffic, edge bandwidth, the tile set and the probe stream for a maps service
Write four functions in sizing.py for the estimate sheet of a maps service. A day has 86,400 seconds, 1 KB is 1,000 bytes, 1 GB is 10⁹ bytes and 1 Gbit is 10⁹ bits.
tile_requests_per_second(daily_users, views_each, tiles_per_view, peak_factor): tile requests a second in the busiest hour, when that hour runs atpeak_factortimes the day's average rate.edge_gbit_per_second(tiles_per_second, kb_per_tile): the bandwidth the edge servers send for that many tiles.vector_tileset_gb(area_km2, tile_km2, kb_per_tile): the storage for a vector tile set of an area, where one tile at the deepest zoom coverstile_km2; every lower zoom together adds another third of that count.probe_points_per_second(phones, seconds_between): speed reports a second from phones that each send one everyseconds_betweenseconds.
The sample tests accept each figure within 10 %, as an estimate should.
Starter code · sizing.py
DAY_SECONDS = 86_400
def tile_requests_per_second(daily_users, views_each, tiles_per_view, peak_factor):
"""Tile requests a second in the busiest hour."""
# Replace this line with your code.
return 0
def edge_gbit_per_second(tiles_per_second, kb_per_tile):
"""Gbit/s the edge sends for that many tiles a second."""
# Replace this line with your code.
return 0
def vector_tileset_gb(area_km2, tile_km2, kb_per_tile):
"""GB for a vector tile set of the area, every zoom included."""
# Replace this line with your code.
return 0
def probe_points_per_second(phones, seconds_between):
"""Speed reports a second from navigating phones."""
# Replace this line with your code.
return 0 The sample tests · test_sizing.py
import math
from sizing import edge_gbit_per_second, probe_points_per_second, tile_requests_per_second, vector_tileset_gb
def near(actual, expected):
"""Within 10%, as an estimate should be."""
return math.isclose(actual, expected, rel_tol=0.10)
def test_tiles_at_peak():
"""every view's tiles spread over the day, times the peak factor"""
assert near(tile_requests_per_second(60_000_000, 5, 20, 2.5), 173_611)
assert near(tile_requests_per_second(5_000_000, 3, 12, 3), 6_250)
def test_edge_bandwidth():
"""tiles times bytes, in gigabits"""
assert near(edge_gbit_per_second(173_611, 25), 34.7)
assert near(edge_gbit_per_second(6_250, 40), 2.0)
def test_tileset_storage():
"""the deepest zoom's tiles, a third more for the zooms above, times the bytes"""
assert near(vector_tileset_gb(3_000_000, 5.29, 25), 18.9)
assert near(vector_tileset_gb(50_000, 4.0, 30), 0.5)
def test_probe_stream():
"""one report per phone every few seconds"""
assert near(probe_points_per_second(2_000_000, 5), 400_000)
assert near(probe_points_per_second(300_000, 4), 75_000) A hint
Tile requests: daily_users * views_each * tiles_per_view / 86_400 * peak_factor. Bandwidth multiplies tiles by bytes and by 8 for bits, then divides by 10**9. The tile set counts the deepest zoom's tiles (area_km2 / tile_km2), multiplies by 4/3 for the zooms above it, then by the bytes per tile.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Checkpoint
Check the estimate
The first design: tiles, routes and speeds
The first complete design of a maps service
Text description of the diagram
The diagram shows three paths.
- The phone or browser asks a CDN edge for map tiles by address; on a miss the edge fetches the tile from tile storage, which holds vector tiles up to zoom 14.
- Requests for routes and arrival times go to the API gateway, whose geocoder turns addresses into points, and on to the routing servers, which keep the road graph in memory.
- Navigating phones also send probe points through the gateway to the traffic pipeline, which computes a speed for each road segment and sends the new speeds to the routing servers.
- The tile and graph builders turn the map data into changed tiles for tile storage and a new road graph for the routing servers.
The phone takes tiles from the CDN, which fetches misses from tile storage. Route and arrival-time requests pass the API gateway, whose geocoder turns addresses into points, to routing servers holding the road graph in memory. Phones send probe points to the traffic pipeline, which publishes speeds every minute. Builders turn map edits into changed tiles and new graphs. Follow one phone through it:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- A map view. The phone opens the map and asks the CDN edge for the tiles on its screen, each by its address. A tile is the same for everyone, so most of them are already at the edge.
- A miss. One tile is not at this edge yet. The edge fetches it from tile storage, keeps a copy for the next phone, and answers.
- An address. The rider asks for a route to an address. The API gateway's geocoder turns the address into a point on the map.
- A route. A routing server finds the fastest path in the road graph it holds in memory, and adds up the arrival time from the speed of each segment.
- Probe points. While the phone navigates, it reports its position and speed every few seconds. The gateway passes the reports to the traffic pipeline.
- New speeds. Once a minute the traffic pipeline publishes a speed for every road segment that changed, and the routing servers use it for the next routes.
- Map edits. When the map data changes, builders turn the edits into changed tiles for tile storage and a new graph for the routing servers.
The questions this design must answer:
- Raster or vector tiles? Raster tiles are images drawn on the server; Map tiles that carry shapes such as roads, rivers and labels as coordinates instead of an image, so the phone or browser draws and styles the map itself. carry shapes that the phone draws and styles itself.
- Which search? Dijkstra explores outward from the start in order of travel time; A* steers it towards the target with a lower bound.
- Contraction hierarchies: preprocessing adds shortcut edges, so a A road graph prepared once by adding shortcut edges in an order of importance, so that a route query searches from both ends only towards more important junctions. query only climbs from both ends towards more important roads.
- Multi-level Dijkstra: times across nested cells of the graph are precomputed, so new speeds apply cell by cell.
- ETA: probe speeds for the next few minutes, usual speeds for later segments, and corrections for waits at junctions.
- Addresses: PIN codes, landmarks and India Post's open grid code that names a small square of the map; a geocoder uses it beside the PIN code, the locality and landmarks. grid codes turn an Indian address into a point.
Checkpoint
Check the first design
Vector tiles stop at zoom 14 and cache better than raster tiles Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- New addresses only for changed tiles keep the edge warm through an update
- Dijkstra settles every junction nearer than the target; A* steers towards it
- A contraction hierarchy settles 75 junctions where Dijkstra settles 780
- 400,000 probe points a second become one speed per segment each minute
- Cell overlays take in new speeds; shortcuts must be rebuilt
- Arrival times need history for later segments and corrections for waits
- A PIN code narrows the search; a DIGIPIN names a 4 m square
- What gives way at 10× and 100×
- Failure drills: each path keeps serving its last good data
- Alternatives considered and rejected
- What each level is expected to cover
- A sample answer and a 45-minute self-mock
- Practise: the whole design, one question at a time
- Interview questions
- Your routing service answers routes in under a millisecond with contraction hierarchies built every night from usual speeds. Product now wants routes and arrival times to follow live traffic, with speeds changing every minute. What breaks, and how do you change the design?
- In one city, evening arrival times are 5 to 10 minutes too optimistic on most trips, although the routes look right and the traffic layer shows the jams. How do you find the cause, and what do you change?
- A new map build is published every night. For an hour after each release, tile latency doubles and the load on the tile origin rises tenfold. Explain why, and design a release that avoids it.
- Key takeaways
4 diagrams · 6 runnable examples (Python) · 3 interview answers · about 68 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Read a complete sample first: Design ride hailing like Uber or Ola: matching
References
- OSRM: Open Source Routing Machine (README) (Project OSRM)
- OSRM HTTP API documentation (v5.24.0) (Project OSRM)
- OSRM wiki: Traffic (Project OSRM)
- Copyright and License (OpenStreetMap Foundation)
- Tile Usage Policy (OpenStreetMap Foundation)
- Zoom levels (OpenStreetMap Wiki)
- Vector Tile Specification 2.1 (Mapbox)
- RFC 8246: HTTP Immutable Responses (IETF)
- Route Planning in Transportation Networks (arXiv (Bast, Delling, Goldberg, Müller-Hannemann, Pajor, Sanders, Wagner, Werneck))
- Customizable Route Planning in Road Networks (Microsoft Research (Delling, Goldberg, Pajor, Werneck))
- ETA Prediction with Graph Neural Networks in Google Maps (arXiv (Derrow-Pinion and others, CIKM 2021))
- DIGIPIN (Department of Posts, Government of India)
- DIGIPIN encoder and decoder (source code) (Department of Posts, Government of India)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress