Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

GeoPackage To Go: Offline Maps from Web to Mobile

FOSS4G North America 2018 - St. Louis, MO

A browser based GeoPackage example using GeoPackage JS from the session at FOSS4G. There are also examples for iOS and Android.

GeoPackage-JS Example Screenshot Map tiles from OpenStreetMap.

This example page opens a GeoPackage with local landmarks, pizza restaurants, vectors of the parks, and OpenStreetMap tiles from St. Louis and displays them on a Leaflet map.

Want to learn more? See the API documentation.


You can run this example using any web server. For simplicity, I prefer to use the Python module SimpleHTTPServer. If you have python installed, then you already have this module as well.

From this directory, run:

$ python -m SimpleHTTPServer

Open a web browser and point it to http://localhost:8000.

Code Walkthrough

We are using Leaflet, but you could use any map you like. There is also a leaflet plugin that has convenience methods for quickly adding data from a GeoPackage to a Leaflet map.

In your HTML, you can include GeoPackage JS from unpkg.

<script type="text/javascript" src=""></script>

For a full end to end example, please see index.js in this directory. Once you have your file loaded, you can access the tiles inside like so:

var GeoPackageAPI = window.geopackage;


GeoPackageAPI.openGeoPackageByteArray(array, function(err, geoPackage) { // Now you can operate on the GeoPackage
  // Get the tile table names
  geoPackage.getTileTables(function(err, tileTableNames) {
    geoPackage.getTileDaoWithTableName(tileTableNames[0], function(err, tileDao) { // We know we have one tile layer, loop if you have more.
    var maxZoom = tileDao.maxWebMapZoom;
    var minZoom = tileDao.minWebMapZoom;
    var tableLayer = new L.GridLayer({noWrap: true, minZoom: minZoom, maxZoom: maxZoom});
    tableLayer.createTile = function(tilePoint, done) {
      var canvas = L.DomUtil.create('canvas', 'leaflet-tile');
      var size = this.getTileSize();
      canvas.width = size.x;
      canvas.height = size.y;
      setTimeout(function() {
        console.time('Draw tile ' + tilePoint.x + ', ' + tilePoint.y + ' zoom: ' + tilePoint.z);
        GeoPackageAPI.drawXYZTileInCanvas(geoPackage, tileTableNames[0], tilePoint.x, tilePoint.y, tilePoint.z, size.x, size.y, canvas, function(err) {
          console.timeEnd('Draw tile ' + tilePoint.x + ', ' + tilePoint.y + ' zoom: ' + tilePoint.z);
          done(err, canvas);
      }, 0);
      return canvas;

Now for the features. We need to pull the features out of the tables and convert them into a format that Leaflet can use display them on the map, in this case GeoJSON.

geoPackage.getFeatureTables(function(err, featureTableNames) {
  for (var i = 0; i < featureTableNames.length; i++) {
    geoPackage.getFeatureDaoWithTableName(featureTableNames[i], function(err, featureDao) {
      geoPackage.getInfoForTable(featureDao, function(err, info) {

        // query for all features
        featureDao.queryForEach(function(err, row, rowDone) {
          var feature = featureDao.getFeatureRow(row);
          var geometry = feature.getGeometry();
          if (geometry) {
            // Make the information into something we can display on the map with leaflet
            var geom = geometry.geometry;
            var geoJson = geometry.geometry.toGeoJSON();
   = {};
  ["table_name"] = feature.featureTable.table_name;

            // map the values from the feature table into GeoJSON properties we can use to style the map and show a popup
            for (var key in feature.values) {
              if(feature.values.hasOwnProperty(key) && key != feature.getGeometryColumn().name) {
                var column = info.columnMap[key];
      [column.displayName] = feature.values[key];