Skip to content

Map and Tile Caching

Overview

Formbird supports offline maps through vector tile caching. Vector tiles are cached by the service worker and stored in browser cache storage, enabling map display when the device is offline. The system supports multiple tile servers including the Formbird tile server (vmap.formbird.com), Vicmaps, MapBox, and MapTiler.

Vector Tiles vs Raster Tiles

Vector Tiles

  • Smaller file size - Compact binary format (PBF)
  • Scalable rendering - Smooth zoom at any level
  • Styleable - Custom styles applied client-side
  • Faster caching - Less data to download

Raster Tiles

  • Pre-rendered images - PNG or JPEG
  • Fixed zoom levels - Blurry when scaled
  • Simpler rendering - No styling needed
  • Larger storage - More data per tile

Formbird uses vector tiles for offline maps due to their efficiency.

Tile Server Options

vmap.formbird.com

Formbird's internal tile server using OpenStreetMap data:

{
    "clientConfiguration": {
        "openLayers": {
            "tileServerGL": {
                "url": "https://vmap.formbird.com/data/v3/{z}/{x}/{y}.pbf"
            }
        }
    }
}

Vicmaps (Victoria, Australia)

Victorian Government mapping service:

{
    "tileServerGL": {
        "url": "https://vicmap.land.vic.gov.au/hosting/rest/services/Vicmap_Vector_Tile_Basemap_Hybrid_WM/VectorTileServer/tile/{z}/{y}/{x}.pbf",
        "styleLocationUrl": "https://vicmap.land.vic.gov.au/hosting/rest/services/Vicmap_Vector_Tile_Basemap_Hybrid_WM/VectorTileServer/resources/styles/root.json"
    }
}

Note: Vicmaps uses {z}/{y}/{x} order, not the standard {z}/{x}/{y}.

Available Vicmaps Basemaps:

Name URL Path
Colour Vicmap_Vector_Tile_Basemap_Colour_WM
Dark Grey Vicmap_Vector_Tile_Basemap_DarkGrey_WM
Hybrid Vicmap_Vector_Tile_Basemap_Hybrid_WM
Greyscale VicmapVectorGreyscale
Overlay Vicmap_Vector_Tile_Basemap_Overlay_WM

MapBox

Commercial mapping service:

{
    "mapParameters": {
        "mapViews": [
            {
                "uiLabel": "MapBox",
                "serverType": "tilePBF",
                "url": "https://{a-d}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token=YOUR_TOKEN"
            }
        ]
    }
}

MapTiler

Commercial mapping service:

{
    "mapParameters": {
        "mapViews": [
            {
                "uiLabel": "MapTiler",
                "serverType": "tilePBF",
                "url": "https://api.maptiler.com/tiles/v3-openmaptiles/{z}/{x}/{y}.pbf?key=YOUR_KEY"
            }
        ]
    }
}

Configuration

Basic Tile Server Configuration

{
    "clientConfiguration": {
        "openLayers": {
            "tileServerGL": {
                "url": "https://vmap.formbird.com/data/v3/{z}/{x}/{y}.pbf"
            }
        }
    }
}

Cache Bounds Configuration

Map caching configuration is defined in clientConfiguration.openLayers.mapViewsToCache. This includes the tile URL, style URL, bounds, and zoom levels:

{
    "clientConfiguration": {
        "openLayers": {
            "mapViewsToCache": [
                {
                    "name": "Vicmaps Colour",
                    "styleLocationUrls": [
                        "https://vicmap.land.vic.gov.au/hosting/rest/services/Hosted/Vicmap_Vector_Tile_Basemap_Colour_WM_Hosted/VectorTileServer/resources/styles/root.json"
                    ],
                    "minZoom": 1,
                    "maxZoom": 15,
                    "dataBounds": [
                        {
                            "topLeftCoord": [144.173, -37.209],
                            "bottomRightCoord": [144.965, -37.959]
                        }
                    ],
                    "url": "https://vicmap.land.vic.gov.au/hosting/rest/services/Hosted/Vicmap_Vector_Tile_Basemap_Colour_WM_Hosted/VectorTileServer/tile/{z}/{y}/{x}.pbf"
                }
            ]
        }
    }
}

Key properties: - name - Display name for the cached map view - styleLocationUrls - Array of URLs to vector tile style JSON files (required for proper rendering) - minZoom/maxZoom - Zoom level range to cache during initial caching - dataBounds - Geographic areas to cache using topLeftCoord and bottomRightCoord as [longitude, latitude] - url - Tile URL pattern with {z}, {y}, {x} placeholders

Map Views Configuration

Configure multiple map views for the component:

{
    "mapParameters": {
        "defaultLat": -37.8,
        "defaultLon": 144.9,
        "minResolution": 0.211667090000847,
        "maxResolution": 2116.67090000847,
        "defaultZoom": 9,
        "mapViews": [
            {
                "uiLabel": "OSM",
                "serverType": "tilePBF",
                "url": "https://vmap.formbird.com/data/v3/{z}/{x}/{y}.pbf",
                "crossOrigin": null
            },
            {
                "uiLabel": "Vicmap",
                "serverType": "tilePBF",
                "url": "https://vicmap.land.vic.gov.au/.../tile/{z}/{y}/{x}.pbf",
                "styleLocationUrl": "https://vicmap.land.vic.gov.au/.../styles/root.json"
            }
        ]
    }
}

Specifying Views to Cache

Use mapViewsToCache to specify which map views to cache offline:

{
    "clientConfiguration": {
        "offline": {
            "mapViewsToCache": ["OSM", "Vicmap"]
        }
    }
}

Styling

Style Location URL

Vector tiles require styling. Specify with styleLocationUrl:

{
    "mapViews": [
        {
            "uiLabel": "Vicmap Hybrid",
            "serverType": "tilePBF",
            "url": "https://vicmap.land.vic.gov.au/.../tile/{z}/{y}/{x}.pbf",
            "styleLocationUrl": "https://vicmap.land.vic.gov.au/.../resources/styles/root.json"
        }
    ]
}

Default Styling

If no style URL is provided:

  • OpenLayers applies default blue styling
  • Vector features are still displayed
  • Styling can be applied programmatically

Caching Process

How Tiles Are Cached

  1. Enable caching - User enables offline mode
  2. Calculate bounds - Get region from configuration
  3. Generate tile URLs - Calculate all tiles within bounds
  4. Fetch and cache - Download each tile URL
  5. Store in cache - Service worker stores responses

Cache Storage

Map tiles are cached in "Cache Storage" under tiles-UUID, where UUID is unique for each device user. This allows multiple different Formbird logins to cache map tiles on the same browser without conflicts.

// CacheMapTilesService
const cacheStorage = await caches.open('tiles-' + userUUID);

fetch(coordArray[index].url).then(async (response) => {
    cacheStorage.add(coordArray[index].url);
});

To view cached map tiles in browser DevTools: 1. Open DevTools → Application → Cache Storage 2. Look for cache names starting with tiles-

Service Worker Integration

The service worker intercepts tile requests:

self.addEventListener("fetch", function(e) {
    e.respondWith(
        caches.match(e.request).then(function(response) {
            return response || fetch(e.request);
        })
    );
});

Zoom Levels

Tiles are cached at multiple zoom levels:

Zoom Level Approximate Scale
0-4 Continental
5-9 Regional
10-14 City/Town
15-17 Street level
18+ Building level

Configure the zoom range for caching based on your needs.

Handling Missing Zoom Levels

Problem

Some tile servers don't provide all zoom levels. Vicmaps, for example, only goes to zoom level 16.

Solution

When a tile request fails at a higher zoom level:

  1. Detect failure - 404 response from tile server
  2. Find valid level - Find the nearest available zoom level
  3. Load valid tiles - Fetch from available zoom level
  4. Vector zoom - OpenLayers zooms the vectors smoothly

Configuration for Smooth Vector Rendering

// OpenLayers renders vectors at any zoom level
new ol.layer.VectorTile({
    renderMode: 'vector',  // Smooth zooming
    source: vectorTileSource
});

Offline Map Display

Loading Cached Maps

When offline:

  1. Map component requests tile URLs
  2. Service worker intercepts requests
  3. Cache returns stored responses
  4. Map renders from cached data

Fallback Behavior

If a tile is not cached:

  1. Request fails (no network)
  2. Placeholder or no tile displayed
  3. Warning logged to console

Self-Hosted Tile Server

tileserver-gl

Run your own tile server with tileserver-gl:

# Install
npm install -g tileserver-gl

# Run with map tiles file
tileserver-gl melbourne.mbtiles

Server runs on port 8080.

Getting Map Tiles

Download mbtiles from OpenMapTiles:

  • https://openmaptiles.com/downloads/

Configure in Formbird:

{
    "tileServerGL": {
        "url": "http://localhost:8080/data/v3/{z}/{x}/{y}.pbf"
    }
}

Asset Layers from Tile Servers

MapTiler Assets

Upload GeoJSON assets to MapTiler cloud:

{
    "layers": [
        {
            "type": "tile",
            "url": "https://api.maptiler.com/data/{dataset_id}/features.json?key=YOUR_KEY",
            "color": "blue",
            "geoJsonField": "locationGeo"
        }
    ]
}

MapBox Assets

Upload datasets to MapBox:

{
    "layers": [
        {
            "type": "tile",
            "url": "https://api.mapbox.com/datasets/v1/{username}/{dataset_id}/features?access_token=YOUR_TOKEN"
        }
    ]
}

Asset Data Format

GeoJSON format for uploading:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [144.9, -37.8]
            },
            "properties": {
                "documentId": "abc-123",
                "name": "Asset Name"
            }
        }
    ]
}

Troubleshooting

Maps Not Loading Offline

  1. Check cache storage - Verify tiles are cached in DevTools
  2. Verify bounds - Ensure region is within cached bounds
  3. Check service worker - Confirm it's active and intercepting
  4. Inspect URL pattern - Verify URL matches cached pattern

Tiles Blurry When Zoomed

  • Ensure renderMode: 'vector' is set
  • Check if zoom level is within tile server range
  • Verify vector tiles are being used (not raster)

Caching Takes Too Long

  1. Reduce bounds - Cache smaller region
  2. Reduce zoom levels - Skip higher zoom levels
  3. Check network - Verify connection speed
  4. Monitor progress - Watch for errors

Style Not Applied

  1. Verify styleLocationUrl - Check URL is accessible
  2. Check CORS - Style must allow cross-origin
  3. Inspect response - Verify valid JSON returned