Maps and Location

Building Location-Aware Applications


Location data powers a surprising range of business applications. Delivery routing. Territory management. Asset tracking. Store locators. Service area definition. Field workforce coordination. Anywhere geography matters to operations, location-aware applications provide value. These features integrate naturally with web applications and mobile apps, adding spatial awareness to core business workflows.

The naive implementation is straightforward: store latitude and longitude as decimals, call Google Maps when you need a map, and use the Haversine formula for distance calculations. This works for a proof of concept. It breaks in production when you need to query "find all customers within 15km of this warehouse" against 50,000 records, or when your geocoding bill arrives, or when users in rural areas complain that addresses aren't resolving correctly.

Building location features that perform at scale, cost predictably, and handle edge cases requires understanding the full stack: coordinate systems, geocoding strategies, spatial databases, map rendering, and the privacy implications of location data. This page covers the patterns we use.


Coordinate Systems and Precision

Coordinates look simple. Latitude and longitude, two numbers. The complexity hides in the details.

WGS84: The Standard

WGS84 (World Geodetic System 1984) is the coordinate reference system used by GPS and most mapping services. Coordinates are expressed as latitude (north/south, -90 to +90) and longitude (east/west, -180 to +180). Store coordinates in this format unless you have a specific reason not to.

Some legacy systems use other coordinate systems. British National Grid (EPSG:27700) is common in UK government data. UTM (Universal Transverse Mercator) appears in surveying data. PostGIS handles conversions between systems, but WGS84 should be your storage standard.

Precision Matters

Coordinate precision directly affects accuracy. Each decimal place represents roughly:

Decimal Places Approximate Precision Use Case
1 ~11 km Country-level only
2 ~1.1 km Town identification
3 ~110 m Street-level
4 ~11 m Building identification
5 ~1.1 m Door-level accuracy
6 ~10 cm Survey-grade positioning

For most business applications, 6 decimal places is sufficient. Storing more precision wastes space without adding useful accuracy (GPS itself is typically accurate to 3-5 metres under normal conditions).

Storage format: Store coordinates as DECIMAL(9,6) for latitude and DECIMAL(10,6) for longitude if using standard SQL columns. This accommodates the full range of valid coordinates with appropriate precision. Better still, use PostGIS geometry types.

The Earth Is Not Flat

The Haversine formula calculates great-circle distance between two points on a sphere. It's computationally cheap and accurate enough for most purposes. For distances under 20km, the error is negligible.

For longer distances or when accuracy matters (surveying, precise routing), use the Vincenty formula or PostGIS's geography type, which accounts for the Earth's ellipsoidal shape. The difference between spherical and ellipsoidal calculations can be up to 0.5% over long distances.


Geocoding: Addresses to Coordinates

Converting addresses to coordinates (forward geocoding) and coordinates to addresses (reverse geocoding) is fundamental. It's also where naive implementations become expensive.

The Naive Approach

Call a geocoding API every time you need coordinates. Google Maps Geocoding API, Mapbox, or HERE. Pass the address string, get back latitude and longitude.

This works until:

  • You process a bulk import of 10,000 customer addresses and the API bill arrives
  • The API rate-limits you mid-import and the job fails
  • Users experience latency spikes during address entry
  • The same addresses get geocoded repeatedly across different features

The Robust Pattern: Geocode Once, Cache Forever

Geocode addresses on first encounter, store the result, and never geocode the same address twice. This requires:

Address Normalisation

Before geocoding, normalise addresses to a canonical form. "10 Downing Street, London" and "10 Downing St, London, UK" should resolve to the same cache key. Strip whitespace, standardise abbreviations, uppercase for comparison. This prevents duplicate geocoding of equivalent addresses.

Geocode Results Table

Store geocoded results with the normalised address as key, coordinates, provider used, confidence score, and timestamp. Index on the normalised address for fast lookups. Include the raw API response for debugging.

Batch Processing

Queue geocoding jobs for bulk imports rather than processing synchronously. Respect API rate limits. Retry failed geocodes with exponential backoff. Alert on systematic failures (which often indicate address data quality issues). For robust queue handling patterns, see our approach to background jobs.

Provider Selection

Geocoding providers vary significantly in accuracy, coverage, and cost.

Provider Strengths Considerations
Google Maps Excellent global coverage, high accuracy, address autocomplete Most expensive, strict terms (results must display on Google Maps)
Mapbox Good accuracy, generous free tier, flexible terms Weaker in some regions, less detailed POI data
HERE Strong European coverage, good batch processing, enterprise support Complex pricing, heavier API
Nominatim (OSM) Free, no terms restrictions, self-hostable Variable accuracy, limited commercial support, rate limits on public servers
Postcodes.io (UK) Free UK postcode lookups, fast, no rate limits UK only, postcode centroid (not door-level)

For UK-focused applications, a hybrid approach works well: use Postcodes.io for initial postcode-based lookups (free, fast), then fall back to Google or Mapbox for full address resolution when door-level precision is needed.

Address Autocomplete

Address autocomplete provides suggestions as users type, reducing errors and improving data quality. Google Places Autocomplete is the standard, but charges per session (a session covers all keystrokes until selection).

Implementation pattern: debounce input (wait 300ms after typing stops before querying), restrict to relevant countries, bias results toward user location, and validate the final selected address with a geocode call.

Terms of service: Google's terms require displaying autocomplete results on a Google Map. If you're using Mapbox or Leaflet for display, you need a different autocomplete provider. Mapbox and Algolia Places offer alternatives with more flexible terms.


Spatial Databases: PostGIS

Storing coordinates as decimal columns works for simple cases. Once you need to answer questions like "find all locations within 10km" or "which delivery zone contains this address", you need a spatial database.

The Naive Approach

Store latitude and longitude as decimals. Calculate distances in application code. For "find nearest", pull all records and sort by calculated distance.

This fails at scale. Calculating distance for 50,000 records takes seconds. The database can't use indexes for distance queries because the calculation happens after retrieval. Memory usage spikes when loading all records for in-application filtering.

The Robust Pattern: PostGIS

PostGIS extends PostgreSQL with spatial types and functions. Coordinates become geometry or geography objects. Spatial indexes enable fast queries against millions of records.

Geometry vs Geography Types

PostGIS offers two spatial types with different calculation methods:

geometry

Planar calculations (flat Earth). Faster. Suitable for small areas where curvature is negligible. Units depend on coordinate system (degrees for WGS84).

Use for: building floor plans, city-scale analysis, when performance matters more than precision.

geography

Spheroidal calculations (curved Earth). Slower but accurate over long distances. Results in metres. Automatically handles coordinate wrapping.

Use for: country-wide queries, international applications, when accuracy matters.

For most business applications, geography is the better default. The performance difference is measurable but rarely significant with proper indexing.

Essential Spatial Queries

PostGIS provides functions for common spatial operations:

ST_DWithin: Find Within Distance

SELECT * FROM locations WHERE ST_DWithin(location, ST_MakePoint(-0.1276, 51.5074)::geography, 10000)

Returns all locations within 10km of the given point. Uses spatial indexes. Fast even against millions of records.

ST_Distance: Calculate Distance

SELECT name, ST_Distance(location, ST_MakePoint(-0.1276, 51.5074)::geography) as distance FROM locations ORDER BY distance LIMIT 10

Returns the 10 nearest locations with exact distances in metres. Combine with ST_DWithin for performance (filter first, then calculate exact distances).

ST_Contains: Point in Polygon

SELECT zone.name FROM delivery_zones zone WHERE ST_Contains(zone.boundary, ST_MakePoint(-0.1276, 51.5074)::geometry)

Returns which delivery zone contains the given point. Essential for territory assignment and service area checks.

ST_Intersects: Overlap Detection

SELECT a.name, b.name FROM territories a, territories b WHERE a.id != b.id AND ST_Intersects(a.boundary, b.boundary)

Finds overlapping territories. Useful for validation and conflict detection.

Spatial Indexing

Spatial indexes (GiST or SP-GiST) are essential for performance. Without them, every spatial query scans the entire table. With them, PostGIS can eliminate most records before calculation.

CREATE INDEX idx_locations_geom ON locations USING GIST (location);

Index creation is automatic for geography columns in recent PostGIS versions, but verify with EXPLAIN ANALYZE that your queries are using the index.

Performance benchmark: A "find within 10km" query against 100,000 points completes in under 10ms with a spatial index. Without the index, the same query takes 2-3 seconds. Index your spatial columns.


Map Display and Rendering

Map display seems straightforward: embed a map, add markers. The complexity emerges with scale (thousands of markers), customisation requirements, and mobile performance.

Provider Options

Provider Type Best For
Google Maps Proprietary Consumer-facing apps where users expect Google. Street View. Places data.
Mapbox GL JS Vector tiles Custom styling, high performance, data visualisation. Developer-friendly.
Leaflet Library (tile-agnostic) Lightweight, works with any tile provider. Good for simple maps.
OpenLayers Library (tile-agnostic) Complex GIS applications. Steeper learning curve, more capable.
Maplibre GL JS Vector tiles (open source) Mapbox GL fork, no vendor lock-in, self-hostable.

For internal business applications, Mapbox GL JS or Maplibre typically offer the best balance of capability and cost. For consumer applications where users expect familiar UI, Google Maps may be worth the premium.

Raster vs Vector Tiles

Traditional maps use raster tiles: pre-rendered images at each zoom level. Vector tiles send raw geometry data and render on the client.

Vector tiles: Smaller payload, smooth zooming, client-side styling, better for data overlays, higher performance on modern devices.
Raster tiles: Simpler implementation, works on older devices, no WebGL requirement, easier caching.

Vector tiles are the modern standard. Use raster only when supporting legacy devices or when map styling isn't needed.

Handling Many Markers

Displaying thousands of markers causes performance problems. The browser struggles to render them all, and the map becomes unusable visually.

Marker Clustering

Group nearby markers into clusters that show a count. Expand on zoom or click. Leaflet.markercluster and Mapbox's cluster sources handle this automatically. For 10,000+ points, server-side clustering (PostGIS ST_ClusterKMeans) before sending data to the client improves initial load.

Viewport Loading

Only load markers visible in the current viewport. As the user pans or zooms, fetch new data. Requires a spatial query backend (PostGIS) that can efficiently return "points within this bounding box". Include a buffer around the viewport to prevent flickering during pan.

Vector Tile Data Layers

For very large datasets (100,000+ points), generate vector tiles from your data. Tippecanoe (from Mapbox) converts GeoJSON to vector tiles. Serve as static files or generate dynamically. The map library handles rendering efficiently using WebGL.

Performance target: Map interactions (pan, zoom) should feel instant, under 100ms. If your map stutters, you're sending too much data to the client. Move filtering and aggregation to the server.


Routing and Optimisation

Simple directions (A to B) are a solved problem. Multi-stop route optimisation with constraints is computationally hard and remains an active area of development.

Directions APIs

All major providers offer directions APIs: Google Directions, Mapbox Directions, HERE Routing, OSRM (open source). They return distance, duration, and turn-by-turn instructions for a given origin and destination.

Considerations when choosing:

  • Traffic data: Google and HERE have the best real-time traffic. Mapbox and OSRM use historical patterns.
  • Vehicle profiles: Truck routing needs height, weight, and hazmat restrictions. Not all providers support this.
  • Waypoints: Most APIs support intermediate stops but limit the number (Google: 25 waypoints).
  • Terms: Google requires displaying routes on Google Maps. Others are more flexible.

The Route Optimisation Problem

Given a set of stops, find the order that minimises total distance or time. This is the Travelling Salesman Problem (TSP), which is NP-hard. Exact solutions are computationally infeasible beyond about 20 stops.

Practical Approaches

Nearest Neighbour Heuristic

Start at the depot, always go to the nearest unvisited stop. Fast (O(n^2)), easy to implement, produces routes 20-25% longer than optimal. Good enough for informal route suggestions.

Google OR-Tools

Open-source optimisation library from Google. OR-Tools handles TSP, Vehicle Routing Problem (VRP), and variants with time windows, capacity constraints, and multiple vehicles. Python and C++ interfaces. Production-grade for medium-scale problems (hundreds of stops).

Commercial Optimisation APIs

Google Route Optimization API, Mapbox Optimization API, HERE Tour Planning. Handle large-scale problems, include traffic, return turn-by-turn directions. Cost scales with stops and complexity.

Constraints in Real Routing

Real delivery and service routing involves constraints that basic TSP ignores:

  • Time windows: Customer available 9am-12pm only. Delivery before 6pm.
  • Vehicle capacity: Van holds 50 packages or 500kg.
  • Driver hours: Maximum 8 hours driving. Mandatory breaks.
  • Priority stops: Some stops must happen first or last.
  • Skills: Some deliveries require specific equipment or certification.
  • Return to depot: Or end at a different location.

Each constraint narrows the solution space but complicates the optimisation. Commercial APIs and OR-Tools support these constraints; naive implementations don't.

Real-world result: A logistics client reduced daily route distance by 18% after implementing constraint-aware optimisation. For a fleet of 12 vehicles, this translated to measurable fuel savings and an extra 2-3 deliveries per vehicle per day.


Offline Maps and Caching

Field applications often operate where connectivity is poor or absent. Offline capability requires caching both map tiles and application data.

Map Tile Caching

Map tiles can be cached locally for offline use. The approach depends on the platform:

Mobile Native

Mapbox Mobile SDKs support offline map packs. Define a region and zoom range, download in advance. Tiles stored in SQLite. Automatic management of cache size and expiry.

Progressive Web Apps

Service workers can cache tile requests. Limited storage (varies by browser, typically 50-100MB usable). Manual cache management needed. Consider caching only frequently-accessed areas.

Desktop/Embedded

Download tile sets directly. MBTiles format (SQLite) is standard. Leaflet and OpenLayers support local tile sources. Storage is less constrained; pre-cache entire regions.

Terms of Service

Tile caching terms vary significantly by provider:

  • Google Maps: No caching permitted beyond browser/OS cache. Offline requires specific licensing.
  • Mapbox: Offline supported in mobile SDKs with active subscription. Pre-rendering for distribution requires custom agreement.
  • OpenStreetMap: Free to cache and distribute. Attribution required.

For applications requiring offline maps, OSM-based tiles (self-hosted or via providers like Mapbox with appropriate licensing) are often the most practical choice.

Offline Data Synchronisation

Caching map tiles is only half the problem. Application data (customer locations, job details, route information) also needs offline access with eventual synchronisation.

Patterns for offline data:

  • Download before departure: Sync the day's jobs before leaving. Read-only offline access.
  • Queue modifications: Store changes locally, sync when connectivity returns. Handle conflicts (same record modified by different users).
  • Optimistic UI: Show success immediately, sync in background. Roll back if sync fails.

Implementation note: For React Native and Expo applications, WatermelonDB provides a robust local database with sync capabilities. For web PWAs, IndexedDB with a sync layer handles most cases. The complexity is in conflict resolution, not storage.


Geofencing

Geofencing triggers actions when a device enters or exits a defined geographic area. Common uses: delivery arrival notifications, time tracking for field workers, asset security alerts.

Implementation Approaches

Client-Side (Mobile OS)

iOS and Android provide native geofencing APIs. Define regions, receive callbacks on entry/exit. Battery-efficient (uses cell towers and WiFi, not continuous GPS). Limited to ~100 regions per app on iOS.

Best for: user's own device, where you control the app.

Server-Side

Device reports location periodically. Server checks position against fence definitions using PostGIS ST_Contains. More flexible (unlimited fences, complex shapes), but requires position reporting infrastructure.

Best for: fleet tracking, asset monitoring, where position is already being reported.

Fence Definition

Circular fences (centre point plus radius) are simplest. Polygonal fences allow arbitrary shapes (property boundaries, irregular service areas). PostGIS handles both.

For client-side geofencing, circular regions are more battery-efficient. For server-side, polygons add no cost.

Edge Cases

  • GPS jitter: Position updates can bounce in and out of a fence boundary. Implement hysteresis (require position to be clearly inside/outside before triggering) or debounce events.
  • Tunnel/building entry: GPS signal loss can look like an exit. Don't trigger alerts on signal loss alone.
  • Small fences: GPS accuracy (3-5m in good conditions, 10-30m in urban canyons) limits useful minimum fence radius. A 10m fence will generate false triggers.

Privacy and Compliance

Location data is personal data under GDPR and similar regulations. Collection, storage, and processing require appropriate legal basis and security measures.

Legal Basis

For employee location tracking, legitimate interest may apply for delivery tracking (customers expect to know where their package is) but consent is typically required for broader monitoring. For consumer applications, consent is almost always required.

The legal basis affects what you can do with the data. Location collected for delivery tracking shouldn't be used for productivity monitoring without separate consent.

Data Minimisation

Collect only what's needed. Store only what's needed. Delete when no longer needed.

Delivery tracking: Record location at key events (pickup, delivery) rather than continuous tracking. Delete detailed tracks after delivery confirmed.
Field worker tracking: Record clock-in/out locations and job site arrivals. Aggregate or delete continuous tracks after payroll processed.
Analytics: Aggregate location data for analysis (heat maps, common routes) rather than storing individual tracks long-term.

Transparency

Users should know when location is being collected and why. Mobile apps should use location permission prompts that explain the purpose. Employees should have clear policies on what's tracked and how it's used.

Consider providing users access to their own location history. This builds trust and is often required under data subject access rights.

Security

Location data reveals patterns: where people live, work, visit regularly. Protect accordingly:

  • Encryption: TLS for transmission, encryption at rest for storage.
  • Access control: Limit who can view location data. Log access for audit.
  • Retention: Automatic deletion after retention period. Don't keep data "just in case".
  • Anonymisation: For analytics, consider k-anonymity or differential privacy techniques.

Breach risk: A location data breach is particularly sensitive. Historical location data can reveal home addresses, workplace, daily routines, and personal associations. The reputational and legal exposure from a location data breach exceeds that of many other data types.


Common Patterns

Recurring implementation patterns we use across location-aware applications:

Store Locator

1

User enters postcode or grants location permission. Use address autocomplete or Geolocation API.


2

Geocode the input (if postcode/address). Cache the result.


3

Query PostGIS for stores within radius, ordered by distance. ST_DWithin then ST_Distance.


4

Display results as list and map markers. Cluster if many results. Link to directions.

Delivery Zone Check

1

Admin defines delivery zones as polygons in a map editor. Store as PostGIS geometry.


2

Customer enters delivery address during checkout. Geocode to coordinates.


3

Query PostGIS: SELECT zone FROM delivery_zones WHERE ST_Contains(boundary, point).


4

Return zone-specific pricing, delivery windows, or "outside delivery area" message.

Field Worker Dispatch

1

Workers report location periodically via mobile app. Store in time-series table.


2

New job arrives. Geocode job address if not already done.


3

Find available workers within reasonable distance. Factor in current job queue and skills.


4

Assign job. Send notification with navigation link. Update dispatch board in real-time.


Technology Stack

Our standard stack for location-aware applications:

Database
  • PostgreSQL with PostGIS extension
  • Geography type for coordinate storage
  • GiST indexes on spatial columns
  • TimescaleDB for high-volume tracking data
Geocoding
  • Google Maps or Mapbox as primary provider
  • Postcodes.io for UK postcode lookups
  • Results cached in database
  • Queued processing for bulk operations
Map Display
  • Mapbox GL JS or Maplibre for web
  • React Native Maps or Mapbox SDK for mobile
  • Vector tiles for custom styling
  • Server-side clustering for large datasets
Routing
  • Mapbox Directions or OSRM for simple routing
  • Google OR-Tools for optimisation
  • Commercial APIs for complex constraints
  • Cached distance matrices where appropriate

What You Get

  • Accurate geocoding Addresses converted to coordinates reliably, with caching to control costs.
  • Fast spatial queries "Find nearest" and "within area" queries that perform at scale using PostGIS.
  • Interactive maps Maps that render smoothly with thousands of points and work offline when needed.
  • Efficient routing Optimised routes that account for real constraints, not just shortest path.
  • Reliable geofencing Entry/exit detection that handles GPS noise and edge cases.
  • Privacy compliance Location data handled with appropriate consent, minimisation, and security.

Location capabilities built on solid foundations: PostGIS for spatial queries, proper geocoding strategies, maps that perform, and privacy handled correctly from the start.


Build Location-Aware Features

Store locators, delivery routing, territory management, asset tracking, field workforce coordination. Location features that perform at scale, cost predictably, and handle the edge cases that naive implementations miss.

Discuss your location requirements →
Graphic Swish