/leaflet-offline

A Leaflet library that downloads map tiles and uses them offline.

Primary LanguageJavaScriptGNU General Public License v2.0GPL-2.0

leaflet-offline License: GPLv2 npm version

A Leaflet library that downloads map tiles and uses them offline. Check out the demo at https://robertomlsoares.github.io/leaflet-offline/.

Inspiration

This library was heavily inspired and based on the leaflet.offline library by Allart Kooiman. I decided to create a new one because the ideas I had were diverging too much from what leaflet.offline proposes to do.

The biggest change I made was removing the dependency on localForage and introducing a new parameter that makes you able to give your own implementantion of a database layer to save, retrieve and delete the map tiles however you like as long as it implements the same interface (more on that below).

Dependencies

Install

If you use npm, you can install leaflet-offline by running: npm install leaflet-offline

If you don't use a package manager, simply download a file from the dist/ folder and include it in your application.

Guide

Database Layer

When creating both the OfflineControl and the OfflineLayer, you need to pass an object that will act as the database layer when saving, retrieving and deleting map tiles. The object must implement the following interface.

Method Parameters Returns
'getItem' key: String Promise. The result of the promise must be a Blob, File or MediaStream representing the image
'saveTiles' tileUrls: Array Promise. The result is ignored
'clear' None Promise. The result is ignored

Layer API

When creating the OfflineLayer, you need to pass the URL from where the map tiles will be retrieved, the database layer object and the options parameter. There are no special options for the OfflineLayer, they are the same as the TileLayer options from Leaflet.

Control API

When creating the OfflineControl, you need to pass the OfflineLayer, the database layer object and the options parameter. The options parameter is defined as follows:

Option Type Default Description
'position' String 'topleft' Control options
'saveButtonHtml' String 'S' The HTML that will be displayed as the save button
'saveButtonTitle' String 'Save tiles' The title that will be used for the save button
'removeButtonHtml' String 'R' The HTML that will be displayed as the remove button
'removeButtonTitle' String 'Remove tiles' The title that will be used for the remove button
'minZoom' Number 0 The save operation won't start when trying to save map tiles below this zoom level
'maxZoom' Number 19 The save operation will not save map tiles beyond this zoom level
'confirmSavingCallback' Function null A function to be executed before the save operation starts
'confirmRemovalCallback' Function null A function to be executed before the remove operation starts

The callbacks should implement the following interface.

Method Parameters Returns
'confirmSavingCallback' nTilesToSave: Number, continueSaveTiles: Function Whatever. Remember to call continueSaveTiles to resume the operation
'confirmRemovalCallback' continueRemoveTiles: Function Whatever. Remember to call continueRemoveTiles to resume the operation

Events

Events fired by the OfflineControl on the OfflineLayer. Assume that tilesDb is the database layer object.

Event Data Description
'offline:below-min-zoom-error' undefined Fired when trying to save below minimum zoom level
'offline:save-start' {nTilesToSave: Number} Fired when the save operation starts but before calling tilesDb.saveTiles
'offline:save-end' undefined Fired when the promise from tilesDb.saveTiles finishes successfully
'offline:save-error' {error: Error} Fired when the promise from tilesDb.saveTiles finishes with an error
 'offline:remove-start' undefined Fired when the remove operation starts but before calling tilesDb.clear
 'offline:remove-end' undefined Fired when the promise from tilesDb.clear finishes successfully
'offline:remove-error' {error: Error} Fired when the promise from tilesDb.clear finishes with an error

Example

Creating the database layer object.

var tilesDb = {
    getItem: function (key) {
        // return Promise that has the image Blob/File/Stream.
    },

    saveTiles: function (tileUrls) {
        // return Promise.
    },

    clear: function () {
        // return Promise.
    }
};

Creating the OfflineLayer.

var map = L.map('map-id');
var offlineLayer = L.tileLayer.offline('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', tilesDb, {
    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
    subdomains: 'abc',
    minZoom: 13,
    maxZoom: 19,
    crossOrigin: true
});

Creating the OfflineControl.

var offlineControl = L.control.offline(offlineLayer, tilesDb, {
    saveButtonHtml: '<i class="fa fa-download" aria-hidden="true"></i>',
    removeButtonHtml: '<i class="fa fa-trash" aria-hidden="true"></i>',
    confirmSavingCallback: function (nTilesToSave, continueSaveTiles) {
        if (window.confirm('Save ' + nTilesToSave + '?')) {
            continueSaveTiles();
        }
    },
    confirmRemovalCallback: function (continueRemoveTiles) {
        if (window.confirm('Remove all the tiles?')) {
            continueRemoveTiles();
        }
    },
    minZoom: 13,
    maxZoom: 19
});

Adding both of them to the map.

offlineLayer.addTo(map);
offlineControl.addTo(map);

For a more complete example, check the demo code inside the gh-pages branch.