This is a minimal example project that shows how to create custom maps with Planetiler.
Requirements:
- Java 21+ (see CONTIRBUTING.md)
- on mac:
brew install --cask temurin
- on mac:
- Maven
- on mac:
brew install maven
- on mac:
- Node.js
- on mac:
brew install node
- on mac:
- TileServer GL
npm install -g tileserver-gl-light
- Also recommended: IntelliJ IDEA
- Disk: 5-10x as much free space as the input data
- RAM: 1.5x the size of the
.osm.pbf
file
First, make a copy of this example project. It contains:
- standalone.pom.xml - build instructions for Maven:
com.onthegomap:planetiler-core
main Planetiler dependencycom.onthegomap:planetiler-core
test dependency for test utilitiesmaven-assembly-plugin
build plugin configuration to create a single executable jar file from Maven'spackage
goal command
pom.xml
exists for the parent pom.xml to treat this as a child project, you can replace withstandalone.pom.xml
or append--file standalone.pom.xml
to every maven command to run as a standalone project.- src/main/java/com/onthegomap/planetiler/examples - some minimal
example map profiles:
- ToiletsOverlay - demonstrates how to build a simple overlay with toilets locations from OpenStreetMap
- BikeRouteOverlay - demonstrates how to use OSM relations to build an overlay map of bicycle routes
- ToiletsOverlayLowLevelApi - alternate driver for the ToiletsOverlay using lower-level Planetiler APIs
- src/test/java/com/onthegomap/planetiler/examples unit and integration tests for each of the map generators
Then, create a new class that implements com.onthegomap.planetiler.Profile
:
package com.onthegomap.planetiler.examples;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.Planetiler;
import com.onthegomap.planetiler.Profile;
import com.onthegomap.planetiler.reader.SourceFeature;
import java.nio.file.Path;
public class MyProfile implements Profile {
@Override
public String name() {
// name that shows up in the MBTiles metadata table
return "My Profile";
}
}
Then, implement the processFeature()
method in your class (add the code before the last closing curly bracket)
that determines what vector tile features to emit for each source feature.
For example, to include a map of toilets from OpenStreetMap
at zoom level 12 and above:
@Override
public void processFeature(SourceFeature sourceFeature, FeatureCollector features) {
if (sourceFeature.isPoint() && sourceFeature.hasTag("amenity", "toilets")) {
features.point("toilets") // create a point in layer named "toilets"
.setMinZoom(12)
.setAttr("customers_only", sourceFeature.hasTag("access", "customers"))
.setAttr("indoor", sourceFeature.getBoolean("indoor"))
.setAttr("name", sourceFeature.getTag("name"))
.setAttr("operator", sourceFeature.getTag("operator"));
}
}
Next, add a main
entrypoint that
uses Planetiler to define input sources
and default input/output paths:
public static void main(String... args) throws Exception {
Planetiler.create(args)
.setProfile(new MyProfile())
// if input.pbf not found, download Monaco from Geofabrik
.addOsmSource("osm", Path.of("data", "sources", "input.pbf"), "geofabrik:monaco")
.overwriteOutput("mbtiles", Path.of("data", "toilets.mbtiles"))
.run();
}
Then build the application into a single jar file with all dependencies included:
mvn clean package --file standalone.pom.xml
And run the application:
java -cp target/*-with-deps.jar com.onthegomap.planetiler.examples.MyProfile
Then, to inspect the tiles:
tileserver-gl-light data/toilets.mbtiles
Finally, open http://localhost:8080 to see your tiles.
Unit tests verify the logic for mapping source features to vector tile features, and integration tests run the entire profile end-to-end and ensure the output vector tiles contain features you expect. TestUtils contains utilities for unit and integration testing.
A basic unit test:
@Test
public void unitTest() {
var profile = new MyProfile();
var node = SimpleFeature.create(
TestUtils.newPoint(1, 2),
Map.of("amenity", "toilets")
);
List<FeatureCollector.Feature> mapFeatures = TestUtils.processSourceFeature(node, profile);
// Then inspect attributes of each of vector tile fetures emitted...
assertEquals(1, mapFeatures.length);
assertEquals(12, mapFeatures.get(0).getMinZoom());
}
A basic integration test:
@Test
public void integrationTest(@TempDir Path tmpDir) throws Exception {
Path mbtilesPath = tmpDir.resolve("output.mbtiles");
MyProfile.main(
"--osm_path=" + TestUtils.pathToResource("monaco-latest.osm.pbf"),
"--tmp=" + tmpDir,
"--output=" + mbtilesPath,
));
try (Mbtiles mbtiles = Mbtiles.newReadOnlyDatabase(mbtilesPath)) {
Map<String, String> metadata = mbtiles.metadata().getAll();
assertEquals("My Profile", metadata.get("name"));
// then inspect features in the emitted vector tiles
TestUtils.assertNumFeatures(mbtiles, "toilets", 14, Map.of(), GeoUtils.WORLD_LAT_LON_BOUNDS,
34, Point.class);
}
}
See ToiletsProfileTest for a complete unit and integration test.
Check out:
- The other minimal examples
- The OpenMapTiles profile for a full-featured example of a complex profile with processing broken out into a handler per layer
- Planetiler for more options when invoking the program
- FeatureCollector for the full API to construct vector tile features
- SourceFeature and WithTags for the full API to extract data from source features
- Profile for the rest of methods you can
implement to:
- customize OSM relation preprocessing
- set MBTiles metadata attributes
- get notified when a source finishes processing
- and post-process vector-tile features (i.e. merge touching linestrings or polygons)