planetiler

Planetiler Example Project

This is a minimal example project that shows how to create custom maps with Planetiler.

Requirements:

First, make a copy of this example project. It contains:

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 --mbtiles data/toilets.mbtiles

Finally, open http://localhost:8080 to see your tiles.

Testing your profile

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,
    "--mbtiles=" + 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.

Next Steps

Check out: