planetiler/planetiler-examples
dependabot[bot] ac3f27c03a
Bump maven-surefire-plugin in /.github/planetiler-examples-dependabot (#156)
2022-04-04 05:48:19 -04:00
..
src Offload multipolygon storage to disk (reduce memory usage by 10-15gb) (#141) 2022-03-22 20:34:54 -04:00
README.md Update README.md (#140) 2022-03-21 20:42:10 -04:00
pom.xml bump to 0.4-SNAPSHOT 2022-03-31 05:54:57 -04:00
standalone.pom.xml Bump maven-surefire-plugin in /.github/planetiler-examples-dependabot (#156) 2022-04-04 05:48:19 -04:00

README.md

Planetiler Example Project

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

Requirements:

  • Java 16+ (see CONTIRBUTING.md)
    • on mac: brew install --cask temurin
  • Maven
    • on mac: brew install maven
  • Node.js
    • on mac: brew install node
  • 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 dependency
    • com.onthegomap:planetiler-core test dependency for test utilities
    • maven-assembly-plugin build plugin configuration to create a single executable jar file from Maven's package goal command
  • pom.xml exists for the parent pom.xml to treat this as a child project, you can replace with standalone.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:
  • 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 --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:

  • The other minimal examples
  • The basemap 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)