Skip to content
PWRxPSYCHO edited this page Apr 18, 2023 · 12 revisions

Geopackage support for osmdroid

NOTICE:

Geopackage support for osmdroid is still evolving. This guide may be out of date or change frequently.

What is Geopackage?

GeoPackage is an open standard file that stores geospatial data using SQLite technology. It is portable, lightweight, and supports various data types in 2D and 3D. It also allows metadata and styling information, making it a comprehensive solution for geospatial data storage, sharing, and visualization. It is widely used in the geospatial industry.

For more information, refer https://www.geopackage.org/

Basically, it's a sqlite database that can contain:

  • 0 or more raster tile sources (think jpg, or png)
  • 0 or more features sources (think vector graphics)

Geopackage internally uses a different tile numbering system than osmdroid or anything else based on the slippy tile format (z/x/y) thus a support library is required in order to chop up, rearrange, and transform whatever is in the geopackage database into something more usable.

Sources

This code was adopted from NGA's Geopackage support library and their adapter for Google Maps. Since the APIs between Google Maps and Osmdroid are so similiar, much of the code was used with only minor modifications.

The GeoPackage Core API, which osmdroid refers to, is written in Java for Android and can be found at https://github.com/ngageoint/geopackage-android

License

This library: Inherited from the NGA code base Dependencies: NGA code is MIT See source code files for specific license info

How to use

Min SDK: 14

Add it your project

compile 'org.osmdroid:osmdroid-geopackage:<VERSION>'

Adding a geopackage raster tile set to the osmdroid map

Note: It was introduced in Osmdroid 5.6.4 for GeoPackage Core API version 2.0.1. However, in OsmDroid 6.1.17, a new GeoPackage Core API version 6.6.0 was introduced.

For a complete working example see:

https://github.com/osmdroid/osmdroid/blob/master/OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/geopackage/GeopackageSample.java

First let's up our map source, geopackage needs you to explicitly specify which map files to load

    XYTileSource currentSource = null;
    GeoPackageProvider geoPackageProvider = null;


    //first let's up our map source, geopackage needs you to explicitly specify which map files to load
    //this bit does some basic file system scanning
    Set<File> mapfiles = findMapFiles();
    //do a simple scan of local storage for .gpkg files.
    File[] maps = new File[mapfiles.size()];
    maps = mapfiles.toArray(maps);

Set the tile provider

    geoPackageProvider = new GeoPackageProvider(maps, this.getContext());
    mMapView.setTileProvider(geoPackageProvider);

Set the tile source by selecting a tile table in the geopackage database. Since you're probably looking at a grey grid, center the map where the tiles are

    //get the list of sources
    List<GeopackageRasterTileSource> tileSources = geoPackageProvider.geoPackageMapTileModuleProvider().getTileSources();

    boolean sourceSet = false;  
      
    List<GeopackageRasterTileSource> tileSources = geoPackageProvider.geoPackageMapTileModuleProvider().getTileSources();  
    if (!tileSources.isEmpty()) {  
        getmMapView().setTileSource(tileSources.get(0));
        getmMapView().zoomToBoundingBox(tileSources.get(0).getBounds(), true);  
        getmMapView().getController().setZoom(tileSources.get(0).getMinimumZoomLevel());  
        sourceSet = true;  
    }

Adding a geopackage features to the osmdroid map

Note: It was introduced in Osmdroid 5.6.5 for GeoPackage Core API version 2.0.1. However, in OsmDroid 6.1.17, a new GeoPackage Core API version 6.5.0 was introduced.

For a complete working example see:

https://github.com/osmdroid/osmdroid/blob/master/OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/geopackage/GeopackageFeatures.java

For smale feature sets, you can use the following code to convert all features in a given geopackage feature table into osmdroid overlays (Marker, Polyline and Polygon).

Find our geopackages

    //first let's up our map source, geopackage needs you to explicitly specify which map files to load
    //this bit does some basic file system scanning
    Set<File> mapfiles = findMapFiles();
    //do a simple scan of local storage for .gpkg files.
    File[] maps = new File[mapfiles.size()];
    maps = mapfiles.toArray(maps);

Import them into the NGA manager

    // Get a manager
    GeoPackageManager manager = GeoPackageFactory.getManager(getContext());
    // Available databases
    List<String> databases = manager.databases();
    // Import database
    for (File f : maps) {
        try {
            boolean imported = manager.importGeoPackage(f);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

Start the conversion

    if (!databases.isEmpty()) {
        // Open database
        for (int k = 0; k < databases.size(); k++) {
            GeoPackage geoPackage = manager.open(databases.get(k));
            // Feature tile tables
            List<String> features = geoPackage.getFeatureTables();
            // Query Features
            if (!features.isEmpty()) {
                for (int i = 0; i < features.size(); i++) {

                    MarkerOptions markerRenderingOptions = new MarkerOptions();
                    PolylineOptions polylineRenderingOptions = new PolylineOptions();
                    polylineRenderingOptions.setWidth(2f);
                    polylineRenderingOptions.setColor(Color.argb(100, 255, 0, 0));
                    polylineRenderingOptions.setTitle(databases.get(k) + ":" + features.get(i));

                    PolygonOptions polygonOptions = new PolygonOptions();
                    polygonOptions.setStrokeWidth(2f);
                    polygonOptions.setFillColor(Color.argb(100, 255, 0, 255));
                    polygonOptions.setStrokeColor(Color.argb(100, 0, 0, 255));
                    polygonOptions.setTitle(databases.get(k) + ":" + features.get(i));

                    OsmMapShapeConverter converter = new OsmMapShapeConverter(null, markerRenderingOptions, polylineRenderingOptions, polygonOptions);

                    String featureTable = features.get(i);
                    FeatureDao featureDao = geoPackage.getFeatureDao(featureTable);
                    FeatureCursor featureCursor = featureDao.queryForAll();
                    try {
                        while (featureCursor.moveToNext()) {
                            try {
                                FeatureRow featureRow = featureCursor.getRow();
                                GeoPackageGeometryData geometryData = featureRow.getGeometry();
                                if ("statesQGIS".equals(featureTable) && "states10".equals(databases.get(k))) {
                                    //cheating here a bit, if you happen to have the states10.gpkg file
                                    //this will colorize thes tates based on the 2016 presential election results

                                    //get it here: https://github.com/opengeospatial/geopackage/raw/gh-pages/data/states10.gpkg
                                    String state = (String) featureRow.getValue("STATE_NAME");
                                    String stateabbr = (String) featureRow.getValue("STATE_ABBR");
                                    long population = (long) featureRow.getValue("POP1996");
                                    applyTheming(state, stateabbr, population, polygonOptions);
                                }
                                Geometry geometry = geometryData.getGeometry();
                                converter.addToMap(getmMapView(), geometry);
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                            // ...
                        }
                    } finally {
                        featureCursor.close();
                    }
                }
            } else
                showMessage(getContext(), "No feature tables available in " + geoPackage.getName(), Toast.LENGTH_LONG);
        }
    } else {
        showMessage(getContext(), "No databases available", Toast.LENGTH_LONG);
    }

Adding a geopackage feature tile set to the osmdroid map

Note: It was introduced in Osmdroid 5.6.5 for GeoPackage Core API version 2.0.1. However, in OsmDroid 6.1.17, a new GeoPackage Core API version 6.5.0 was introduced.

For a complete working example see:

https://github.com/osmdroid/osmdroid/blob/master/OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/geopackage/GeopackageFeatureTiles.java

The following code opens a Geopackage database and adds 1 tile overlay to the map per feature tile table.

    // Get a manager
    GeoPackageManager manager = GeoPackageFactory.getManager(getContext());

    // Available databases
    List<String> databases = manager.databases();

    // Import database
    for (File f : maps) {
        try {
            boolean imported = manager.importGeoPackage(f);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    if (!databases.isEmpty()) {

        for (int k = 0; k < databases.size(); k++) {
            // Open database
            GeoPackage geoPackage = manager.open(databases.get(k));
            // Feature tile tables
            List<String> features = geoPackage.getFeatureTables();
            // Query Features
            if (!features.isEmpty()) {
                for (int i = 0; i < features.size(); i++) {
                    GeoPackageFeatureTileProvider provider = new GeoPackageFeatureTileProvider(
                            new XYTileSource(databases.get(k) + ":" + features.get(i), 0, 22, 256, "png", new String[0])
                    );
                    GeopackageFeatureTilesOverlay overlay = new GeopackageFeatureTilesOverlay(provider, getContext());
                    overlay.setDatabaseAndFeatureTable(databases.get(k), features.get(i));
                    getmMapView().getOverlayManager().add(overlay);
                }
            }
        }
    }