Geopackage Support
NOTICE:
Geopackage support for osmdroid is still evolving. This guide may be out of date or change frequently.
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.
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
This library: Inherited from the NGA code base Dependencies: NGA code is MIT See source code files for specific license info
Min SDK: 14
Add it your project
compile 'org.osmdroid:osmdroid-geopackage:<VERSION>'
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:
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;
}
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:
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);
}
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:
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);
}
}
}
}