@makina-corpus/maplibre-offline-pmtiles
v2.1.1
Published
A plugin for MapLibre GL JS to manage offline maps in PMTiles format.
Downloads
54
Readme
MapLibre Offline PMTiles
A plugin for MapLibre GL JS to manage offline maps in PMTiles format.
This project provides a set of functions to help manage offline maps, allowing you to download, store (via OPFS - Origin Private File System), and render vector and raster maps without an internet connection.
Features
- 📥 Download PMTiles files.
- 💾 Optimized storage using OPFS.
- 🗺️ Seamless integration with MapLibre (custom protocol
offline-pmtiles://). - 🗜️ Support for both MVT (Mapbox Vector Tile) and MLT (MapLibre Tile) formats.
- 📊 Storage quota management.
Installation
npm install @makina-corpus/maplibre-offline-pmtilesUsage
Initialization
import maplibregl from 'maplibre-gl';
import { OfflinePlugin, OFFLINE_STATUS } from '@makina-corpus/maplibre-offline-pmtiles';
// 1. Register the offline protocol
OfflinePlugin.registerProtocol(maplibregl);
// 2. Initialize the plugin
const offlinePlugin = new OfflinePlugin();
// 3. Example: Download a map
await offlinePlugin.downloadMap(
'https://example.com/map.pmtiles',
'my-map',
(status) => console.log('Download status:', status)
);
// 4. Instantiate MapLibre
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
sources: {},
layers: []
}
});
// 5. Load the map and its offline style automatically once MapLibre is ready
map.on('load', async () => {
// This will automatically add the source and any saved custom style layers
await offlinePlugin.loadMap(map, 'my-map');
});
// Alternatively, you can always manually add the source using the special protocol:
// map.addSource('my-offline-source', {
// type: 'vector', // or 'raster'
// url: 'offline-pmtiles://my-map'
// });Development
# Install dependencies
npm install
# Run the development example
npm run dev
# Build the library
npm run build
# Build the example (PWA)
npm run build:example
# Preview the production build of the example
npm run preview
API Reference
Best Practices & Storage Limits (for PWA / Mobile)
This plugin relies on the browser's OPFS (Origin Private File System) to store PMTiles files. OPFS completely changes the game compared to IndexedDB: it offers excellent, near-native performance for parsing and storing very large files without significant memory overhead.
While modern browsers have generous storage quotas (often >1GB based on free disk space), you should still carefully manage the size of the maps you provide for offline use. Although the browser can handle massive files, downloading them over a mobile network remains a bottleneck.
Recommendations:
- Network Constraints: While OPFS can easily read a 500MB
.pmtilesfile instantly, downloading it over 3G/4G can fail or take a long time. A good rule of thumb for reliable mobile downloads is to keep files between 10MB and 100MB. - Avoid entire countries: Do not force mobile users to download hundreds of megabytes (like an entire country) over slow or metered cellular networks. Instead, provide PMTiles extracts at the regional, city, or district level.
- Limit Zoom Levels: Only package the zoom levels you actually need (e.g., zoom 10 to 15). Zoom levels 14 and 15 contain the most data and drastically increase file size.
- Quota Exceeded: The plugin detects storage limits and will emit an
OFFLINE_STATUS.ERROR_QUOTAprogress event if the device runs out of allocated space. Always handle this status in your UI.
Handling Offline Styles (Sprites and Fonts)
The plugin downloads PMTiles data (map geometries) and optionally caches the associated style.json in OPFS. However, MapLibre fetches external style resources like Sprites (icons) and Glyphs (fonts) dynamically. The plugin does not intercept or store these assets.
To ensure your map remains fully functional offline (displaying text labels and icons), you should delegate the caching of these assets to your Progressive Web App (PWA) Service Worker:
Local Assets (Recommended): Download the necessary font .pbf folders and sprite files locally into your application's public/ or assets/ directory. Update the sprite and glyphs URLs in your style.json to point to these relative paths. Your Service Worker will then cache them automatically before going offline, during the initial installation of your app. This guarantees 100% availability.
OfflinePlugin
The main class for managing offline maps.
static registerProtocol(maplibregl)
Registers the offline-pmtiles protocol with MapLibre GL JS. MUST be called before using the plugin.
- maplibregl: The MapLibre GL JS object.
constructor()
Creates a new instance of the plugin.
async downloadMap(url, name, onProgress, styleSource, options)
Downloads a PMTiles file and saves it to local storage.
- url
(string): URL of the PMTiles file. - name
(string): Unique ID/name for the map. - onProgress
(function): Callback({ code, message, progress }). SeeOFFLINE_STATUSbelow. - styleSource
(string|object): (Optional) URL to a style JSON or the style object itself. - options
(object): (Optional) Download options.- signal
(AbortSignal): An instance ofAbortSignalto cancel the download and clean up storage.
- signal
Example with cancellation:
const controller = new AbortController();
try {
await offlinePlugin.downloadMap(
'https://example.com/map.pmtiles',
'my-map',
(p) => console.log(p.progress),
null,
{ signal: controller.signal }
);
} catch (e) {
if (e.name === 'AbortError') {
console.log('Download cancelled and files cleaned up');
}
}
// Later...
controller.abort();async loadMap(map, name, onProgress)
Loads a map from storage into the MapLibre instance.
- map
(MapLibreMap): The map instance. - name
(string): Name of the map to load. - onProgress
(function): Callback for status updates. SeeOFFLINE_STATUS.
unloadMap(map, name)
Removes the map's layers and source from the map instance, but keeps files in storage.
- map
(MapLibreMap): The map instance. - name
(string): Name of the map to unload.
async removeMap(map, name, onProgress)
Permanently deletes the map (and style) from storage and removes it from the map instance.
- map
(MapLibreMap): The map instance. - name
(string): Name of the map to delete. - onProgress
(function): Callback for status updates. SeeOFFLINE_STATUS.
toggleMap(map, name, visible)
Toggles the visibility of all layers associated with the map.
- map
(MapLibreMap): The map instance. - name
(string): Name of the map. - visible
(boolean):trueto show,falseto hide.
async getStorageUsage()
Returns estimated storage usage.
- Returns:
Promise<{used: number, quota: number, percent: number}>ornull.
OFFLINE_STATUS
Enum constants corresponding to the code property in the onProgress callback object.
- START: Download has initiated.
- PROGRESS: Download is in progress.
- COMPLETE: Download completed successfully.
- ERROR: A general error occurred during download.
- ERROR_QUOTA: Storage quota exceeded (browser limit reached).
License
MIT
