├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── cache-sources-action │ └── action.yml ├── dependabot.yml ├── planetiler-examples-dependabot │ └── pom.xml └── workflows │ ├── docs.yml │ ├── docs_mlc_config.json │ ├── maven.yml │ ├── performance.yml │ ├── release.yml │ ├── snapshot.yml │ ├── sonar.yml │ └── update-pr.yml ├── .gitignore ├── .gitmodules ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── eclipseCodeFormatter.xml ├── jsonSchemas.xml └── vcs.xml ├── .mvn ├── jvm.config └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .vscode └── settings.json ├── ARCHITECTURE.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE.md ├── PLANET.md ├── README.md ├── config-example.properties ├── diagrams ├── architecture.png ├── architecture.puml ├── demo.png └── slicing.png ├── eclipse-formatter.xml ├── grafana.json ├── layerstats ├── README.md └── top_osm_tiles.tsv.gz ├── mvnw ├── mvnw.cmd ├── planet-logs ├── v0.1.0-daylight-do-16cpu-128gb.txt ├── v0.1.0-planet-c5ad-64cpu-128gb.txt ├── v0.1.0-planet-do-16cpu-128gb.nps ├── v0.1.0-planet-do-16cpu-128gb.txt ├── v0.1.0-planet-linode-50cpu-128gb.nps ├── v0.1.0-planet-linode-50cpu-128gb.txt ├── v0.3.0-planet-c5ad-128gb.txt ├── v0.3.0-planet-r6g-64cpu-512gb-ramdisk.txt ├── v0.4.0-planet-c6gd-128gb-no-z13-building-merge.txt ├── v0.4.0-planet-c6gd-128gb.txt ├── v0.4.0-planet-c6gd-32gb.txt ├── v0.4.0-planet-c6gd-64gb.txt ├── v0.5.0-planet-c2d-standard-112-no-z13-building-merge.txt ├── v0.5.0-planet-c2d-standard-112.txt ├── v0.5.0-planet-c6gd-128gb-no-z13-building-merge.nps ├── v0.5.0-planet-c6gd-128gb-no-z13-building-merge.txt ├── v0.5.0-planet-c6gd-128gb.nps ├── v0.5.0-planet-c6gd-128gb.txt ├── v0.5.0-planet-c6gd-32gb.txt ├── v0.5.0-planet-c6gd-64gb.txt ├── v0.7.0-planet-c3d-standard-180-no-z13-building-merge.txt ├── v0.7.0-planet-c3d-standard-180.txt ├── v0.7.0-planet-c7gd-128gb-no-z13-building-merge.txt ├── v0.7.0-planet-c7gd-128gb.txt ├── v0.7.0-planet-c7gd-16gb-no-z13-building-merge.txt └── v0.7.0-planet-im4gn-8gb-no-z13-building-merge.txt ├── planetiler-benchmarks ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── onthegomap │ └── planetiler │ ├── benchmarks │ ├── BenchmarkLineMerge.java │ ├── BenchmarkLineSplitter.java │ ├── BenchmarkMbtilesRead.java │ ├── BenchmarkMbtilesWriter.java │ ├── BenchmarkOsmRead.java │ ├── BenchmarkParquetRead.java │ ├── BenchmarkPmtiles.java │ ├── BenchmarkSimplify.java │ ├── BenchmarkTileCoord.java │ ├── BenchmarkVarInt.java │ ├── LongLongMapBench.java │ └── OpenMapTilesMapping.java │ └── collection │ ├── BenchmarkExternalMergeSort.java │ ├── BenchmarkKWayMerge.java │ └── StressTestKWayMerge.java ├── planetiler-core ├── pom.xml └── src │ ├── main │ ├── java │ │ ├── com │ │ │ └── onthegomap │ │ │ │ └── planetiler │ │ │ │ ├── FeatureCollector.java │ │ │ │ ├── FeatureMerge.java │ │ │ │ ├── FeatureProcessor.java │ │ │ │ ├── ForwardingProfile.java │ │ │ │ ├── Planetiler.java │ │ │ │ ├── Profile.java │ │ │ │ ├── VectorTile.java │ │ │ │ ├── archive │ │ │ │ ├── ReadableTileArchive.java │ │ │ │ ├── Tile.java │ │ │ │ ├── TileArchiveConfig.java │ │ │ │ ├── TileArchiveMetadata.java │ │ │ │ ├── TileArchiveMetadataDeSer.java │ │ │ │ ├── TileArchiveWriter.java │ │ │ │ ├── TileArchives.java │ │ │ │ ├── TileCompression.java │ │ │ │ ├── TileEncodingResult.java │ │ │ │ └── WriteableTileArchive.java │ │ │ │ ├── collection │ │ │ │ ├── AppendStore.java │ │ │ │ ├── AppendStoreMmap.java │ │ │ │ ├── AppendStoreRam.java │ │ │ │ ├── ArrayDoubleMinHeap.java │ │ │ │ ├── ArrayLongLongMapMmap.java │ │ │ │ ├── ArrayLongLongMapRam.java │ │ │ │ ├── ArrayLongMinHeap.java │ │ │ │ ├── DoubleMinHeap.java │ │ │ │ ├── ExternalMergeSort.java │ │ │ │ ├── FeatureGroup.java │ │ │ │ ├── FeatureSort.java │ │ │ │ ├── HasLongSortKey.java │ │ │ │ ├── Hppc.java │ │ │ │ ├── IntRangeSet.java │ │ │ │ ├── IterableOnce.java │ │ │ │ ├── LongLongMap.java │ │ │ │ ├── LongLongMultimap.java │ │ │ │ ├── LongMerger.java │ │ │ │ ├── LongMinHeap.java │ │ │ │ ├── SortableFeature.java │ │ │ │ ├── SortedTableLongLongMap.java │ │ │ │ ├── SparseArrayLongLongMap.java │ │ │ │ ├── Storage.java │ │ │ │ └── SupplierIterator.java │ │ │ │ ├── config │ │ │ │ ├── Arguments.java │ │ │ │ ├── Bounds.java │ │ │ │ └── PlanetilerConfig.java │ │ │ │ ├── expression │ │ │ │ ├── DataType.java │ │ │ │ ├── Expression.java │ │ │ │ ├── MultiExpression.java │ │ │ │ ├── Simplifiable.java │ │ │ │ └── TypedGetter.java │ │ │ │ ├── files │ │ │ │ ├── FilesArchiveUtils.java │ │ │ │ ├── ReadableFilesArchive.java │ │ │ │ ├── TileSchemeEncoding.java │ │ │ │ └── WriteableFilesArchive.java │ │ │ │ ├── geo │ │ │ │ ├── DouglasPeuckerSimplifier.java │ │ │ │ ├── DualMidpointSmoother.java │ │ │ │ ├── GeoUtils.java │ │ │ │ ├── GeometryException.java │ │ │ │ ├── GeometryPipeline.java │ │ │ │ ├── GeometryType.java │ │ │ │ ├── LineSplitter.java │ │ │ │ ├── MidpointSmoother.java │ │ │ │ ├── MutableCoordinateSequence.java │ │ │ │ ├── PointIndex.java │ │ │ │ ├── PolygonIndex.java │ │ │ │ ├── SimplifyMethod.java │ │ │ │ ├── TileCoord.java │ │ │ │ ├── TileExtents.java │ │ │ │ ├── TileOrder.java │ │ │ │ ├── TilePredicate.java │ │ │ │ ├── Unit.java │ │ │ │ ├── VWSimplifier.java │ │ │ │ └── WithGeometry.java │ │ │ │ ├── mbtiles │ │ │ │ ├── Compare.java │ │ │ │ ├── Mbtiles.java │ │ │ │ └── Verify.java │ │ │ │ ├── pmtiles │ │ │ │ ├── Pmtiles.java │ │ │ │ ├── ReadablePmtiles.java │ │ │ │ └── WriteablePmtiles.java │ │ │ │ ├── reader │ │ │ │ ├── FileFormatException.java │ │ │ │ ├── GeoPackageReader.java │ │ │ │ ├── JsonConversion.java │ │ │ │ ├── NaturalEarthReader.java │ │ │ │ ├── ShapefileReader.java │ │ │ │ ├── SimpleFeature.java │ │ │ │ ├── SimpleReader.java │ │ │ │ ├── SourceFeature.java │ │ │ │ ├── SourceFeatureProcessor.java │ │ │ │ ├── Struct.java │ │ │ │ ├── StructSerializer.java │ │ │ │ ├── WithGeometryType.java │ │ │ │ ├── WithSource.java │ │ │ │ ├── WithSourceLayer.java │ │ │ │ ├── WithTags.java │ │ │ │ ├── geojson │ │ │ │ │ ├── GeoJson.java │ │ │ │ │ ├── GeoJsonFeature.java │ │ │ │ │ ├── GeoJsonFeatureCounter.java │ │ │ │ │ ├── GeoJsonFeatureIterator.java │ │ │ │ │ └── GeoJsonReader.java │ │ │ │ ├── osm │ │ │ │ │ ├── OsmBlockSource.java │ │ │ │ │ ├── OsmElement.java │ │ │ │ │ ├── OsmHeader.java │ │ │ │ │ ├── OsmInputFile.java │ │ │ │ │ ├── OsmMultipolygon.java │ │ │ │ │ ├── OsmNodeBoundsProvider.java │ │ │ │ │ ├── OsmPhaser.java │ │ │ │ │ ├── OsmReader.java │ │ │ │ │ ├── OsmRelationInfo.java │ │ │ │ │ ├── OsmSourceFeature.java │ │ │ │ │ ├── PbfDecoder.java │ │ │ │ │ ├── PbfFieldDecoder.java │ │ │ │ │ └── PolyFileReader.java │ │ │ │ └── parquet │ │ │ │ │ ├── CoordinateSequenceBuilder.java │ │ │ │ │ ├── GeoArrow.java │ │ │ │ │ ├── GeoParquetMetadata.java │ │ │ │ │ ├── GeometryReader.java │ │ │ │ │ ├── Interval.java │ │ │ │ │ ├── ParquetFeature.java │ │ │ │ │ ├── ParquetInputFile.java │ │ │ │ │ ├── ParquetPrimitiveConverter.java │ │ │ │ │ ├── ParquetReader.java │ │ │ │ │ └── ParquetRecordConverter.java │ │ │ │ ├── render │ │ │ │ ├── FeatureRenderer.java │ │ │ │ ├── GeometryCoordinateSequences.java │ │ │ │ ├── RenderedFeature.java │ │ │ │ └── TiledGeometry.java │ │ │ │ ├── stats │ │ │ │ ├── Counter.java │ │ │ │ ├── DefaultStats.java │ │ │ │ ├── ProcessInfo.java │ │ │ │ ├── ProcessTime.java │ │ │ │ ├── ProgressLoggers.java │ │ │ │ ├── PrometheusStats.java │ │ │ │ ├── Stats.java │ │ │ │ ├── Timer.java │ │ │ │ └── Timers.java │ │ │ │ ├── stream │ │ │ │ ├── StreamArchiveConfig.java │ │ │ │ ├── StreamArchiveUtils.java │ │ │ │ ├── WriteableCsvArchive.java │ │ │ │ ├── WriteableJsonStreamArchive.java │ │ │ │ ├── WriteableProtoStreamArchive.java │ │ │ │ └── WriteableStreamArchive.java │ │ │ │ ├── util │ │ │ │ ├── AnsiColors.java │ │ │ │ ├── AwsOsm.java │ │ │ │ ├── BinPack.java │ │ │ │ ├── BuildInfo.java │ │ │ │ ├── ByteBufferUtil.java │ │ │ │ ├── CacheByZoom.java │ │ │ │ ├── CloseShieldOutputStream.java │ │ │ │ ├── CloseableConsumer.java │ │ │ │ ├── CloseableIterator.java │ │ │ │ ├── CommonStringEncoder.java │ │ │ │ ├── CompareArchives.java │ │ │ │ ├── CountingOutputStream.java │ │ │ │ ├── DelegatingOutputStream.java │ │ │ │ ├── DiskBacked.java │ │ │ │ ├── Downloader.java │ │ │ │ ├── DuplicateClassLoader.java │ │ │ │ ├── Exceptions.java │ │ │ │ ├── FastGzipOutputStream.java │ │ │ │ ├── FileUtils.java │ │ │ │ ├── FileWatcher.java │ │ │ │ ├── Format.java │ │ │ │ ├── FunctionThatThrows.java │ │ │ │ ├── Geofabrik.java │ │ │ │ ├── Glob.java │ │ │ │ ├── Gzip.java │ │ │ │ ├── Hashing.java │ │ │ │ ├── Hilbert.java │ │ │ │ ├── Imposm3Parsers.java │ │ │ │ ├── LanguageUtils.java │ │ │ │ ├── LayerAttrStats.java │ │ │ │ ├── LogUtil.java │ │ │ │ ├── LoopLineMerger.java │ │ │ │ ├── Madvise.java │ │ │ │ ├── MapUtil.java │ │ │ │ ├── Memoized.java │ │ │ │ ├── MemoryEstimator.java │ │ │ │ ├── MergingRangeMap.java │ │ │ │ ├── MutableCollections.java │ │ │ │ ├── Parse.java │ │ │ │ ├── ResourceUsage.java │ │ │ │ ├── SeekableInMemoryByteChannel.java │ │ │ │ ├── SlidingWindow.java │ │ │ │ ├── SortKey.java │ │ │ │ ├── ThreadLocalTransliterator.java │ │ │ │ ├── TileSizeStats.java │ │ │ │ ├── TileWeights.java │ │ │ │ ├── TilesetSummaryStatistics.java │ │ │ │ ├── ToDoubleFunctionThatThrows.java │ │ │ │ ├── TopOsmTiles.java │ │ │ │ ├── Translations.java │ │ │ │ ├── Try.java │ │ │ │ ├── VarInt.java │ │ │ │ ├── Wikidata.java │ │ │ │ ├── YAML.java │ │ │ │ ├── ZoomFunction.java │ │ │ │ └── log4j │ │ │ │ │ └── ElapsedTimeLookupPlugin.java │ │ │ │ ├── validator │ │ │ │ ├── BaseSchemaValidator.java │ │ │ │ ├── JavaProfileValidator.java │ │ │ │ └── SchemaSpecification.java │ │ │ │ └── worker │ │ │ │ ├── Distributor.java │ │ │ │ ├── IntConsumerThatThrows.java │ │ │ │ ├── RunnableThatThrows.java │ │ │ │ ├── WeightedHandoffQueue.java │ │ │ │ ├── WorkQueue.java │ │ │ │ ├── Worker.java │ │ │ │ └── WorkerPipeline.java │ │ └── org │ │ │ └── apache │ │ │ ├── hadoop │ │ │ └── io │ │ │ │ └── compress │ │ │ │ ├── CompressionCodec.java │ │ │ │ ├── DirectDecompressionCodec.java │ │ │ │ ├── DirectDecompressor.java │ │ │ │ ├── GzipCodec.java │ │ │ │ └── Lz4Codec.java │ │ │ └── parquet │ │ │ └── filter2 │ │ │ └── predicate │ │ │ └── Filters.java │ ├── proto │ │ ├── fileformat.proto │ │ ├── osmformat.proto │ │ ├── stream_archive_proto.proto │ │ └── vector_tile_proto.proto │ └── resources │ │ ├── assemblies │ │ └── with-deps.xml │ │ ├── buildinfo.properties │ │ ├── log4j2.properties │ │ └── top_50k_osm_tiles.tsv │ └── test │ ├── java │ └── com │ │ └── onthegomap │ │ └── planetiler │ │ ├── ExpectedError.java │ │ ├── ExpectedException.java │ │ ├── FeatureCollectorTest.java │ │ ├── FeatureMergeTest.java │ │ ├── ForwardingProfileTests.java │ │ ├── PlanetilerTests.java │ │ ├── Slow.java │ │ ├── TestUtils.java │ │ ├── VectorTileTest.java │ │ ├── archive │ │ ├── TileArchiveConfigTest.java │ │ └── TileArchiveMetadataTest.java │ │ ├── collection │ │ ├── AppendStoreTest.java │ │ ├── FeatureGroupTest.java │ │ ├── FeatureSortTest.java │ │ ├── IntRangeSetTest.java │ │ ├── IterableOnceTest.java │ │ ├── LongLongMapTest.java │ │ ├── LongLongMultimapTest.java │ │ ├── LongMergerTest.java │ │ ├── MinHeapTest.java │ │ └── ParallelLongLongMapTest.java │ │ ├── config │ │ └── ArgumentsTest.java │ │ ├── expression │ │ ├── ExpressionTest.java │ │ ├── ExpressionTestUtil.java │ │ └── MultiExpressionTest.java │ │ ├── files │ │ ├── FilesArchiveUtilsTest.java │ │ ├── ReadableFilesArchiveTest.java │ │ ├── TileSchemeEncodingTest.java │ │ └── WriteableFilesArchiveTest.java │ │ ├── geo │ │ ├── DouglasPeuckerSimplifierTest.java │ │ ├── DualMidpointSmootherTest.java │ │ ├── GeoUtilsTest.java │ │ ├── GeometryPipelineTest.java │ │ ├── GeometryTypeTest.java │ │ ├── LineSplitterTest.java │ │ ├── MidpointSmootherTest.java │ │ ├── MutableCoordinateSequenceTest.java │ │ ├── PointIndexTest.java │ │ ├── PolygonIndexTest.java │ │ ├── TileCoordTest.java │ │ ├── TileExtentsTest.java │ │ ├── UnitTest.java │ │ └── VWSimplifierTest.java │ │ ├── mbtiles │ │ ├── MbtilesTest.java │ │ └── VerifyTest.java │ │ ├── pmtiles │ │ └── PmtilesTest.java │ │ ├── reader │ │ ├── GeoPackageReaderTest.java │ │ ├── NaturalEarthReaderTest.java │ │ ├── ShapefileReaderTest.java │ │ ├── SimpleFeatureTest.java │ │ ├── SourceFeatureProcessorTest.java │ │ ├── StructTest.java │ │ ├── geojson │ │ │ ├── GeoJsonReaderTest.java │ │ │ └── GeoJsonTest.java │ │ ├── osm │ │ │ ├── OsmInputFileTest.java │ │ │ ├── OsmMultipolygonTest.java │ │ │ ├── OsmNodeBoundsProviderTest.java │ │ │ ├── OsmPhaserTest.java │ │ │ ├── OsmReaderTest.java │ │ │ └── PolyFileReaderTest.java │ │ └── parquet │ │ │ ├── CoordinateSequenceBuilderTest.java │ │ │ ├── GeoParquetMetadataTest.java │ │ │ ├── ParquetConverterTest.java │ │ │ ├── ParquetFeatureTest.java │ │ │ ├── ParquetInputFileTest.java │ │ │ └── ParquetReaderTest.java │ │ ├── render │ │ ├── FeatureRendererTest.java │ │ └── TiledGeometryTest.java │ │ ├── stats │ │ ├── CounterTest.java │ │ ├── ProcessInfoTest.java │ │ ├── ProgressLoggersTest.java │ │ ├── PrometheusStatsTest.java │ │ ├── StatsTest.java │ │ ├── TimerTest.java │ │ └── TimersTest.java │ │ ├── stream │ │ ├── InMemoryStreamArchive.java │ │ ├── StreamArchiveUtilsTest.java │ │ ├── WriteableCsvArchiveTest.java │ │ ├── WriteableJsonStreamArchiveTest.java │ │ └── WriteableProtoStreamArchiveTest.java │ │ ├── util │ │ ├── AwsOsmTest.java │ │ ├── BinBackTest.java │ │ ├── ByteBufferUtilTest.java │ │ ├── CacheByZoomTest.java │ │ ├── CloseShieldOutputStreamTest.java │ │ ├── CommonStringEncoderTest.java │ │ ├── CompareArchivesTest.java │ │ ├── CountingOutputStreamTest.java │ │ ├── DownloaderTest.java │ │ ├── DuplicateClassLoaderTest.java │ │ ├── FileUtilsTest.java │ │ ├── FileWatcherTest.java │ │ ├── FormatTest.java │ │ ├── GeofabrikTest.java │ │ ├── GlobTest.java │ │ ├── GzipTest.java │ │ ├── HashingTest.java │ │ ├── HilbertTest.java │ │ ├── LanguageUtilsTest.java │ │ ├── LayerAttrStatsTest.java │ │ ├── LogUtilTest.java │ │ ├── LoopLineMergerTest.java │ │ ├── MemoizedTest.java │ │ ├── MergingRangeMapTest.java │ │ ├── MutableCollectionsTest.java │ │ ├── ParseTest.java │ │ ├── ResourceUsageTest.java │ │ ├── SlidingWindowTests.java │ │ ├── SortKeyTest.java │ │ ├── TestClass.java │ │ ├── ThreadLocalTransliteratorTest.java │ │ ├── TileSizeStatsTest.java │ │ ├── TileWeightsTest.java │ │ ├── TilesetSummaryStatisticsTest.java │ │ ├── TopOsmTilesTest.java │ │ ├── TranslationsTest.java │ │ ├── TryTest.java │ │ ├── VarIntTest.java │ │ ├── WikidataTest.java │ │ ├── YamlTest.java │ │ └── ZoomFunctionTest.java │ │ ├── validator │ │ ├── BaseSchemaValidatorTest.java │ │ └── JavaProfileValidatorTest.java │ │ └── worker │ │ ├── DistributorTest.java │ │ ├── WeightedHandoffQueueTest.java │ │ ├── WorkQueueTest.java │ │ ├── WorkerPipelineTest.java │ │ └── WorkerTest.java │ └── resources │ ├── bad_spain_relation.xml │ ├── bostonbuildings.mbtiles │ ├── bottomrightearth.poly │ ├── box1degree.pmtiles │ ├── chesapeake.wkb │ ├── empty-geom.gpkg │ ├── feature.geojson │ ├── featurecollection.geojson │ ├── geopackage-unindexed.gpkg │ ├── geopackage.gpkg │ ├── geopackage.gpkg.zip │ ├── issue_496_baseball_multipolygon.xml │ ├── issue_509_lena_delta.xml │ ├── issue_546_terschelling.wkb │ ├── issue_700 │ ├── exception_1.wkb │ ├── exception_2.wkb │ ├── exception_3.wkb │ ├── exception_4.wkb │ ├── exception_5.wkb │ ├── exception_6.wkb │ ├── exception_7.wkb │ ├── exception_8.wkb │ └── exception_9.wkb │ ├── jakartabuildings.mbtiles │ ├── kobroor.wkb │ ├── log4j2-test.properties │ ├── mdshore.wkb │ ├── mergelines │ ├── i90.wkb.gz │ ├── mergelines_1759_point_line.wkb.gz │ ├── mergelines_200433_lines.wkb.gz │ └── mergelines_239823_lines.wkb.gz │ ├── monaco-latest-without-changesets.osm.pbf │ ├── monaco-latest.lz4.osm.pbf │ ├── monaco-latest.osm.pbf │ ├── natural_earth_vector.sqlite │ ├── natural_earth_vector.sqlite.zip │ ├── newlines.geojson │ ├── njshore.wkb │ ├── parquet │ ├── all_data_types.parquet │ ├── boston.customgeometryname.parquet │ ├── boston.geoarrow_from_gdal.parquet │ ├── boston.gzip.parquet │ ├── boston.lz4hadoop.parquet │ ├── boston.lz4raw.parquet │ ├── boston.none.parquet │ ├── boston.parquet │ ├── boston.snappy.parquet │ └── boston.zstd.parquet │ ├── shapefile-copy.zip │ ├── shapefile.zip │ ├── test.properties │ └── water-polygons-split-3857.zip ├── planetiler-custommap ├── .gitignore ├── README.md ├── planetiler.schema.json ├── planetilerspec.schema.json ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── onthegomap │ │ │ └── planetiler │ │ │ └── custommap │ │ │ ├── BooleanExpressionParser.java │ │ │ ├── ConfigExpressionParser.java │ │ │ ├── ConfiguredFeature.java │ │ │ ├── ConfiguredMapMain.java │ │ │ ├── ConfiguredProfile.java │ │ │ ├── Contexts.java │ │ │ ├── Source.java │ │ │ ├── TagValueProducer.java │ │ │ ├── TypeConversion.java │ │ │ ├── configschema │ │ │ ├── AttributeDefinition.java │ │ │ ├── DataSource.java │ │ │ ├── DataSourceType.java │ │ │ ├── FeatureGeometry.java │ │ │ ├── FeatureItem.java │ │ │ ├── FeatureLayer.java │ │ │ ├── MergeLineStrings.java │ │ │ ├── MergePolygons.java │ │ │ ├── PostProcess.java │ │ │ ├── SchemaConfig.java │ │ │ └── ZoomOverride.java │ │ │ ├── expression │ │ │ ├── BooleanExpressionScript.java │ │ │ ├── ConfigExpression.java │ │ │ ├── ConfigExpressionScript.java │ │ │ ├── EvaluationException.java │ │ │ ├── ParseException.java │ │ │ ├── ScriptContext.java │ │ │ ├── ScriptEnvironment.java │ │ │ └── stdlib │ │ │ │ ├── BuiltInFunction.java │ │ │ │ ├── GeometryVal.java │ │ │ │ ├── PlanetilerLib.java │ │ │ │ ├── PlanetilerStdLib.java │ │ │ │ └── PlanetilerTypeRegistry.java │ │ │ └── validator │ │ │ └── SchemaValidator.java │ └── resources │ │ └── samples │ │ ├── highway_areas.yml │ │ ├── manholes.yml │ │ ├── natural_earth.yml │ │ ├── owg_simple.yml │ │ ├── power.yml │ │ ├── railways.yml │ │ ├── shortbread.spec.yml │ │ └── shortbread.yml │ └── test │ ├── java │ └── com │ │ └── onthegomap │ │ └── planetiler │ │ └── custommap │ │ ├── BooleanExpressionParserTest.java │ │ ├── ConfigExpressionParserTest.java │ │ ├── ConfiguredFeatureTest.java │ │ ├── ConfiguredMapTest.java │ │ ├── ContextsTest.java │ │ ├── SchemaTests.java │ │ ├── SchemaYAMLLoadTest.java │ │ ├── TagValueProducerTest.java │ │ ├── TestContexts.java │ │ ├── TypeConversionTest.java │ │ ├── expression │ │ ├── BooleanExpressionScriptTest.java │ │ ├── ConfigExpressionTest.java │ │ ├── DataTypeTest.java │ │ └── ExpressionTests.java │ │ ├── util │ │ ├── TestConfigurableUtils.java │ │ └── VerifyMonaco.java │ │ └── validator │ │ └── SchemaValidatorTest.java │ └── resources │ ├── invalidSchema │ ├── bad_geometry_type.yml │ ├── invalid_post_process.yml │ └── no_layers.yml │ └── validSchema │ ├── data_type_attributes.yml │ ├── local_path.yml │ ├── road_motorway.yml │ ├── static_attribute.yml │ ├── tag_attribute.yml │ ├── tag_attribute_null.yml │ ├── tag_include.yml │ └── zoom_filter.yml ├── planetiler-dist ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── onthegomap │ └── planetiler │ └── Main.java ├── planetiler-examples ├── README.md ├── pom.xml ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── onthegomap │ │ │ └── planetiler │ │ │ └── examples │ │ │ ├── BikeRouteOverlay.java │ │ │ ├── OsmQaTiles.java │ │ │ ├── ToiletsOverlay.java │ │ │ ├── ToiletsOverlayLowLevelApi.java │ │ │ └── overture │ │ │ └── OvertureBasemap.java │ └── test │ │ └── java │ │ └── com │ │ └── onthegomap │ │ └── planetiler │ │ └── examples │ │ ├── BikeRouteOverlayTest.java │ │ ├── OsmQaTilesTest.java │ │ ├── ToiletsOverlayLowLevelApiTest.java │ │ └── ToiletsProfileTest.java └── standalone.pom.xml ├── planetiler-openmaptiles.pom.xml ├── pom.xml ├── quickstart.sh ├── scripts ├── build-release.sh ├── build.sh ├── check-doc-links.sh ├── check-mbtiles.sh ├── check-monaco.sh ├── fasttests.sh ├── format.sh ├── push-release.sh ├── serve-tiles-docker.sh ├── set-versions.sh ├── sonar.sh └── test-release.sh └── sonar-project.properties /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: onthegomap 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Bug report 4 | about: Create a report to help improve Planetiler 5 | title: "[BUG] " 6 | labels: ["bug"] 7 | assignees: '' 8 | 9 | --- 10 | 11 | **Describe the bug** 12 | A clear and concise description of what the bug is. 13 | 14 | **To Reproduce** 15 | Steps to reproduce the behavior: 16 | 17 | 1. Download data from '...' 18 | 2. Run command '....' 19 | 3. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. For map data issues, please include OSM element IDs and 23 | the vector tile features you expect in the output to make help reproduce the issue in tests. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem (include tile IDs or latitude/longitude for visual issues 27 | with generated maps) 28 | 29 | **Environment (please complete the following information):** 30 | 31 | - Hardware: [e.g. 2015 Macbook Pro] 32 | - OS: [e.g. MacOS 10.15.7] 33 | - Java version and distribution: [e.g. Eclipse Temurin 17.35] 34 | - Maven version: [e.g. 3.8.1] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Feature request 4 | about: Suggest an idea for Planetiler 5 | title: "[FEATURE] " 6 | labels: '' 7 | assignees: '' 8 | 9 | --- 10 | 11 | **Is your feature request related to a problem? Please describe.** 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Additional context** 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/cache-sources-action/action.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/metadata-syntax-for-github-actions 2 | name: 'Cache data/sources' 3 | description: 'Save/restore data/sources cache' 4 | inputs: 5 | basedir: 6 | description: 'Base dir for computing file hash' 7 | required: false 8 | default: '' 9 | runs: 10 | using: 'composite' 11 | steps: 12 | - name: Get Date 13 | id: get-data 14 | run: | 15 | echo "::set-output name=hash::${{ hashFiles('**/Planetiler.java', '**/Downloader.java', '**/Geofabrik.java', '**/OpenMapTilesMain.java') }}" 16 | echo "::set-output name=date::$(date -u "+%Y-%m-%d")" 17 | shell: bash 18 | working-directory: ${{ inputs.basedir }} 19 | - uses: actions/cache@v4 20 | with: 21 | path: data/sources 22 | key: data-sources-${{ steps.get-data.outputs.date }}-${{ steps.get-data.outputs.hash }} 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Configure dependabot automatic version upgrades 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: maven 7 | directory: "/" 8 | open-pull-requests-limit: 1 9 | schedule: 10 | interval: daily 11 | time: "04:25" 12 | timezone: America/New_York 13 | labels: 14 | - dependencies 15 | - package-ecosystem: maven 16 | # workaround for non-standard pom.xml filename (https://github.com/dependabot/dependabot-core/issues/4425) 17 | directory: "/.github/planetiler-examples-dependabot" 18 | open-pull-requests-limit: 1 19 | schedule: 20 | interval: daily 21 | time: "04:30" 22 | timezone: America/New_York 23 | labels: 24 | - dependencies 25 | ignore: 26 | - dependency-name: "com.onthegomap.planetiler:*" 27 | - package-ecosystem: github-actions 28 | directory: "/" 29 | open-pull-requests-limit: 1 30 | schedule: 31 | interval: daily 32 | time: "04:30" 33 | timezone: America/New_York 34 | labels: 35 | - dependencies 36 | -------------------------------------------------------------------------------- /.github/planetiler-examples-dependabot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 4.0.0 13 | 14 | com.onthegomap.planetiler 15 | planetiler-examples-parent-for-dependabot 16 | HEAD 17 | pom 18 | 19 | 20 | ../../planetiler-examples/standalone.pom.xml 21 | 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Docs 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | markdown-link-check: 11 | name: Broken Links 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | with: 17 | # don't need to check planetiler-openmaptiles 18 | submodules: false 19 | - name: Run link check 20 | uses: gaurav-nelson/github-action-markdown-link-check@1.0.13 21 | with: 22 | use-quiet-mode: 'no' 23 | use-verbose-mode: 'yes' 24 | config-file: '.github/workflows/docs_mlc_config.json' 25 | -------------------------------------------------------------------------------- /.github/workflows/docs_mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "^http://localhost.*$" 5 | } 6 | ], 7 | "retryCount": 5, 8 | "fallbackRetryDelay": "30s", 9 | "aliveStatusCodes": [ 10 | 200, 11 | 429 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/snapshot.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds a map using the base and branch commit of a PR and uploads 2 | # the logs as an artifact that update-pr.yml uses to add back as a comment. 3 | 4 | name: Publish a Snapshot 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | workflow_dispatch: 10 | inputs: 11 | image_tags: 12 | description: 'Extra docker image tags ("latest,test")' 13 | required: true 14 | default: 'latest,snapshot' 15 | 16 | jobs: 17 | snapshot: 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 20 20 | permissions: 21 | contents: read 22 | packages: write 23 | steps: 24 | - uses: actions/checkout@v4 25 | with: 26 | submodules: true 27 | - name: Cache data/sources 28 | uses: ./.github/cache-sources-action 29 | - name: Set up JDK 30 | uses: actions/setup-java@v4 31 | with: 32 | java-version: 21 33 | distribution: 'temurin' 34 | cache: 'maven' 35 | server-id: ossrh 36 | server-username: MAVEN_USERNAME 37 | server-password: MAVEN_PASSWORD 38 | gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} 39 | gpg-passphrase: OSSRH_GPG_SECRET_KEY_PASSWORD 40 | - run: ./scripts/build-release.sh 41 | - run: ./scripts/test-release.sh 42 | - run: sha256sum planetiler-dist/target/*with-deps.jar 43 | - run: md5sum planetiler-dist/target/*with-deps.jar 44 | - name: 'Upload artifact' 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: planetiler-build 48 | path: planetiler-dist/target/*with-deps.jar 49 | - run: ./scripts/push-release.sh 50 | env: 51 | GITHUB_ACTOR: ${{ github.actor }} 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | IMAGE_TAGS: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tags || 'latest,snapshot' }} 54 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 55 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 56 | OSSRH_GPG_SECRET_KEY_PASSWORD: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | target/ 4 | .flattened-pom.xml 5 | *.jar 6 | !.mvn/wrapper/*.jar 7 | *.log 8 | 9 | # idea 10 | */.idea 11 | .idea/* 12 | *.iml 13 | !.idea/codeStyles 14 | !.idea/vcs.xml 15 | !.idea/eclipseCodeFormatter.xml 16 | !.idea/jsonSchemas.xml 17 | 18 | # eclipse 19 | .classpath 20 | .project 21 | .settings 22 | bin/ 23 | 24 | TODO 25 | 26 | data/ 27 | *-unzipped/ 28 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "planetiler-openmaptiles"] 2 | path = planetiler-openmaptiles 3 | url = https://github.com/openmaptiles/planetiler-openmaptiles.git 4 | branch = main 5 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/eclipseCodeFormatter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/jsonSchemas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED 2 | --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED 3 | --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED 4 | --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED 5 | --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED 6 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.format.settings.url": "eclipse-formatter.xml", 3 | "java.format.settings.profile": "Planetiler", 4 | "java.completion.importOrder": ["#", ""], 5 | "java.sources.organizeImports.staticStarThreshold": 5, 6 | "java.sources.organizeImports.starThreshold": 999, 7 | "java.saveActions.organizeImports": true, 8 | "yaml.schemas": { 9 | "./planetiler-custommap/planetiler.schema.json": "planetiler-custommap/**/*.yml" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /diagrams/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/diagrams/architecture.png -------------------------------------------------------------------------------- /diagrams/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/diagrams/demo.png -------------------------------------------------------------------------------- /diagrams/slicing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/diagrams/slicing.png -------------------------------------------------------------------------------- /layerstats/top_osm_tiles.tsv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/layerstats/top_osm_tiles.tsv.gz -------------------------------------------------------------------------------- /planet-logs/v0.1.0-planet-do-16cpu-128gb.nps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planet-logs/v0.1.0-planet-do-16cpu-128gb.nps -------------------------------------------------------------------------------- /planet-logs/v0.1.0-planet-linode-50cpu-128gb.nps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planet-logs/v0.1.0-planet-linode-50cpu-128gb.nps -------------------------------------------------------------------------------- /planet-logs/v0.5.0-planet-c6gd-128gb-no-z13-building-merge.nps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planet-logs/v0.5.0-planet-c6gd-128gb-no-z13-building-merge.nps -------------------------------------------------------------------------------- /planet-logs/v0.5.0-planet-c6gd-128gb.nps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planet-logs/v0.5.0-planet-c6gd-128gb.nps -------------------------------------------------------------------------------- /planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/benchmarks/BenchmarkOsmRead.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.benchmarks; 2 | 3 | import com.onthegomap.planetiler.Profile; 4 | import com.onthegomap.planetiler.collection.LongLongMap; 5 | import com.onthegomap.planetiler.collection.LongLongMultimap; 6 | import com.onthegomap.planetiler.config.Arguments; 7 | import com.onthegomap.planetiler.config.PlanetilerConfig; 8 | import com.onthegomap.planetiler.reader.osm.OsmInputFile; 9 | import com.onthegomap.planetiler.reader.osm.OsmReader; 10 | import com.onthegomap.planetiler.stats.Stats; 11 | import com.onthegomap.planetiler.stats.Timer; 12 | import java.io.IOException; 13 | import java.nio.file.Path; 14 | 15 | public class BenchmarkOsmRead { 16 | 17 | public static void main(String[] args) throws IOException { 18 | var profile = new Profile.NullProfile(); 19 | var stats = Stats.inMemory(); 20 | var parsedArgs = Arguments.fromArgsOrConfigFile(args); 21 | var config = PlanetilerConfig.from(parsedArgs); 22 | var path = parsedArgs.inputFile("osm_path", "path to osm file", Path.of("data/sources/northeast.osm.pbf")); 23 | OsmInputFile file = new OsmInputFile(path, config.osmLazyReads()); 24 | 25 | while (true) { 26 | Timer timer = Timer.start(); 27 | try ( 28 | var nodes = LongLongMap.noop(); 29 | var multipolygons = LongLongMultimap.noop(); 30 | var reader = new OsmReader("osm", file, nodes, multipolygons, profile, stats) 31 | ) { 32 | reader.pass1(config); 33 | } 34 | System.err.println(timer.stop()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/benchmarks/BenchmarkParquetRead.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.benchmarks; 2 | 3 | import com.onthegomap.planetiler.config.Arguments; 4 | import com.onthegomap.planetiler.config.Bounds; 5 | import com.onthegomap.planetiler.reader.parquet.ParquetInputFile; 6 | import java.nio.file.Path; 7 | 8 | public class BenchmarkParquetRead { 9 | 10 | public static void main(String[] args) { 11 | var arguments = Arguments.fromArgs(args); 12 | var path = 13 | arguments.inputFile("parquet", "parquet file to read", Path.of("data", "sources", "locality.zstd.parquet")); 14 | long c = 0; 15 | var file = new ParquetInputFile("parquet", "locality", path, null, Bounds.WORLD, null, tags -> tags.get("id")); 16 | for (int i = 0; i < 20; i++) { 17 | long start = System.currentTimeMillis(); 18 | for (var block : file.get()) { 19 | for (var item : block) { 20 | c += item.tags().size(); 21 | } 22 | } 23 | System.err.println(System.currentTimeMillis() - start); 24 | } 25 | 26 | System.err.println(c); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/benchmarks/BenchmarkPmtiles.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.benchmarks; 2 | 3 | import static io.prometheus.client.Collector.NANOSECONDS_PER_SECOND; 4 | 5 | import com.onthegomap.planetiler.pmtiles.Pmtiles; 6 | import com.onthegomap.planetiler.stats.Timer; 7 | import com.onthegomap.planetiler.util.Format; 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Random; 11 | 12 | public class BenchmarkPmtiles { 13 | 14 | public static void main(String[] args) throws IOException { 15 | 16 | long num = 60_000_000; 17 | 18 | var random = new Random(0); 19 | 20 | for (int i = 0; i < 3; i++) { 21 | 22 | var entries = new ArrayList(); 23 | 24 | long offset = 0; 25 | for (int j = 0; j < num; j++) { 26 | int len = 200 + random.nextInt(64000); 27 | entries.add(new Pmtiles.Entry(j, offset, len, 1)); 28 | offset += len; 29 | } 30 | 31 | var timer = Timer.start(); 32 | 33 | var result = Pmtiles.directoryFromBytes(Pmtiles.directoryToBytes(entries)); 34 | assert (result.size() == entries.size()); 35 | 36 | System.err.println( 37 | num + " entries took " + 38 | Format.defaultInstance().duration(timer.stop().elapsed().wall()) + " (" + 39 | Format.defaultInstance() 40 | .numeric(num * 1d / (timer.stop().elapsed().wall().toNanos() / NANOSECONDS_PER_SECOND)) + 41 | "/s)" 42 | ); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/benchmarks/BenchmarkTileCoord.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.benchmarks; 2 | 3 | import static io.prometheus.client.Collector.NANOSECONDS_PER_SECOND; 4 | 5 | import com.onthegomap.planetiler.geo.TileCoord; 6 | import com.onthegomap.planetiler.stats.Timer; 7 | import com.onthegomap.planetiler.util.Format; 8 | 9 | public class BenchmarkTileCoord { 10 | 11 | public static void main(String[] args) { 12 | for (int i = 0; i < 3; i++) { 13 | var timer = Timer.start(); 14 | int num = 0; 15 | for (int z = 0; z <= 14; z++) { 16 | int max = 1 << z; 17 | for (int x = 0; x < max; x++) { 18 | for (int y = 0; y < max; y++) { 19 | int encoded = TileCoord.encode(x, y, z); 20 | int decoded = TileCoord.decode(encoded).encoded(); 21 | // make sure we use the result so it doesn't get jit'ed-out 22 | if (encoded != decoded) { 23 | System.err.println("Error on " + z + "/" + x + "/" + y); 24 | } 25 | num++; 26 | } 27 | } 28 | } 29 | System.err.println( 30 | "z0-z14 took " + 31 | Format.defaultInstance().duration(timer.stop().elapsed().wall()) + " (" + 32 | Format.defaultInstance() 33 | .numeric(num * 1d / (timer.stop().elapsed().wall().toNanos() / NANOSECONDS_PER_SECOND)) + 34 | "/s)" 35 | ); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/benchmarks/BenchmarkVarInt.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.benchmarks; 2 | 3 | import static io.prometheus.client.Collector.NANOSECONDS_PER_SECOND; 4 | 5 | import com.carrotsearch.hppc.ByteArrayList; 6 | import com.onthegomap.planetiler.stats.Timer; 7 | import com.onthegomap.planetiler.util.Format; 8 | import com.onthegomap.planetiler.util.VarInt; 9 | import java.io.IOException; 10 | import java.nio.ByteBuffer; 11 | 12 | public class BenchmarkVarInt { 13 | 14 | public static void main(String[] args) throws IOException { 15 | 16 | long num = 100000000; 17 | 18 | for (int i = 0; i < 3; i++) { 19 | ByteArrayList stream = new ByteArrayList(); 20 | var timer = Timer.start(); 21 | 22 | long sum = 0; 23 | 24 | for (long l = 0; l < num; l++) { 25 | VarInt.putVarLong(l, stream); 26 | sum += l; 27 | } 28 | 29 | ByteBuffer buf = ByteBuffer.wrap(stream.toArray()); 30 | 31 | long acc = 0; 32 | for (long l = 0; l < num; l++) { 33 | acc += VarInt.getVarLong(buf); 34 | } 35 | 36 | if (sum != acc) { 37 | System.err.println("Sums do not match"); 38 | } 39 | 40 | 41 | System.err.println( 42 | num + " varints took " + 43 | Format.defaultInstance().duration(timer.stop().elapsed().wall()) + " (" + 44 | Format.defaultInstance() 45 | .numeric(num * 1d / (timer.stop().elapsed().wall().toNanos() / NANOSECONDS_PER_SECOND)) + 46 | "/s)" 47 | ); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/collection/StressTestKWayMerge.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | public class StressTestKWayMerge { 12 | 13 | public static void main(String[] args) throws InterruptedException { 14 | for (int i = 1; i < 20; i++) { 15 | test(i, 100_000, 200_000); 16 | } 17 | for (int i = 50; i <= 500; i += 50) { 18 | test(i, 10_000, 20_000); 19 | } 20 | test(5_000, 1000, 2000); 21 | } 22 | 23 | private static void test(int n, long items, long maxKey) throws InterruptedException { 24 | System.out.println("test(" + n + ")"); 25 | var random = new Random(0); 26 | List> featureLists = new ArrayList<>(); 27 | ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 28 | for (int i = 0; i < n; i++) { 29 | List list = new ArrayList<>(); 30 | featureLists.add(list); 31 | for (int j = 0; j < items; j++) { 32 | byte[] bytes = new byte[random.nextInt(1, 10)]; 33 | random.nextBytes(bytes); 34 | list.add(new SortableFeature(random.nextLong(maxKey), bytes)); 35 | } 36 | executorService.submit(() -> list.sort(Comparator.naturalOrder())); 37 | } 38 | executorService.shutdown(); 39 | executorService.awaitTermination(1, TimeUnit.DAYS); 40 | 41 | 42 | var iter = 43 | LongMerger.mergeIterators(featureLists.stream().map(List::iterator).toList(), SortableFeature.COMPARE_BYTES); 44 | var last = iter.next(); 45 | int i = 1; 46 | while (iter.hasNext()) { 47 | i++; 48 | var item = iter.next(); 49 | if (last.compareTo(item) > 0) { 50 | System.err 51 | .println("items out of order lists=" + n + " last=" + last + " item=" + item + " i=" + i); 52 | return; 53 | } 54 | last = item; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureProcessor.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler; 2 | 3 | import com.onthegomap.planetiler.reader.SourceFeature; 4 | 5 | /** 6 | * Subcomponent of {@link Profile} that handles processing layers from a feature, and optionally when that source is 7 | * finished. 8 | */ 9 | @FunctionalInterface 10 | public interface FeatureProcessor { 11 | 12 | /** 13 | * Generates output features for any input feature that should appear in the map. 14 | *

15 | * Multiple threads may invoke this method concurrently for a single data source so implementations should ensure 16 | * thread-safe access to any shared data structures. Separate data sources are processed sequentially. 17 | *

18 | * All OSM nodes are processed first, then ways, then relations. 19 | * 20 | * @param sourceFeature the input feature from a source dataset (OSM element, shapefile element, etc.) 21 | * @param features a collector for generating output map features to emit 22 | */ 23 | void processFeature(T sourceFeature, FeatureCollector features); 24 | } 25 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/archive/ReadableTileArchive.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.archive; 2 | 3 | import com.onthegomap.planetiler.geo.TileCoord; 4 | import com.onthegomap.planetiler.util.CloseableIterator; 5 | import java.io.Closeable; 6 | 7 | /** 8 | * Read API for on-disk representation of a tileset in a portable format. Example: MBTiles, a sqlite-based archive 9 | * format. 10 | *

11 | * See {@link WriteableTileArchive} for the write API. 12 | */ 13 | public interface ReadableTileArchive extends Closeable { 14 | 15 | /** Returns the raw tile data at {@code coord} or {@code null} if not found. */ 16 | default byte[] getTile(TileCoord coord) { 17 | return getTile(coord.x(), coord.y(), coord.z()); 18 | } 19 | 20 | /** Returns the raw tile data at {@code x, y, z} or {@code null} if not found. */ 21 | byte[] getTile(int x, int y, int z); 22 | 23 | /** 24 | * Returns an iterator over the coordinates of tiles in this archive. 25 | *

26 | * The order should respect {@link WriteableTileArchive#tileOrder()} of the corresponding writer. 27 | *

28 | * Clients should be sure to close the iterator after iterating through it, for example: 29 | * 30 | *

31 |    * {@code
32 |    * try (var iter = archive.getAllTileCoords()) {
33 |    *   while (iter.hasNext()) {
34 |    *     var coord = iter.next();
35 |    *     ...
36 |    *   }
37 |    * }
38 |    * }
39 |    * 
40 | */ 41 | CloseableIterator getAllTileCoords(); 42 | 43 | default CloseableIterator getAllTiles() { 44 | return getAllTileCoords().map(coord -> new Tile(coord, getTile(coord))); 45 | } 46 | 47 | /** 48 | * Returns the metadata stored in this archive. 49 | */ 50 | TileArchiveMetadata metadata(); 51 | 52 | // TODO access archive metadata 53 | } 54 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/archive/Tile.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.archive; 2 | 3 | import com.onthegomap.planetiler.geo.TileCoord; 4 | import java.util.Arrays; 5 | import java.util.Objects; 6 | 7 | /** A tile stored in an archive with coordinate {@code coord} and archived {@code bytes}. */ 8 | public record Tile(TileCoord coord, byte[] bytes) implements Comparable { 9 | 10 | @Override 11 | public boolean equals(Object o) { 12 | return (this == o) || 13 | (o instanceof Tile other && Objects.equals(coord, other.coord) && Arrays.equals(bytes, other.bytes)); 14 | } 15 | 16 | @Override 17 | public int hashCode() { 18 | int result = coord.hashCode(); 19 | result = 31 * result + Arrays.hashCode(bytes); 20 | return result; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "Tile{coord=" + coord + ", data=byte[" + bytes.length + "]}"; 26 | } 27 | 28 | @Override 29 | public int compareTo(Tile o) { 30 | return coord.compareTo(o.coord); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/archive/TileEncodingResult.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.archive; 2 | 3 | import com.onthegomap.planetiler.geo.TileCoord; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Objects; 7 | import java.util.OptionalLong; 8 | 9 | public record TileEncodingResult( 10 | TileCoord coord, 11 | byte[] tileData, 12 | int rawTileSize, 13 | /* will always be empty in non-compact mode and might also be empty in compact mode */ 14 | OptionalLong tileDataHash, 15 | List layerStats 16 | ) { 17 | public TileEncodingResult( 18 | TileCoord coord, 19 | byte[] tileData, 20 | OptionalLong tileDataHash 21 | ) { 22 | this(coord, tileData, tileData.length, tileDataHash, List.of()); 23 | } 24 | 25 | @Override 26 | public int hashCode() { 27 | final int prime = 31; 28 | int result = 1; 29 | result = prime * result + Arrays.hashCode(tileData); 30 | result = prime * result + Objects.hash(coord, tileDataHash); 31 | return result; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object obj) { 36 | return this == obj || (obj instanceof TileEncodingResult other && 37 | Objects.equals(coord, other.coord) && 38 | Arrays.equals(tileData, other.tileData) && 39 | Objects.equals(tileDataHash, other.tileDataHash)); 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "TileEncodingResult [coord=" + coord + ", tileData=" + Arrays.toString(tileData) + ", tileDataHash=" + 45 | tileDataHash + "]"; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/archive/WriteableTileArchive.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.archive; 2 | 3 | import com.onthegomap.planetiler.config.PlanetilerConfig; 4 | import com.onthegomap.planetiler.geo.TileOrder; 5 | import java.io.Closeable; 6 | import net.jcip.annotations.NotThreadSafe; 7 | 8 | /** 9 | * Write API for an on-disk representation of a tileset in a portable format. Example: MBTiles, a sqlite-based archive 10 | * format. 11 | *

12 | * See {@link ReadableTileArchive} for the read API. 13 | */ 14 | @NotThreadSafe 15 | public interface WriteableTileArchive extends Closeable { 16 | 17 | /** 18 | * Returns true if this tile archive deduplicates tiles with the same content. 19 | *

20 | * If false, then {@link TileWriter} will skip computing tile hashes. 21 | */ 22 | boolean deduplicates(); 23 | 24 | /** 25 | * Specify the preferred insertion order for this archive, e.g. {@link TileOrder#TMS} or {@link TileOrder#HILBERT}. 26 | */ 27 | TileOrder tileOrder(); 28 | 29 | /** 30 | * Called before any tiles are written into {@link TileWriter}. Implementations of TileArchive should set up any 31 | * required state here. 32 | */ 33 | default void initialize() {} 34 | 35 | /** 36 | * Implementations should return a object that implements {@link TileWriter} The specific TileWriter returned might 37 | * depend on {@link PlanetilerConfig}. 38 | */ 39 | TileWriter newTileWriter(); 40 | 41 | /** 42 | * Called after all tiles are written into {@link TileWriter}. After this is called, the archive should be complete on 43 | * disk. 44 | */ 45 | default void finish(TileArchiveMetadata tileArchiveMetadata) {} 46 | 47 | long bytesWritten(); 48 | 49 | interface TileWriter extends Closeable { 50 | 51 | void write(TileEncodingResult encodingResult); 52 | 53 | @Override 54 | void close(); 55 | 56 | default void printStats() {} 57 | } 58 | 59 | // TODO update archive metadata 60 | } 61 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/collection/HasLongSortKey.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | /** 4 | * An item with a {@code long key} that can be used for sorting/grouping. 5 | *

6 | * These items can be sorted or grouped by {@link FeatureSort}/{@link FeatureGroup} implementations. Sorted lists can 7 | * also be merged using {@link LongMerger}. 8 | */ 9 | public interface HasLongSortKey { 10 | /** Value to sort/group items by. */ 11 | long key(); 12 | } 13 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/collection/Hppc.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | import com.carrotsearch.hppc.IntObjectHashMap; 4 | import com.carrotsearch.hppc.LongByteHashMap; 5 | import com.carrotsearch.hppc.LongByteMap; 6 | import com.carrotsearch.hppc.LongIntHashMap; 7 | import com.carrotsearch.hppc.LongLongHashMap; 8 | import com.carrotsearch.hppc.LongObjectHashMap; 9 | import com.carrotsearch.hppc.ObjectIntHashMap; 10 | import com.carrotsearch.hppc.SortedIterationLongObjectHashMap; 11 | 12 | /** 13 | * Static factory method for High Performance Primitive Collections. 14 | */ 15 | public class Hppc { 16 | 17 | public static IntObjectHashMap newIntObjectHashMap() { 18 | return new IntObjectHashMap<>(10, 0.75); 19 | } 20 | 21 | public static ObjectIntHashMap newObjectIntHashMap() { 22 | return new ObjectIntHashMap<>(10, 0.75); 23 | } 24 | 25 | public static LongLongHashMap newLongLongHashMap() { 26 | return new LongLongHashMap(10, 0.75); 27 | } 28 | 29 | public static LongObjectHashMap newLongObjectHashMap() { 30 | return new LongObjectHashMap<>(10, 0.75); 31 | } 32 | 33 | public static LongObjectHashMap newLongObjectHashMap(int size) { 34 | return new LongObjectHashMap<>(size, 0.75); 35 | } 36 | 37 | public static LongIntHashMap newLongIntHashMap() { 38 | return new LongIntHashMap(10, 0.75); 39 | } 40 | 41 | public static LongByteMap newLongByteHashMap() { 42 | return new LongByteHashMap(10, 0.75); 43 | } 44 | 45 | public static SortedIterationLongObjectHashMap sortedView(LongObjectHashMap input) { 46 | return new SortedIterationLongObjectHashMap<>(input, Long::compare); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/collection/IterableOnce.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | import java.util.Iterator; 4 | import java.util.function.Supplier; 5 | 6 | /** 7 | * A {@link Supplier} that returns {@code null} when there are no elements left, with an {@link Iterable} view to 8 | * support for each loop. 9 | * 10 | * @param Type of element returned 11 | */ 12 | public interface IterableOnce extends Iterable, Supplier { 13 | 14 | @Override 15 | default Iterator iterator() { 16 | return new SupplierIterator<>(this); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/collection/SortableFeature.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | public record SortableFeature(@Override long key, byte[] value) implements Comparable, HasLongSortKey { 7 | public static final Comparator COMPARE_BYTES = (a, b) -> Arrays.compareUnsigned(a.value, b.value); 8 | 9 | @Override 10 | public int compareTo(SortableFeature o) { 11 | if (key < o.key) { 12 | return -1; 13 | } else if (key == o.key) { 14 | return Arrays.compareUnsigned(value, o.value); 15 | } else { 16 | return 1; 17 | } 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "SortableFeature{" + 23 | "key=" + key + 24 | ", value=" + Arrays.toString(value) + 25 | '}'; 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) { 31 | return true; 32 | } 33 | if (o == null || getClass() != o.getClass()) { 34 | return false; 35 | } 36 | 37 | SortableFeature entry = (SortableFeature) o; 38 | 39 | if (key != entry.key) { 40 | return false; 41 | } 42 | return Arrays.equals(value, entry.value); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | int result = (int) (key ^ (key >>> 32)); 48 | result = 31 * result + Arrays.hashCode(value); 49 | return result; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/collection/Storage.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | import java.nio.file.Path; 4 | 5 | /** 6 | * Storage method to use for {@link LongLongMap} and {@link AppendStore} implementations. 7 | */ 8 | public enum Storage { 9 | /** Primitive {@code int[]} or {@code long[]} arrays stored on the JVM heap. */ 10 | RAM("ram"), 11 | /** Memory-mapped files stored on disk. */ 12 | MMAP("mmap"), 13 | /** Off-heap native byte buffers stored in-memory but outside the JVM heap. */ 14 | DIRECT("direct"); 15 | 16 | private final String id; 17 | 18 | Storage(String id) { 19 | this.id = id; 20 | } 21 | 22 | public String id() { 23 | return id; 24 | } 25 | 26 | /** 27 | * Returns the storage type associated with {@code id} or throws {@link IllegalArgumentException} if no match is 28 | * found. 29 | */ 30 | public static Storage from(String id) { 31 | for (Storage value : values()) { 32 | if (value.id.equalsIgnoreCase(id.trim())) { 33 | return value; 34 | } 35 | } 36 | throw new IllegalArgumentException("Unexpected storage type: " + id); 37 | } 38 | 39 | /** Options for implementations that vary by {@link Storage}. */ 40 | public record Params(Path path, boolean madvise) { 41 | 42 | /** Returns a copy of this instance, with {@code suffix} appended to {@link #path}. */ 43 | public Params resolve(String suffix) { 44 | return new Params(path.resolve(suffix), madvise); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/collection/SupplierIterator.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.collection; 2 | 3 | import java.util.Iterator; 4 | import java.util.NoSuchElementException; 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * Adapts a {@link Supplier} that returns {@code null} when no items are left to an {@link Iterator} where 9 | * {@link #hasNext()} returns {@code false} when there are no items left. 10 | */ 11 | public class SupplierIterator implements Iterator { 12 | private final Supplier supplier; 13 | T next = null; 14 | boolean stale = true; 15 | 16 | public SupplierIterator(Supplier supplier) { 17 | this.supplier = supplier; 18 | } 19 | 20 | private void advance() { 21 | if (stale) { 22 | next = supplier.get(); 23 | stale = false; 24 | } 25 | } 26 | 27 | @Override 28 | public boolean hasNext() { 29 | advance(); 30 | return next != null; 31 | } 32 | 33 | @Override 34 | public T next() { 35 | if (!hasNext()) { 36 | throw new NoSuchElementException(); 37 | } 38 | stale = true; 39 | return next; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/expression/Simplifiable.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.expression; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * An expression that can be simplified to an equivalent, but cheaper to evaluate expression. 8 | *

9 | * Implementers should only override {@link #simplifyOnce()} which applies all the rules that can be used to simplify 10 | * this expression, and {@link #simplify()} will take care of applying it repeatedly until the output settles to a fixed 11 | * point. 12 | *

13 | * Implementers must also ensure {@code equals} and {@code hashCode} reflect equivalence between expressions so that 14 | * {@link #simplify()} can know when to stop. 15 | */ 16 | public interface Simplifiable> { 17 | 18 | /** 19 | * Returns a copy of this expression, with all simplification rules applied once. 20 | *

21 | * {@link #simplify()} will take care of applying it repeatedly until the output settles. 22 | */ 23 | default T simplifyOnce() { 24 | return self(); 25 | } 26 | 27 | default T self() { 28 | @SuppressWarnings("unchecked") T self = (T) this; 29 | return self; 30 | } 31 | 32 | /** 33 | * Returns an equivalent, simplified copy of this expression but does not modify {@code this} by repeatedly running 34 | * {@link #simplifyOnce()}. 35 | */ 36 | default T simplify() { 37 | // iteratively simplify the expression until we reach a fixed point and start seeing 38 | // an expression that's already been seen before 39 | T simplified = self(); 40 | Set seen = new HashSet<>(); 41 | seen.add(simplified); 42 | while (true) { 43 | simplified = simplified.simplifyOnce(); 44 | if (seen.contains(simplified)) { 45 | return simplified; 46 | } 47 | if (seen.size() > 1000) { 48 | throw new IllegalStateException("Infinite loop while simplifying " + this); 49 | } 50 | seen.add(simplified); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/expression/TypedGetter.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.expression; 2 | 3 | import com.onthegomap.planetiler.reader.WithTags; 4 | 5 | @FunctionalInterface 6 | public interface TypedGetter { 7 | Object apply(WithTags withTags, String tag); 8 | } 9 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/geo/SimplifyMethod.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.geo; 2 | 3 | public enum SimplifyMethod { 4 | RETAIN_IMPORTANT_POINTS, 5 | RETAIN_EFFECTIVE_AREAS, 6 | RETAIN_WEIGHTED_EFFECTIVE_AREAS; 7 | 8 | public static final SimplifyMethod DOUGLAS_PEUCKER = RETAIN_IMPORTANT_POINTS; 9 | public static final SimplifyMethod VISVALINGAM_WHYATT = RETAIN_EFFECTIVE_AREAS; 10 | } 11 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/geo/TileOrder.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.geo; 2 | 3 | import com.onthegomap.planetiler.archive.WriteableTileArchive; 4 | import java.util.function.IntFunction; 5 | import java.util.function.ToDoubleBiFunction; 6 | import java.util.function.ToIntFunction; 7 | 8 | /** 9 | * Controls the sort order of {@link com.onthegomap.planetiler.collection.FeatureGroup}, which determines the ordering 10 | * of {@link com.onthegomap.planetiler.archive.TileEncodingResult}s when written to 11 | * {@link WriteableTileArchive.TileWriter}. 12 | */ 13 | public enum TileOrder { 14 | TMS(TileCoord::encoded, TileCoord::decode, TileCoord::progressOnLevel), 15 | HILBERT(TileCoord::hilbertEncoded, TileCoord::hilbertDecode, TileCoord::hilbertProgressOnLevel); 16 | 17 | private final ToIntFunction encode; 18 | private final IntFunction decode; 19 | private final ToDoubleBiFunction progressOnLevel; 20 | 21 | private TileOrder(ToIntFunction encode, IntFunction decode, 22 | ToDoubleBiFunction progressOnLevel) { 23 | this.encode = encode; 24 | this.decode = decode; 25 | this.progressOnLevel = progressOnLevel; 26 | } 27 | 28 | public int encode(TileCoord coord) { 29 | return encode.applyAsInt(coord); 30 | } 31 | 32 | public TileCoord decode(int encoded) { 33 | return decode.apply(encoded); 34 | } 35 | 36 | public double progressOnLevel(TileCoord coord, TileExtents extents) { 37 | return progressOnLevel.applyAsDouble(coord, extents); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/geo/TilePredicate.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.geo; 2 | 3 | public interface TilePredicate { 4 | boolean test(int x, int y); 5 | } 6 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/FileFormatException.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | /** 4 | * Error encountered while parsing an input file. 5 | */ 6 | public class FileFormatException extends RuntimeException { 7 | public FileFormatException(String message) { 8 | super(message); 9 | } 10 | 11 | public FileFormatException(String message, Throwable throwable) { 12 | super(message, throwable); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/JsonConversion.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.SerializationFeature; 6 | import com.fasterxml.jackson.databind.json.JsonMapper; 7 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 8 | import java.io.UncheckedIOException; 9 | 10 | /** 11 | * Utilities for converting between JSON strings and java objects using Jackson utilities. 12 | *

13 | * {@link ObjectMapper} are expensive to construct, but not thread safe, so this class reuses the same object mapper 14 | * within each thread but does not share between threads. 15 | */ 16 | class JsonConversion { 17 | private JsonConversion() {} 18 | 19 | @SuppressWarnings("java:S5164") // ignore not calling remove() on mappers since number of threads is limited 20 | private static final ThreadLocal MAPPERS = ThreadLocal.withInitial(() -> JsonMapper.builder() 21 | .addModule( 22 | new JavaTimeModule().addSerializer(Struct.class, new StructSerializer()) 23 | ) 24 | .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) 25 | .build()); 26 | 27 | public static String writeValueAsString(Object o) { 28 | try { 29 | return o == null ? null : MAPPERS.get().writeValueAsString(o); 30 | } catch (JsonProcessingException e) { 31 | throw new UncheckedIOException(e); 32 | } 33 | } 34 | 35 | public static T convertValue(Object o, Class clazz) { 36 | return o == null ? null : MAPPERS.get().convertValue(o, clazz); 37 | } 38 | 39 | public static T readValue(String string, Class clazz) { 40 | try { 41 | return string == null ? null : MAPPERS.get().readValue(string, clazz); 42 | } catch (JsonProcessingException e) { 43 | return null; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/SimpleReader.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | import com.onthegomap.planetiler.reader.osm.OsmReader; 4 | import java.io.Closeable; 5 | import java.util.function.Consumer; 6 | 7 | 8 | /** 9 | * Base class for utilities that read {@link SourceFeature SourceFeatures} from a simple data source where geometries 10 | * can be read in a single pass, like {@link ShapefileReader} but not {@link OsmReader} which requires complex 11 | * multi-pass processing. 12 | *

13 | * Implementations provide features through {@link #readFeatures(Consumer)}} and {@link #getFeatureCount()}}. 14 | */ 15 | public abstract class SimpleReader implements Closeable { 16 | 17 | protected final String sourceName; 18 | 19 | protected SimpleReader(String sourceName) { 20 | this.sourceName = sourceName; 21 | } 22 | 23 | /** Returns the number of features to be read from this reader to use for displaying progress. */ 24 | public abstract long getFeatureCount(); 25 | 26 | /** Reads all features in this data provider, submitting each to {@code next} for further processing. */ 27 | @SuppressWarnings("java:S112") 28 | public abstract void readFeatures(Consumer next) throws Exception; 29 | } 30 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/StructSerializer.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.SerializerProvider; 5 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 6 | import java.io.IOException; 7 | 8 | class StructSerializer extends StdSerializer { 9 | 10 | public StructSerializer() { 11 | this(null); 12 | } 13 | 14 | public StructSerializer(Class t) { 15 | super(t); 16 | } 17 | 18 | @Override 19 | public void serialize( 20 | Struct value, JsonGenerator jgen, SerializerProvider provider) 21 | throws IOException { 22 | jgen.writePOJO(value.rawValue()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/WithGeometryType.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | import org.locationtech.jts.geom.LineString; 4 | import org.locationtech.jts.geom.MultiLineString; 5 | import org.locationtech.jts.geom.MultiPoint; 6 | import org.locationtech.jts.geom.MultiPolygon; 7 | import org.locationtech.jts.geom.Point; 8 | import org.locationtech.jts.geom.Polygon; 9 | 10 | /** 11 | * Something attached to a geometry that can be matched using a 12 | * {@link com.onthegomap.planetiler.expression.Expression.MatchType} geometry type filter expression. 13 | */ 14 | public interface WithGeometryType { 15 | 16 | /** Returns true if this feature can be interpreted as a {@link Point} or {@link MultiPoint}. */ 17 | boolean isPoint(); 18 | 19 | /** 20 | * Returns true if this feature can be interpreted as a {@link Polygon} or {@link MultiPolygon}. 21 | *

22 | * A closed ring can either be a polygon or linestring, so return false to not allow this closed ring to be treated as 23 | * a polygon. 24 | */ 25 | boolean canBePolygon(); 26 | 27 | /** 28 | * Returns true if this feature can be interpreted as a {@link LineString} or {@link MultiLineString}. 29 | *

30 | * A closed ring can either be a polygon or linestring, so return false to not allow this closed ring to be treated as 31 | * a linestring. 32 | */ 33 | boolean canBeLine(); 34 | } 35 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/WithSource.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | public interface WithSource { 4 | String getSource(); 5 | } 6 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/WithSourceLayer.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader; 2 | 3 | public interface WithSourceLayer { 4 | String getSourceLayer(); 5 | } 6 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/geojson/GeoJsonFeature.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.geojson; 2 | 3 | import com.onthegomap.planetiler.reader.WithTags; 4 | import java.util.Map; 5 | import org.locationtech.jts.geom.Geometry; 6 | 7 | /** 8 | * Feature read from a geojson document. 9 | * 10 | * @param geometry The parsed JTS geometry from {@code geometry} field, or an empty geometry if it was invalid 11 | * @param tags The parsed map from {@code properties} field, or empty map if properties are missing 12 | */ 13 | public record GeoJsonFeature(Geometry geometry, @Override Map tags) 14 | implements WithTags {} 15 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/geojson/GeoJsonFeatureCounter.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.geojson; 2 | 3 | import com.fasterxml.jackson.core.JsonFactory; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonToken; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | /** 10 | * Internal streaming utility to count the number of features in a geojson document. 11 | *

12 | * To simplify processing, it counts the number of "geometry" fields on objects, and descends into any "features" array 13 | * to traverse FeatureCollections. This will result in the correct count for valid geojson, but may be off for invalid 14 | * geojson. 15 | */ 16 | class GeoJsonFeatureCounter { 17 | private GeoJsonFeatureCounter() {} 18 | 19 | static long count(InputStream inputStream) throws IOException { 20 | long count = 0; 21 | try ( 22 | JsonParser parser = 23 | new JsonFactory().enable(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION).createParser(inputStream) 24 | ) { 25 | while (!parser.isClosed()) { 26 | JsonToken token = parser.nextToken(); 27 | if (token == JsonToken.START_ARRAY) { 28 | parser.skipChildren(); 29 | } else if (token == JsonToken.FIELD_NAME) { 30 | String name = parser.currentName(); 31 | parser.nextToken(); 32 | if ("geometry".equals(name)) { 33 | parser.skipChildren(); 34 | count++; 35 | } else if (!"features".equals(name)) { 36 | parser.skipChildren(); 37 | } 38 | } 39 | } 40 | } 41 | return count; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/osm/OsmBlockSource.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.osm; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * An osm.pbf input file that iterates through {@link Block Blocks} of raw bytes that can be decompressed/parsed in 9 | * worker threads using {@link Block#decodeElements()}. 10 | */ 11 | public interface OsmBlockSource extends Closeable { 12 | 13 | /** Calls {@code consumer} for each block from the input file sequentially in a single thread. */ 14 | void forEachBlock(Consumer consumer); 15 | 16 | @Override 17 | default void close() {} 18 | 19 | /** 20 | * An individual block of raw bytes from an osm.pbf file that can be decompressed/parsed with 21 | * {@link #decodeElements()}. 22 | */ 23 | interface Block extends Iterable { 24 | 25 | /** Create a fake block from existing elements - useful for tests. */ 26 | static Block of(Iterable items) { 27 | return () -> { 28 | @SuppressWarnings("unchecked") Iterable iterable = (Iterable) items; 29 | return iterable; 30 | }; 31 | } 32 | 33 | /** Decompress and parse OSM elements from this block. */ 34 | Iterable decodeElements(); 35 | 36 | @Override 37 | default Iterator iterator() { 38 | return decodeElements().iterator(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/osm/OsmHeader.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.osm; 2 | 3 | import java.time.Instant; 4 | import java.util.List; 5 | import org.locationtech.jts.geom.Envelope; 6 | 7 | /** Data parsed from the header block of an OSM input file. */ 8 | public record OsmHeader( 9 | Envelope bounds, 10 | List requiredFeatures, 11 | List optionalFeaturesList, 12 | String writingprogram, 13 | String source, 14 | Instant instant, 15 | long osmosisReplicationSequenceNumber, 16 | String osmosisReplicationBaseUrl 17 | ) {} 18 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/osm/OsmRelationInfo.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.osm; 2 | 3 | import com.onthegomap.planetiler.util.MemoryEstimator; 4 | 5 | /** 6 | * A user-defined class containing information about a relation that will be relevant during subsequent way processing. 7 | * This is stored in-memory by {@link OsmReader} so keep it as compact as possible. 8 | */ 9 | public interface OsmRelationInfo extends MemoryEstimator.HasEstimate { 10 | 11 | /** Returns the OSM ID of this relation. */ 12 | long id(); 13 | 14 | @Override 15 | default long estimateMemoryUsageBytes() { 16 | return 0; 17 | } 18 | 19 | default long vectorTileFeatureId(int multiplier) { 20 | return OsmElement.vectorTileFeatureId(multiplier, id(), OsmElement.Type.RELATION); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/osm/OsmSourceFeature.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.osm; 2 | 3 | public interface OsmSourceFeature { 4 | OsmElement originalElement(); 5 | } 6 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/parquet/GeoArrow.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.parquet; 2 | 3 | import com.onthegomap.planetiler.geo.GeoUtils; 4 | import java.util.List; 5 | import java.util.function.Function; 6 | import org.locationtech.jts.geom.CoordinateSequence; 7 | import org.locationtech.jts.geom.LineString; 8 | import org.locationtech.jts.geom.LinearRing; 9 | import org.locationtech.jts.geom.MultiLineString; 10 | import org.locationtech.jts.geom.MultiPoint; 11 | import org.locationtech.jts.geom.MultiPolygon; 12 | import org.locationtech.jts.geom.Point; 13 | import org.locationtech.jts.geom.Polygon; 14 | 15 | /** 16 | * Utilities for converting nested geoarrow 18 | * coordinate lists to JTS geometries. 19 | */ 20 | class GeoArrow { 21 | private GeoArrow() {} 22 | 23 | static MultiPolygon multipolygon(List> list) { 24 | return GeoUtils.createMultiPolygon(map(list, GeoArrow::polygon)); 25 | } 26 | 27 | static Polygon polygon(List input) { 28 | return GeoUtils.createPolygon(ring(input.getFirst()), input.stream().skip(1).map(GeoArrow::ring).toList()); 29 | } 30 | 31 | static MultiPoint multipoint(List input) { 32 | return GeoUtils.createMultiPoint(map(input, GeoArrow::point)); 33 | } 34 | 35 | static Point point(CoordinateSequence input) { 36 | return GeoUtils.JTS_FACTORY.createPoint(input); 37 | } 38 | 39 | static MultiLineString multilinestring(List input) { 40 | return GeoUtils.createMultiLineString(map(input, GeoArrow::linestring)); 41 | } 42 | 43 | static LineString linestring(CoordinateSequence input) { 44 | return GeoUtils.JTS_FACTORY.createLineString(input); 45 | } 46 | 47 | private static LinearRing ring(CoordinateSequence input) { 48 | return GeoUtils.JTS_FACTORY.createLinearRing(input); 49 | } 50 | 51 | private static List map(List in, Function remap) { 52 | return in.stream().map(remap).toList(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/reader/parquet/Interval.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.parquet; 2 | 3 | import java.time.Duration; 4 | import java.time.Period; 5 | import java.time.temporal.Temporal; 6 | import java.time.temporal.TemporalAmount; 7 | import java.time.temporal.TemporalUnit; 8 | import java.util.List; 9 | import java.util.stream.Stream; 10 | 11 | /** 12 | * Represents a parquet 13 | * interval datatype which has a month, day, and millisecond part. 14 | *

15 | * Built-in java {@link TemporalAmount} implementations can only store a period or duration amount, but not both. 16 | */ 17 | public record Interval(Period period, Duration duration) implements TemporalAmount { 18 | 19 | public static Interval of(int months, long days, long millis) { 20 | return new Interval(Period.ofMonths(months).plusDays(days), Duration.ofMillis(millis)); 21 | } 22 | 23 | @Override 24 | public long get(TemporalUnit unit) { 25 | return period.get(unit) + duration.get(unit); 26 | } 27 | 28 | @Override 29 | public List getUnits() { 30 | return Stream.concat(period.getUnits().stream(), duration.getUnits().stream()).toList(); 31 | } 32 | 33 | @Override 34 | public Temporal addTo(Temporal temporal) { 35 | return temporal.plus(period).plus(duration); 36 | } 37 | 38 | @Override 39 | public Temporal subtractFrom(Temporal temporal) { 40 | return temporal.minus(period).minus(duration); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/render/RenderedFeature.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.render; 2 | 3 | import com.onthegomap.planetiler.VectorTile; 4 | import com.onthegomap.planetiler.geo.TileCoord; 5 | import java.util.Optional; 6 | 7 | /** 8 | * An encoded vector tile feature on a tile with an extra {@code sortKey} and {@code group} that define its placement in 9 | * the eventual output tile. 10 | * 11 | * @param tile the tile this feature will live in 12 | * @param vectorTileFeature the encoded vector tile feature 13 | * @param sortKey ordering of features in the output tile 14 | * @param group if present, a group ID and limit that is used to limit features in a certain area of tile 15 | */ 16 | public record RenderedFeature( 17 | TileCoord tile, 18 | VectorTile.Feature vectorTileFeature, 19 | int sortKey, 20 | Optional group 21 | ) { 22 | 23 | public RenderedFeature { 24 | assert vectorTileFeature != null; 25 | } 26 | 27 | /** 28 | * Information used to limit features or assign a "rank" for features in a certain area of the tile 29 | * 30 | * @param group ID of the group that features live in 31 | * @param limit maximum rank within {@code group} in a tile that this feature should be included at, for example if 32 | * this is the 4th feature in a group with lowest sort-key then the feature is included if {@code limit 33 | * <= 4} 34 | */ 35 | public record Group(long group, int limit) {} 36 | } 37 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/stats/DefaultStats.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | /** 4 | * Holder for default {@link Stats} implementation to use for this process. 5 | */ 6 | public class DefaultStats { 7 | private DefaultStats() {} 8 | 9 | private static Stats defaultValue = null; 10 | 11 | public static Stats get() { 12 | return defaultValue; 13 | } 14 | 15 | public static void set(Stats stats) { 16 | defaultValue = stats; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/stats/ProcessTime.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import com.onthegomap.planetiler.util.Format; 4 | import java.time.Duration; 5 | import java.util.Locale; 6 | import java.util.Optional; 7 | import net.jcip.annotations.Immutable; 8 | 9 | /** 10 | * A utility for measuring the wall and CPU time that this JVM consumes between snapshots. 11 | *

12 | * For example: 13 | * {@snippet : 14 | * var start = ProcessTime.now(); 15 | * // do expensive work... 16 | * var end = ProcessTime.now(); 17 | * LOGGER.log("Expensive work took " + end.minus(start)); 18 | * } 19 | */ 20 | @Immutable 21 | public record ProcessTime(Duration wall, Optional cpu, Duration gc) { 22 | 23 | /** Takes a snapshot of current wall and CPU time of this JVM. */ 24 | public static ProcessTime now() { 25 | return new ProcessTime(Duration.ofNanos(System.nanoTime()), ProcessInfo.getProcessCpuTime(), 26 | ProcessInfo.getGcTime()); 27 | } 28 | 29 | /** Returns the amount of time elapsed between {@code other} and {@code this}. */ 30 | ProcessTime minus(ProcessTime other) { 31 | return new ProcessTime( 32 | wall.minus(other.wall), 33 | cpu.flatMap(thisCpu -> other.cpu.map(thisCpu::minus)), 34 | gc.minus(other.gc) 35 | ); 36 | } 37 | 38 | public String toString(Locale locale) { 39 | Format format = Format.forLocale(locale); 40 | Optional deltaCpu = cpu.map(format::duration); 41 | String avgCpus = cpu.map(cpuTime -> " avg:" + format.decimal(cpuTime.toNanos() * 1d / wall.toNanos())) 42 | .orElse(""); 43 | String gcString = gc.compareTo(Duration.ofSeconds(1)) > 0 ? (" gc:" + format.duration(gc)) : ""; 44 | return format.duration(wall) + " cpu:" + deltaCpu.orElse("-") + gcString + avgCpus; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return toString(Format.DEFAULT_LOCALE); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/stats/Timer.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import net.jcip.annotations.ThreadSafe; 4 | 5 | /** 6 | * Measures the amount of wall and CPU time that a task takes. 7 | */ 8 | @ThreadSafe 9 | public class Timer { 10 | 11 | private final ProcessTime start; 12 | private volatile ProcessTime end; 13 | 14 | private Timer() { 15 | start = ProcessTime.now(); 16 | } 17 | 18 | public static Timer start() { 19 | return new Timer(); 20 | } 21 | 22 | /** 23 | * Sets the end time to now, and makes {@link #running()} return false. Calling multiple times will extend the end 24 | * time. 25 | */ 26 | public Timer stop() { 27 | synchronized (this) { 28 | end = ProcessTime.now(); 29 | } 30 | return this; 31 | } 32 | 33 | /** Returns {@code false} if {@link #stop()} has been called. */ 34 | public boolean running() { 35 | synchronized (this) { 36 | return end == null; 37 | } 38 | } 39 | 40 | /** Returns the time from start to now if the task is still running, or start to end if it has finished. */ 41 | public ProcessTime elapsed() { 42 | synchronized (this) { 43 | return (end == null ? ProcessTime.now() : end).minus(start); 44 | } 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return elapsed().toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/stream/StreamArchiveConfig.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stream; 2 | 3 | import com.onthegomap.planetiler.config.Arguments; 4 | import com.onthegomap.planetiler.config.PlanetilerConfig; 5 | 6 | public record StreamArchiveConfig(boolean appendToFile, Arguments moreOptions) { 7 | public StreamArchiveConfig(PlanetilerConfig planetilerConfig, Arguments moreOptions) { 8 | this(planetilerConfig.append(), moreOptions); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/stream/StreamArchiveUtils.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stream; 2 | 3 | import com.google.common.net.UrlEscapers; 4 | import com.onthegomap.planetiler.archive.TileArchiveConfig; 5 | import com.onthegomap.planetiler.config.Arguments; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.util.List; 9 | import java.util.regex.Pattern; 10 | import java.util.stream.Collectors; 11 | import org.apache.commons.text.StringEscapeUtils; 12 | 13 | public final class StreamArchiveUtils { 14 | 15 | private static final Pattern quotedPattern = Pattern.compile("^'(.+?)'$"); 16 | 17 | private StreamArchiveUtils() {} 18 | 19 | public static Path constructIndexedPath(Path basePath, int index) { 20 | return index == 0 ? basePath : Paths.get(basePath.toString() + index); 21 | } 22 | 23 | static String getEscapedString(Arguments options, TileArchiveConfig.Format format, String key, 24 | String descriptionPrefix, String defaultValue, List examples) { 25 | 26 | final String cliKey = format.id() + "_" + key; 27 | 28 | final String fullDescription = descriptionPrefix + 29 | " - pass it as option: " + 30 | examples.stream().map(e -> "%s=%s".formatted(cliKey, escapeJava(e))).collect(Collectors.joining(" | ")) + 31 | ", or append to the file: " + 32 | examples.stream().map(e -> "?%s=%s".formatted(key, escapeJavaUri(e))).collect(Collectors.joining(" | ")); 33 | 34 | final String rawOptionValue = options.getString(key, fullDescription, defaultValue); 35 | return quotedPattern.matcher(rawOptionValue) 36 | // allow values to be wrapped by single quotes => allows to pass a space which otherwise gets trimmed 37 | .replaceAll("$1") 38 | // \n -> newline... 39 | .translateEscapes(); 40 | } 41 | 42 | private static String escapeJava(String s) { 43 | if (!s.trim().equals(s)) { 44 | s = "'" + s + "'"; 45 | } 46 | return StringEscapeUtils.escapeJava(s); 47 | } 48 | 49 | private static String escapeJavaUri(String s) { 50 | return UrlEscapers.urlFormParameterEscaper().escape(escapeJava(s)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/AnsiColors.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | 5 | /** Utilities for styling terminal output. */ 6 | public class AnsiColors { 7 | // Support NO_COLOR env var (https://no-color.org/) 8 | private static final AtomicBoolean useColors = 9 | new AtomicBoolean(System.getenv("NO_COLOR") == null || "\0".equals(System.getenv("NO_COLOR"))); 10 | 11 | public static void setUseColors(boolean colors) { 12 | useColors.set(colors); 13 | } 14 | 15 | private AnsiColors() {} 16 | 17 | private static final String COLOR_RESET = "\u001B[0m"; 18 | private static final String FG_RED = "\u001B[31m"; 19 | private static final String FG_GREEN = "\u001B[32m"; 20 | private static final String FG_YELLOW = "\u001B[33m"; 21 | private static final String FG_BLUE = "\u001B[34m"; 22 | private static final String REVERSE = "\u001B[7m"; 23 | private static final String BOLD = "\u001B[1m"; 24 | 25 | private static String color(String fg, String string) { 26 | return useColors.get() ? (fg + string + COLOR_RESET) : string; 27 | } 28 | 29 | public static String red(String string) { 30 | return color(FG_RED, string); 31 | } 32 | 33 | public static String green(String string) { 34 | return color(FG_GREEN, string); 35 | } 36 | 37 | public static String yellow(String string) { 38 | return color(FG_YELLOW, string); 39 | } 40 | 41 | public static String blue(String string) { 42 | return color(FG_BLUE, string); 43 | } 44 | 45 | public static String redBackground(String string) { 46 | return color(REVERSE + BOLD + FG_RED, string); 47 | } 48 | 49 | public static String greenBackground(String string) { 50 | return color(REVERSE + BOLD + FG_GREEN, string); 51 | } 52 | 53 | public static String redBold(String string) { 54 | return color(BOLD + FG_RED, string); 55 | } 56 | 57 | public static String greenBold(String string) { 58 | return color(BOLD + FG_GREEN, string); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/BinPack.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | import java.util.function.ToLongFunction; 8 | 9 | /** 10 | * Implements a best-effort 1-D bin packing using the 11 | * best-fit decreasing 12 | * algorithm. 13 | */ 14 | public class BinPack { 15 | private BinPack() {} 16 | 17 | /** 18 | * Returns {@code items} grouped into an approximately minimum number of bins under {@code maxBinSize} according to 19 | * {@code getSize} function. 20 | */ 21 | public static List> pack(Collection items, long maxBinSize, ToLongFunction getSize) { 22 | class Bin { 23 | long size = 0; 24 | final List items = new ArrayList<>(); 25 | } 26 | var descendingItems = items.stream().sorted(Comparator.comparingLong(getSize).reversed()).toList(); 27 | List bins = new ArrayList<>(); 28 | for (var item : descendingItems) { 29 | long size = getSize.applyAsLong(item); 30 | var bestBin = bins.stream() 31 | .filter(b -> maxBinSize - b.size >= size) 32 | // Instead of using the first bin that this element fits in, use the "fullest" bin. 33 | // This makes the algorithm "best-fit decreasing" instead of "first-fit decreasing" 34 | .max(Comparator.comparingLong(bin -> bin.size)); 35 | Bin bin; 36 | if (bestBin.isPresent()) { 37 | bin = bestBin.get(); 38 | } else { 39 | bins.add(bin = new Bin()); 40 | } 41 | bin.items.add(item); 42 | bin.size += size; 43 | } 44 | return bins.stream().map(bin -> bin.items).toList(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/BuildInfo.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import com.onthegomap.planetiler.Planetiler; 4 | import java.io.IOException; 5 | import java.time.Instant; 6 | import java.util.Properties; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | 11 | /** Accessor for the build info inserted by maven into the {@code buildinfo.properties} file. */ 12 | public record BuildInfo(String githash, String version, Long buildTime) { 13 | 14 | private static final Logger LOGGER = LoggerFactory.getLogger(BuildInfo.class); 15 | 16 | private static final BuildInfo instance; 17 | static { 18 | BuildInfo result = null; 19 | try (var properties = Planetiler.class.getResourceAsStream("/buildinfo.properties")) { 20 | var parsed = new Properties(); 21 | parsed.load(properties); 22 | String githash = parsed.getProperty("githash"); 23 | String version = parsed.getProperty("version"); 24 | String epochMs = parsed.getProperty("timestamp"); 25 | Long buildTime = null; 26 | 27 | if (epochMs != null && !epochMs.isBlank() && epochMs.matches("^\\d+$")) { 28 | buildTime = Long.parseLong(epochMs); 29 | } 30 | result = new BuildInfo(githash, version, buildTime); 31 | } catch (IOException e) { 32 | LOGGER.error("Error getting build properties"); 33 | } 34 | instance = result; 35 | } 36 | 37 | public String buildTimeString() { 38 | return buildTime == null ? null : Instant.ofEpochMilli(buildTime).toString(); 39 | } 40 | 41 | /** Returns info inserted by maven at build-time into the {@code buildinfo.properties} file. */ 42 | public static BuildInfo get() { 43 | return instance; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/CacheByZoom.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import com.onthegomap.planetiler.config.PlanetilerConfig; 4 | import java.util.function.IntFunction; 5 | 6 | /** 7 | * Caches a value that changes by integer zoom level to avoid recomputing it. 8 | * 9 | * @param return type of the function 10 | */ 11 | public class CacheByZoom { 12 | 13 | private final int minzoom; 14 | private final Object[] values; 15 | private final IntFunction supplier; 16 | 17 | private CacheByZoom(int minzoom, int maxzoom, IntFunction supplier) { 18 | this.minzoom = minzoom; 19 | values = new Object[maxzoom + 1 - minzoom]; 20 | this.supplier = supplier; 21 | } 22 | 23 | /** 24 | * Returns a cache for {@code supplier} that can handle a min/max zoom range specified in {@code config}. 25 | * 26 | * @param supplier function that will be called with each zoom-level to get the value 27 | * @param return type of the function 28 | * @return a cache for {@code supplier} by zom 29 | */ 30 | public static CacheByZoom create(IntFunction supplier) { 31 | return new CacheByZoom<>(0, PlanetilerConfig.MAX_MAXZOOM, supplier); 32 | } 33 | 34 | public T get(int zoom) { 35 | @SuppressWarnings("unchecked") T[] casted = (T[]) values; 36 | int off = zoom - minzoom; 37 | if (values[off] != null) { 38 | return casted[off]; 39 | } 40 | return casted[off] = supplier.apply(zoom); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/CloseShieldOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * {@link OutputStream} decorator that suppresses {@link #close()}. 8 | */ 9 | public class CloseShieldOutputStream extends DelegatingOutputStream { 10 | 11 | public CloseShieldOutputStream(OutputStream wrapped) { 12 | super(wrapped); 13 | } 14 | 15 | @Override 16 | public void close() throws IOException { 17 | // suppress closing 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/CloseableConsumer.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.util.function.Consumer; 6 | 7 | @FunctionalInterface 8 | public interface CloseableConsumer extends Consumer, Closeable { 9 | 10 | @Override 11 | default void close() throws IOException {} 12 | } 13 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/CloseableIterator.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | import java.util.Spliterators; 6 | import java.util.function.Function; 7 | import java.util.stream.Stream; 8 | import java.util.stream.StreamSupport; 9 | 10 | public interface CloseableIterator extends Closeable, Iterator { 11 | 12 | static CloseableIterator of(Stream stream) { 13 | return new CloseableIterator<>() { 14 | private final Iterator iter = stream.iterator(); 15 | 16 | @Override 17 | public boolean hasNext() { 18 | return iter.hasNext(); 19 | } 20 | 21 | @Override 22 | public T next() { 23 | return iter.next(); 24 | } 25 | 26 | @Override 27 | public void close() { 28 | stream.close(); 29 | } 30 | }; 31 | } 32 | 33 | @Override 34 | void close(); 35 | 36 | default Stream stream() { 37 | return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false).onClose(this::close); 38 | } 39 | 40 | default CloseableIterator map(Function mapper) { 41 | var parent = this; 42 | return new CloseableIterator<>() { 43 | @Override 44 | public void close() { 45 | parent.close(); 46 | } 47 | 48 | @Override 49 | public boolean hasNext() { 50 | return parent.hasNext(); 51 | } 52 | 53 | @Override 54 | public O next() { 55 | return mapper.apply(parent.next()); 56 | } 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/CountingOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.util.function.LongConsumer; 6 | 7 | /** 8 | * {@link OutputStream} decorator that notifies the callback about the written bytes. 9 | */ 10 | public class CountingOutputStream extends DelegatingOutputStream { 11 | 12 | private final LongConsumer writtenBytesConsumer; 13 | 14 | public CountingOutputStream(OutputStream wrapped, LongConsumer writtenBytesConsumer) { 15 | super(wrapped); 16 | this.writtenBytesConsumer = writtenBytesConsumer; 17 | } 18 | 19 | @Override 20 | public void write(int i) throws IOException { 21 | super.write(i); 22 | writtenBytesConsumer.accept(1L); 23 | } 24 | 25 | @Override 26 | public void write(byte[] b) throws IOException { 27 | super.write(b); 28 | writtenBytesConsumer.accept(b.length); 29 | } 30 | 31 | @Override 32 | public void write(byte[] b, int off, int len) throws IOException { 33 | super.write(b, off, len); 34 | writtenBytesConsumer.accept(len); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/DelegatingOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | abstract class DelegatingOutputStream extends OutputStream { 7 | 8 | private final OutputStream delegate; 9 | 10 | protected DelegatingOutputStream(OutputStream wrapped) { 11 | this.delegate = wrapped; 12 | } 13 | 14 | @Override 15 | public void write(int i) throws IOException { 16 | delegate.write(i); 17 | } 18 | 19 | @Override 20 | public void write(byte[] b) throws IOException { 21 | delegate.write(b); 22 | } 23 | 24 | @Override 25 | public void write(byte[] b, int off, int len) throws IOException { 26 | delegate.write(b, off, len); 27 | } 28 | 29 | @Override 30 | public void flush() throws IOException { 31 | delegate.flush(); 32 | } 33 | 34 | @Override 35 | public void close() throws IOException { 36 | delegate.close(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/DiskBacked.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | /** 4 | * A resource backed by a file or directory on disk. 5 | */ 6 | public interface DiskBacked { 7 | 8 | /** Returns the current size of that file or directory in bytes. */ 9 | long diskUsageBytes(); 10 | } 11 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/DuplicateClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.IOException; 4 | import java.io.UncheckedIOException; 5 | import java.util.Objects; 6 | import java.util.function.Predicate; 7 | 8 | /** 9 | * A class loader that loads certain classes even if they are already loaded by a parent classloader. 10 | * 11 | * This makes it possible to get multiple copies of the same class, so for example you could invoke a method 12 | * synchronized on a static variable from different classes without contention. 13 | */ 14 | public class DuplicateClassLoader extends ClassLoader { 15 | 16 | private final Predicate shouldDuplicate; 17 | 18 | private DuplicateClassLoader(Predicate shouldDuplicate) { 19 | this.shouldDuplicate = shouldDuplicate; 20 | } 21 | 22 | /** 23 | * Returns a {@link ClassLoader} that loads every class with a name starting with {@code prefix} even if the parent 24 | * has already loaded it. 25 | */ 26 | public static DuplicateClassLoader duplicateClassesWithPrefix(String prefix) { 27 | return new DuplicateClassLoader(name -> name.startsWith(prefix)); 28 | } 29 | 30 | @Override 31 | public Class loadClass(String name) throws ClassNotFoundException { 32 | if (shouldDuplicate.test(name)) { 33 | Class c = findLoadedClass(name); 34 | if (c == null) { 35 | byte[] b = loadClassFromFile(name); 36 | return defineClass(name, b, 0, b.length); 37 | } 38 | } 39 | return super.loadClass(name); 40 | } 41 | 42 | private byte[] loadClassFromFile(String fileName) { 43 | String classFileName = fileName.replace('.', '/') + ".class"; 44 | try (var inputStream = getClass().getClassLoader().getResourceAsStream(classFileName)) { 45 | return Objects.requireNonNull(inputStream, "Could not load " + fileName + " (" + classFileName + ")") 46 | .readAllBytes(); 47 | } catch (IOException e) { 48 | throw new UncheckedIOException(e); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/Exceptions.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.IOException; 4 | import java.io.UncheckedIOException; 5 | 6 | /** 7 | * Exception-handling utilities. 8 | */ 9 | public class Exceptions { 10 | private Exceptions() {} 11 | 12 | /** 13 | * Re-throw a caught exception, handling interrupts and wrapping in a {@link FatalPlanetilerException} if checked. 14 | * 15 | * @param exception The original exception 16 | * @param Return type if caller requires it 17 | */ 18 | public static T throwFatalException(Throwable exception) { 19 | if (exception instanceof InterruptedException) { 20 | Thread.currentThread().interrupt(); 21 | } 22 | if (exception instanceof RuntimeException runtimeException) { 23 | throw runtimeException; 24 | } else if (exception instanceof IOException ioe) { 25 | throw new UncheckedIOException(ioe); 26 | } else if (exception instanceof Error error) { 27 | throw error; 28 | } 29 | throw new FatalPlanetilerException(exception); 30 | } 31 | 32 | /** 33 | * Fatal exception that will result in planetiler exiting early and shutting down. 34 | */ 35 | public static class FatalPlanetilerException extends RuntimeException { 36 | public FatalPlanetilerException(Throwable exception) { 37 | super(exception); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/FastGzipOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.util.zip.Deflater; 6 | import java.util.zip.GZIPOutputStream; 7 | 8 | /** 9 | * A version of {@link GZIPOutputStream} that uses {@link Deflater#BEST_SPEED} (level 1) instead of 10 | * {@link Deflater#DEFAULT_COMPRESSION} (-1). 11 | */ 12 | public class FastGzipOutputStream extends GZIPOutputStream { 13 | 14 | public FastGzipOutputStream(OutputStream out) throws IOException { 15 | super(out); 16 | def.setLevel(Deflater.BEST_SPEED); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/FunctionThatThrows.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static com.onthegomap.planetiler.util.Exceptions.throwFatalException; 4 | 5 | @FunctionalInterface 6 | public interface FunctionThatThrows { 7 | 8 | @SuppressWarnings("java:S112") 9 | O apply(I value) throws Exception; 10 | 11 | default O runAndWrapException(I value) { 12 | try { 13 | return apply(value); 14 | } catch (Exception e) { 15 | return throwFatalException(e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/Glob.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.regex.Pattern; 7 | 8 | 9 | /** 10 | * Utility for constructing base+glob paths for matching many files 11 | */ 12 | public record Glob(Path base, String pattern) { 13 | 14 | private static final Pattern GLOB_PATTERN = Pattern.compile("[?*{\\[].*$"); 15 | 16 | /** Wrap a base path with no globs in it yet. */ 17 | public static Glob of(Path path) { 18 | return new Glob(path, null); 19 | } 20 | 21 | /** Resolves a subdirectory using parts separated by the platform file separator. */ 22 | public Glob resolve(String... subPath) { 23 | String separator = "/"; 24 | if (pattern != null) { 25 | return new Glob(base, pattern + separator + String.join(separator, subPath)); 26 | } else if (subPath == null || subPath.length == 0) { 27 | return this; 28 | } else if (GLOB_PATTERN.matcher(subPath[0]).find()) { 29 | return new Glob(base, String.join(separator, subPath)); 30 | } else { 31 | return of(base.resolve(subPath[0])).resolve(Arrays.copyOfRange(subPath, 1, subPath.length)); 32 | } 33 | } 34 | 35 | /** Parse a string containing platform-specific file separators into a base+glob pattern. */ 36 | public static Glob parse(String path) { 37 | var matcher = GLOB_PATTERN.matcher(path); 38 | if (!matcher.find()) { 39 | return of(Path.of(path)); 40 | } 41 | matcher.reset(); 42 | String base = matcher.replaceAll(""); 43 | String separator = Path.of(base).getFileSystem().getSeparator(); 44 | int idx = base.lastIndexOf(separator); 45 | if (idx > 0) { 46 | base = base.substring(0, idx); 47 | } 48 | return of(Path.of(base)).resolve(path.substring(idx + 1).split(Pattern.quote(separator))); 49 | } 50 | 51 | /** Search the filesystem for all files beneath {@link #base()} matching {@link #pattern()}. */ 52 | public List find() { 53 | return pattern == null ? List.of(base) : FileUtils.walkPathWithPattern(base, pattern); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/Gzip.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.util.zip.GZIPInputStream; 7 | import java.util.zip.GZIPOutputStream; 8 | 9 | public class Gzip { 10 | 11 | public static byte[] gzip(byte[] in) throws IOException { 12 | var bos = new ByteArrayOutputStream(in.length); 13 | try (var gzipOS = new GZIPOutputStream(bos)) { 14 | gzipOS.write(in); 15 | } 16 | return bos.toByteArray(); 17 | } 18 | 19 | public static byte[] gunzip(byte[] zipped) throws IOException { 20 | try (var is = new GZIPInputStream(new ByteArrayInputStream(zipped))) { 21 | return is.readAllBytes(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/LogUtil.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.util.regex.Pattern; 4 | import org.slf4j.MDC; 5 | 6 | /** 7 | * Wrapper for SLF4j {@link MDC} log utility to prepend {@code [stage]} to log output. 8 | */ 9 | public class LogUtil { 10 | 11 | private LogUtil() {} 12 | 13 | private static final String STAGE_KEY = "stage"; 14 | 15 | /** Prepends {@code [stage]} to all subsequent logs from this thread. */ 16 | public static void setStage(String stage) { 17 | MDC.put(STAGE_KEY, "[%s] ".formatted(stage)); 18 | } 19 | 20 | /** Removes {@code [stage]} from subsequent logs from this thread. */ 21 | public static void clearStage() { 22 | MDC.remove(STAGE_KEY); 23 | } 24 | 25 | /** Returns the current {@code [stage]} value prepended to log for this thread. */ 26 | public static String getStage() { 27 | // strip out the "[stage] " wrapper 28 | return MDC.get(STAGE_KEY) instanceof String s ? s.substring(1, s.length() - 2) : null; 29 | } 30 | 31 | /** Prepends {@code [parent:child]} to all subsequent logs from this thread. */ 32 | public static void setStage(String parent, String child) { 33 | if (parent == null) { 34 | setStage(child); 35 | } else { 36 | setStage(parent + ":" + child.replaceFirst("^" + Pattern.quote(parent) + "_?", "")); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/MapUtil.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class MapUtil { 7 | private MapUtil() {} 8 | 9 | /** 10 | * Returns a new map with the union of entries from {@code a} and {@code b} where conflicts take the value from 11 | * {@code b}. 12 | */ 13 | public static Map merge(Map a, Map b) { 14 | Map copy = new HashMap<>(a); 15 | copy.putAll(b); 16 | return copy; 17 | } 18 | 19 | /** 20 | * Returns a new map the entries of {@code a} added to {@code (k, v)}. 21 | */ 22 | public static Map with(Map a, K k, V v) { 23 | Map copy = new HashMap<>(a); 24 | if (v == null || "".equals(v)) { 25 | copy.remove(k); 26 | } else { 27 | copy.put(k, v); 28 | } 29 | return copy; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/Memoized.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * Caches the value of a function, so it only gets called once for each unique input, including when it throws an 8 | * exception. 9 | */ 10 | public class Memoized implements Function { 11 | private final ConcurrentHashMap> cache = new ConcurrentHashMap<>(); 12 | private final Function> supplier; 13 | 14 | private Memoized(FunctionThatThrows supplier) { 15 | this.supplier = i -> Try.apply(() -> supplier.apply(i)); 16 | } 17 | 18 | /** Returns a memoized version of {@code supplier} that gets called only once for each input. */ 19 | public static Memoized memoize(FunctionThatThrows supplier) { 20 | return new Memoized<>(supplier); 21 | } 22 | 23 | 24 | @Override 25 | public O apply(I i) { 26 | Try result = cache.get(i); 27 | if (result == null) { 28 | result = cache.computeIfAbsent(i, supplier); 29 | } 30 | return result.get(); 31 | } 32 | 33 | /** Returns a success or failure wrapper for the function call. */ 34 | public Try tryApply(I i) { 35 | Try result = cache.get(i); 36 | if (result == null) { 37 | result = cache.computeIfAbsent(i, supplier); 38 | } 39 | return result; 40 | } 41 | 42 | /** Returns a success or failure wrapper for the function call, and casting the result to {@code clazz}. */ 43 | public Try tryApply(I i, Class clazz) { 44 | return tryApply(i).cast(clazz); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/SlidingWindow.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import com.google.common.util.concurrent.Monitor; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | 6 | /** 7 | * Lets multiple threads work on a sliding window of values. 8 | * 9 | * Calls to {@link #waitUntilInsideWindow(long)} will block until {@link #advanceTail(long)} is within a given range 10 | * from the new value being requested. 11 | */ 12 | public class SlidingWindow { 13 | 14 | private final AtomicLong tail = new AtomicLong(0); 15 | private final Monitor monitor = new Monitor(); 16 | private final long windowSize; 17 | 18 | public SlidingWindow(long windowSize) { 19 | this.windowSize = windowSize; 20 | } 21 | 22 | /** 23 | * Moves the current value for the tail to {@code to}, unblocking any thread waiting on moving the head to 24 | * {@code to + windowSize}. 25 | */ 26 | public void advanceTail(long to) { 27 | monitor.enter(); 28 | try { 29 | if (to < tail.get()) { 30 | throw new IllegalStateException("Tried to move sliding window tail backwards from " + tail + " to " + to); 31 | } 32 | tail.set(to); 33 | } finally { 34 | monitor.leave(); 35 | } 36 | } 37 | 38 | /** Blocks until another thread moves the tail to at least {@code to - windowSize}. */ 39 | public void waitUntilInsideWindow(long to) { 40 | try { 41 | monitor.enterWhen(monitor.newGuard(() -> to - tail.longValue() < windowSize)); 42 | monitor.leave(); 43 | } catch (InterruptedException e) { 44 | Thread.currentThread().interrupt(); 45 | throw new RuntimeException(e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/ToDoubleFunctionThatThrows.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static com.onthegomap.planetiler.util.Exceptions.throwFatalException; 4 | 5 | @FunctionalInterface 6 | public interface ToDoubleFunctionThatThrows { 7 | 8 | @SuppressWarnings("java:S112") 9 | double applyAsDouble(I value) throws Exception; 10 | 11 | default double applyAndWrapException(I value) { 12 | try { 13 | return applyAsDouble(value); 14 | } catch (Exception e) { 15 | return throwFatalException(e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/util/log4j/ElapsedTimeLookupPlugin.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util.log4j; 2 | 3 | import java.time.Duration; 4 | import org.apache.logging.log4j.core.LogEvent; 5 | import org.apache.logging.log4j.core.config.plugins.Plugin; 6 | import org.apache.logging.log4j.core.lookup.StrLookup; 7 | 8 | /** 9 | * A log4j plugin that substitutes {@code $${uptime:now}} pattern with the elapsed time of the program in {@code 10 | * H:MM:SS} form. 11 | *

12 | * log4j properties file needs to include {@code packages=com.onthegomap.planetiler.util.log4j} to look in this package 13 | * for plugins. 14 | */ 15 | @Plugin(name = "uptime", category = StrLookup.CATEGORY) 16 | public class ElapsedTimeLookupPlugin implements StrLookup { 17 | 18 | // rough approximation for start time: when log4j first loads this plugin 19 | private static final long startTime = System.nanoTime(); 20 | 21 | @Override 22 | public String lookup(String key) { 23 | Duration duration = Duration.ofNanos(System.nanoTime() - startTime); 24 | 25 | return "%d:%02d:%02d".formatted( 26 | duration.toHours(), 27 | duration.toMinutesPart(), 28 | duration.toSecondsPart() 29 | ); 30 | } 31 | 32 | @Override 33 | public String lookup(LogEvent event, String key) { 34 | return lookup(key); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/worker/IntConsumerThatThrows.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.worker; 2 | 3 | import static com.onthegomap.planetiler.util.Exceptions.throwFatalException; 4 | 5 | /** 6 | * A function that takes an integer can throw checked exceptions. 7 | */ 8 | @FunctionalInterface 9 | public interface IntConsumerThatThrows { 10 | 11 | @SuppressWarnings("java:S112") 12 | void accept(int value) throws Exception; 13 | 14 | default void runAndWrapException(int value) { 15 | try { 16 | accept(value); 17 | } catch (Exception e) { 18 | throwFatalException(e); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/com/onthegomap/planetiler/worker/RunnableThatThrows.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.worker; 2 | 3 | import static com.onthegomap.planetiler.util.Exceptions.throwFatalException; 4 | 5 | /** 6 | * A function that can throw checked exceptions. 7 | */ 8 | @FunctionalInterface 9 | public interface RunnableThatThrows { 10 | 11 | @SuppressWarnings("java:S112") 12 | void run() throws Exception; 13 | 14 | default void runAndWrapException() { 15 | try { 16 | run(); 17 | } catch (Exception e) { 18 | throwFatalException(e); 19 | } 20 | } 21 | 22 | static Runnable wrap(RunnableThatThrows thrower) { 23 | return thrower::runAndWrapException; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/org/apache/hadoop/io/compress/CompressionCodec.java: -------------------------------------------------------------------------------- 1 | package org.apache.hadoop.io.compress; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | /** Fix interface from parquet floor so we can extend it with {@link GzipCodec} and {@link Lz4Codec} */ 8 | public interface CompressionCodec { 9 | Decompressor createDecompressor(); 10 | 11 | Compressor createCompressor(); 12 | 13 | CompressionInputStream createInputStream(InputStream is, Decompressor d) throws IOException; 14 | 15 | CompressionOutputStream createOutputStream(OutputStream os, Compressor c) throws IOException; 16 | } 17 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/org/apache/hadoop/io/compress/DirectDecompressionCodec.java: -------------------------------------------------------------------------------- 1 | package org.apache.hadoop.io.compress; 2 | 3 | /** Add missing interface from compression libraries in hadoop common. */ 4 | public interface DirectDecompressionCodec extends CompressionCodec { 5 | DirectDecompressor createDirectDecompressor(); 6 | } 7 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/org/apache/hadoop/io/compress/DirectDecompressor.java: -------------------------------------------------------------------------------- 1 | package org.apache.hadoop.io.compress; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | /** Add missing interface from compression libraries in hadoop common. */ 7 | public interface DirectDecompressor { 8 | void decompress(ByteBuffer src, ByteBuffer dst) throws IOException; 9 | } 10 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/org/apache/hadoop/io/compress/GzipCodec.java: -------------------------------------------------------------------------------- 1 | package org.apache.hadoop.io.compress; 2 | 3 | import io.airlift.compress.gzip.JdkGzipCodec; 4 | 5 | /** 6 | * Make {@link JdkGzipCodec} available at the location expected by 7 | * {@link org.apache.parquet.hadoop.metadata.CompressionCodecName} to allow deserializing parquet files that use gzip 8 | * compression. 9 | */ 10 | public class GzipCodec extends JdkGzipCodec {} 11 | 12 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/org/apache/hadoop/io/compress/Lz4Codec.java: -------------------------------------------------------------------------------- 1 | package org.apache.hadoop.io.compress; 2 | 3 | /** 4 | * Make {@link io.airlift.compress.lz4.Lz4Codec} available at the location expected by 5 | * {@link org.apache.parquet.hadoop.metadata.CompressionCodecName} to allow deserializing parquet files that use lz4 6 | * compression. 7 | */ 8 | @SuppressWarnings("java:S2176") 9 | public class Lz4Codec extends io.airlift.compress.lz4.Lz4Codec {} 10 | -------------------------------------------------------------------------------- /planetiler-core/src/main/java/org/apache/parquet/filter2/predicate/Filters.java: -------------------------------------------------------------------------------- 1 | package org.apache.parquet.filter2.predicate; 2 | 3 | import java.util.List; 4 | import org.apache.parquet.hadoop.metadata.ColumnPath; 5 | 6 | /** 7 | * Create {@link Operators.DoubleColumn} and {@link Operators.FloatColumn} instances with dots in the column names since 8 | * their constructors are package-private. 9 | */ 10 | public class Filters { 11 | private Filters() {} 12 | 13 | public static Operators.DoubleColumn doubleColumn(List parts) { 14 | return new Operators.DoubleColumn(ColumnPath.get(parts.toArray(String[]::new))); 15 | } 16 | 17 | public static Operators.FloatColumn floatColumn(List parts) { 18 | return new Operators.FloatColumn(ColumnPath.get(parts.toArray(String[]::new))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /planetiler-core/src/main/proto/stream_archive_proto.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package com.onthegomap.planetiler.proto; 4 | 5 | message Entry { 6 | oneof entry { 7 | TileEntry tile = 1; 8 | InitializationEntry initialization = 2; 9 | FinishEntry finish = 3; 10 | } 11 | } 12 | 13 | message TileEntry { 14 | int32 x = 1; 15 | int32 y = 2; 16 | int32 z = 3; 17 | bytes encoded_data = 4; 18 | } 19 | 20 | message InitializationEntry { 21 | } 22 | 23 | message FinishEntry { 24 | Metadata metadata = 1; 25 | } 26 | 27 | message Metadata { 28 | 29 | string name = 1; 30 | string description = 2; 31 | string attribution = 3; 32 | string version = 4; 33 | string type = 5; 34 | string format = 6; 35 | Envelope bounds = 7; 36 | Coordinate center = 8; 37 | int32 min_zoom = 9; 38 | int32 max_zoom = 10; 39 | repeated VectorLayer vector_layers = 11; 40 | map others = 12; 41 | TileCompression tile_compression = 13; 42 | } 43 | 44 | message Envelope { 45 | double min_x = 1; 46 | double max_x = 2; 47 | double min_y = 3; 48 | double max_y = 4; 49 | } 50 | 51 | message Coordinate { 52 | double x = 1; 53 | double y = 2; 54 | double z = 3; 55 | } 56 | 57 | message VectorLayer { 58 | string id = 1; 59 | map fields = 2; 60 | string description = 3; 61 | int32 min_zoom = 4; 62 | int32 max_zoom = 5; 63 | 64 | enum FieldType { 65 | FIELD_TYPE_UNSPECIFIED = 0; 66 | FIELD_TYPE_NUMBER = 1; 67 | FIELD_TYPE_BOOLEAN = 3; 68 | FIELD_TYPE_STRING = 4; 69 | } 70 | } 71 | 72 | enum TileCompression { 73 | TILE_COMPRESSION_UNSPECIFIED = 0; 74 | TILE_COMPRESSION_GZIP = 1; 75 | TILE_COMPRESSION_NONE = 2; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /planetiler-core/src/main/resources/assemblies/with-deps.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | with-deps 7 | 8 | jar 9 | 10 | false 11 | 12 | 13 | 14 | metaInf-services 15 | 16 | 17 | 18 | 19 | ${project.parent.basedir} 20 | / 21 | 22 | README* 23 | LICENSE* 24 | NOTICE* 25 | 26 | 27 | 28 | 29 | 30 | / 31 | true 32 | true 33 | runtime 34 | 35 | 36 | 37 | **/Log4j2Plugins.dat 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /planetiler-core/src/main/resources/buildinfo.properties: -------------------------------------------------------------------------------- 1 | githash=${buildNumber} 2 | timestamp=${timestamp} 3 | version=${revision} 4 | -------------------------------------------------------------------------------- /planetiler-core/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appenders=console 2 | appender.console.type=Console 3 | appender.console.name=STDOUT 4 | appender.console.layout.type=PatternLayout 5 | appender.console.layout.pattern=%highlight{$${uptime:now} %level{length=3} %X{stage}- %msg%n%throwable}{FATAL=red, ERROR=red, WARN=YELLOW, INFO=normal, DEBUG=normal, TRACE=normal} 6 | packages=com.onthegomap.planetiler.util.log4j 7 | rootLogger.level=debug 8 | rootLogger.appenderRefs=stdout 9 | rootLogger.appenderRef.stdout.ref=STDOUT 10 | 11 | logger.apache.name=org.apache 12 | logger.apache.level=warn 13 | 14 | # suppress warning about unreadable duckdb statistics 15 | logger.apachecorrupt.name=org.apache.parquet.CorruptStatistics 16 | logger.apachecorrupt.level=error 17 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/ExpectedError.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler; 2 | 3 | /** A fatal error intentionally thrown by a test. */ 4 | public class ExpectedError extends Error { 5 | public ExpectedError() { 6 | super("expected error", null, true, false); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/ExpectedException.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler; 2 | 3 | /** An exception intentionally thrown by a test. */ 4 | public class ExpectedException extends RuntimeException { 5 | public ExpectedException() { 6 | super("expected exception", null, true, false); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/Slow.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import org.junit.jupiter.api.Tag; 8 | 9 | /** Add to any junit test classes or methods to exclude when run with {@code -Pfast} maven argument. */ 10 | @Target({ElementType.TYPE, ElementType.METHOD}) 11 | @Tag("slow") 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Slow { 14 | } 15 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/expression/ExpressionTestUtil.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.expression; 2 | 3 | import static com.onthegomap.planetiler.TestUtils.newPoint; 4 | 5 | import com.onthegomap.planetiler.reader.SimpleFeature; 6 | import com.onthegomap.planetiler.reader.SourceFeature; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class ExpressionTestUtil { 11 | static SourceFeature featureWithTags(String... tags) { 12 | Map map = new HashMap<>(); 13 | for (int i = 0; i < tags.length; i += 2) { 14 | map.put(tags[i], tags[i + 1]); 15 | } 16 | return SimpleFeature.create(newPoint(0, 0), map); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/geo/MutableCoordinateSequenceTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.geo; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import com.carrotsearch.hppc.DoubleArrayList; 6 | import org.junit.jupiter.api.Test; 7 | import org.locationtech.jts.geom.impl.PackedCoordinateSequence; 8 | 9 | class MutableCoordinateSequenceTest { 10 | 11 | private static void assertContents(MutableCoordinateSequence seq, double... expected) { 12 | double[] actual = new double[seq.size() * 2]; 13 | for (int i = 0; i < seq.size(); i++) { 14 | actual[i * 2] = seq.getX(i); 15 | actual[i * 2 + 1] = seq.getY(i); 16 | } 17 | assertEquals(DoubleArrayList.from(expected), DoubleArrayList.from(actual), "getX/getY"); 18 | PackedCoordinateSequence copy = seq.copy(); 19 | for (int i = 0; i < seq.size(); i++) { 20 | actual[i * 2] = copy.getX(i); 21 | actual[i * 2 + 1] = copy.getY(i); 22 | } 23 | assertEquals(DoubleArrayList.from(expected), DoubleArrayList.from(actual), "copied getX/getY"); 24 | } 25 | 26 | @Test 27 | void testEmpty() { 28 | var seq = new MutableCoordinateSequence(); 29 | assertEquals(0, seq.copy().size()); 30 | } 31 | 32 | @Test 33 | void testSingle() { 34 | var seq = new MutableCoordinateSequence(); 35 | seq.addPoint(1, 2); 36 | assertContents(seq, 1, 2); 37 | } 38 | 39 | @Test 40 | void testTwoPoints() { 41 | var seq = new MutableCoordinateSequence(); 42 | seq.addPoint(1, 2); 43 | seq.addPoint(3, 4); 44 | assertContents(seq, 1, 2, 3, 4); 45 | } 46 | 47 | @Test 48 | void testClose() { 49 | var seq = new MutableCoordinateSequence(); 50 | seq.addPoint(1, 2); 51 | seq.addPoint(3, 4); 52 | seq.addPoint(0, 1); 53 | seq.closeRing(); 54 | assertContents(seq, 1, 2, 3, 4, 0, 1, 1, 2); 55 | } 56 | 57 | @Test 58 | void testScaling() { 59 | var seq = MutableCoordinateSequence.newScalingSequence(1, 2, 3); 60 | seq.addPoint(1, 2); 61 | seq.addPoint(3, 4); 62 | seq.addPoint(0, 1); 63 | seq.closeRing(); 64 | assertContents(seq, 0, 0, 6, 6, -3, -3, 0, 0); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/geo/UnitTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.geo; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.params.ParameterizedTest; 6 | import org.junit.jupiter.params.provider.CsvSource; 7 | 8 | class UnitTest { 9 | @ParameterizedTest 10 | @CsvSource({ 11 | "METER, 1, m; meters; metre; metres", 12 | "KILOMETER, 0.001, km; kilometers", 13 | "FOOT, 3.28084, ft; feet; foot", 14 | "MILE, 0.000621371, mi; mile; miles", 15 | "NAUTICAL_MILE, 0.000539957, nm; nautical miles", 16 | "YARD, 1.0936136964129, yd; yards; yds", 17 | 18 | "Z0_PIXEL, 0.00390625, z0px; z0 pixels; z0 pixel", 19 | "Z0_TILE, 1, z0ti; z0tile; z0 tile; z0_tiles; z0_tiles; z0 tiles", 20 | }) 21 | void testLengthAndDerivedArea(String name, double expected, String aliases) { 22 | Unit.Length length = Unit.Length.from(name); 23 | Unit.Area area = Unit.Area.from("SQUARE_" + name); 24 | assertEquals(expected, length.fromBaseUnit(1), expected / 1e5); 25 | double expectedArea = expected * expected; 26 | assertEquals(expected * expected, area.fromBaseUnit(1), expectedArea / 1e5); 27 | 28 | for (String alias : aliases.split(";")) { 29 | assertEquals(length, Unit.Length.from(alias), alias); 30 | assertEquals(area, Unit.Area.from("s" + alias), "s" + alias); 31 | assertEquals(area, Unit.Area.from("sq " + alias), "sq " + alias); 32 | assertEquals(area, Unit.Area.from("square " + alias), "square " + alias); 33 | assertEquals(area, Unit.Area.from(alias + "2"), alias + "2"); 34 | } 35 | } 36 | 37 | @ParameterizedTest 38 | @CsvSource({ 39 | "ARE, 0.01, a; ares", 40 | "HECTARE, 0.0001, ha; hectares", 41 | "ACRE, 0.000247105, ac; acres", 42 | }) 43 | void testCustomArea(String name, double expected, String aliases) { 44 | Unit.Area area = Unit.Area.valueOf(name); 45 | assertEquals(expected, area.fromBaseUnit(1), expected / 1e5); 46 | for (String alias : aliases.split(";")) { 47 | assertEquals(area, Unit.Area.from(alias)); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/reader/osm/OsmNodeBoundsProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.reader.osm; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import com.onthegomap.planetiler.TestUtils; 6 | import com.onthegomap.planetiler.config.PlanetilerConfig; 7 | import com.onthegomap.planetiler.stats.Stats; 8 | import org.junit.jupiter.api.Test; 9 | import org.locationtech.jts.geom.Envelope; 10 | 11 | class OsmNodeBoundsProviderTest { 12 | @Test 13 | void test() { 14 | var config = PlanetilerConfig.defaults(); 15 | var stats = Stats.inMemory(); 16 | var inputFile = new OsmInputFile(TestUtils.pathToResource("monaco-latest.osm.pbf")); 17 | var provider = new OsmNodeBoundsProvider(inputFile, config, stats); 18 | assertEquals(new Envelope(7.4016897, 7.5002447, 43.5165358, 43.7543341), provider.getLatLonBounds()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/stats/CounterTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class CounterTest { 8 | 9 | @Test 10 | void testSingleThreadedCounter() { 11 | var counter = Counter.newSingleThreadCounter(); 12 | assertEquals(0, counter.get()); 13 | 14 | counter.inc(); 15 | assertEquals(1, counter.get()); 16 | counter.incBy(2); 17 | assertEquals(3, counter.get()); 18 | counter.incBy(-1); 19 | assertEquals(2, counter.get()); 20 | } 21 | 22 | @Test 23 | void testMultiThreadedCounter() throws InterruptedException { 24 | var counter = Counter.newMultiThreadCounter(); 25 | Thread t1 = new Thread(() -> { 26 | counter.incBy(1); 27 | counter.incBy(1); 28 | }); 29 | t1.start(); 30 | Thread t2 = new Thread(() -> counter.incBy(1)); 31 | t2.start(); 32 | t1.join(); 33 | t2.join(); 34 | assertEquals(3, counter.get()); 35 | } 36 | 37 | @Test 38 | void testMultiThreadedSubCounter() throws InterruptedException { 39 | var counter = Counter.newMultiThreadCounter(); 40 | Thread t1 = new Thread(() -> { 41 | var subCounter = counter.counterForThread(); 42 | subCounter.incBy(1); 43 | subCounter.incBy(1); 44 | }); 45 | t1.start(); 46 | Thread t2 = new Thread(() -> { 47 | var subCounter = counter.counterForThread(); 48 | subCounter.incBy(1); 49 | }); 50 | t2.start(); 51 | t1.join(); 52 | t2.join(); 53 | assertEquals(3, counter.get()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/stats/ProcessInfoTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.time.Duration; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class ProcessInfoTest { 11 | 12 | @Test 13 | void testGC() { 14 | assertTrue(ProcessInfo.getGcTime().toNanos() >= 0); 15 | } 16 | 17 | @Test 18 | void testGetDirectUsedMemoryBytes() { 19 | assertTrue(ProcessInfo.getDirectUsedMemoryBytes() >= 0); 20 | } 21 | 22 | @Test 23 | void testGetDirectUsedMemoryLimit() { 24 | assertTrue(ProcessInfo.getDirectUsedMemoryLimit() >= 0); 25 | } 26 | 27 | @Test 28 | void testGetOnHeapUsedMemoryBytes() { 29 | assertTrue(ProcessInfo.getOnHeapUsedMemoryBytes() >= 0); 30 | } 31 | 32 | @Test 33 | void testGetSystemUsedMemoryBytes() { 34 | assertTrue(ProcessInfo.getSystemMemoryBytes().getAsLong() >= 0); 35 | } 36 | 37 | @Test 38 | void testCPU() { 39 | assertFalse(ProcessInfo.getProcessCpuTime().isEmpty()); 40 | } 41 | 42 | @Test 43 | void testThreads() { 44 | assertFalse(ProcessInfo.getThreadStats().isEmpty()); 45 | } 46 | 47 | @Test 48 | void testAdd() { 49 | var a = new ProcessInfo.ThreadState("", Duration.ofSeconds(1), Duration.ofSeconds(2), Duration.ofSeconds(3), 50 | Duration.ofSeconds(4), -1); 51 | var b = new ProcessInfo.ThreadState("", Duration.ofSeconds(5), Duration.ofSeconds(6), Duration.ofSeconds(7), 52 | Duration.ofSeconds(8), -1); 53 | var sum = a.plus(b); 54 | assertEquals(Duration.ofSeconds(6), sum.cpuTime()); 55 | assertEquals(Duration.ofSeconds(8), sum.userTime()); 56 | assertEquals(Duration.ofSeconds(10), sum.waiting()); 57 | assertEquals(Duration.ofSeconds(12), sum.blocking()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/stats/StatsTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertSame; 5 | 6 | import java.util.Map; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class StatsTest { 10 | @Test 11 | void testDefaultStats() { 12 | var a = Stats.inMemory(); 13 | var b = DefaultStats.get(); 14 | assertSame(a, b); 15 | } 16 | 17 | @Test 18 | void captureDataErrors() { 19 | var a = Stats.inMemory(); 20 | assertEquals(Map.of(), a.dataErrors()); 21 | a.dataError("a"); 22 | a.dataError("a"); 23 | a.dataError("b"); 24 | assertEquals(Map.of("a", 2L, "b", 1L), a.dataErrors()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/stats/TimerTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | class TimerTest { 10 | 11 | @Test 12 | void testTimer() { 13 | Timer timer = Timer.start(); 14 | ProcessTime elapsed1 = timer.elapsed(); 15 | ProcessTime elapsed2 = timer.stop().elapsed(); 16 | ProcessTime elapsed3 = timer.elapsed(); 17 | 18 | assertEquals(elapsed2.wall(), elapsed3.wall()); 19 | assertLessThan(elapsed1.wall(), elapsed2.wall()); 20 | assertFalse(elapsed3.cpu().isEmpty(), "no CPU time"); 21 | } 22 | 23 | private > void assertLessThan(T a, T b) { 24 | assertTrue(a.compareTo(b) < 0, a + " is not less than " + b); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/stats/TimersTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stats; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertFalse; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class TimersTest { 9 | 10 | @Test 11 | void testTimers() { 12 | Timers timers = new Timers(); 13 | assertTrue(timers.all().isEmpty()); 14 | timers.printSummary(); 15 | 16 | var finish = timers.startTimer("task2"); 17 | assertTrue(timers.all().get("task2").timer().running()); 18 | finish.stop(); 19 | assertFalse(timers.all().get("task2").timer().running()); 20 | 21 | timers.printSummary(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/stream/StreamArchiveUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.stream; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import com.onthegomap.planetiler.archive.TileArchiveConfig; 6 | import com.onthegomap.planetiler.config.Arguments; 7 | import java.nio.file.Path; 8 | import java.util.List; 9 | import java.util.Map; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.io.TempDir; 12 | import org.junit.jupiter.params.ParameterizedTest; 13 | import org.junit.jupiter.params.provider.CsvSource; 14 | 15 | class StreamArchiveUtilsTest { 16 | 17 | @ParameterizedTest 18 | @CsvSource(value = {"a,a", "'a',a", "' ',$ $", "'\\n',$\n$"}, quoteCharacter = '$') 19 | void testGetEscpacedString(String in, String out) { 20 | 21 | final Arguments options = Arguments.of(Map.of("key", in)); 22 | 23 | assertEquals(out, StreamArchiveUtils.getEscapedString(options, TileArchiveConfig.Format.CSV, "key", "descr.", "ex", 24 | List.of("\n", " "))); 25 | } 26 | 27 | @Test 28 | void testConstructIndexedPath(@TempDir Path tempDir) { 29 | final Path base = tempDir.resolve("base.test"); 30 | assertEquals(base, StreamArchiveUtils.constructIndexedPath(base, 0)); 31 | assertEquals(tempDir.resolve("base.test" + 1), StreamArchiveUtils.constructIndexedPath(base, 1)); 32 | assertEquals(tempDir.resolve("base.test" + 13), StreamArchiveUtils.constructIndexedPath(base, 13)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/BinBackTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.List; 6 | import java.util.stream.Stream; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.CsvSource; 9 | 10 | class BinBackTest { 11 | 12 | @ParameterizedTest 13 | @CsvSource(value = { 14 | "3;[];[]", 15 | "2;[1];[[1]]", 16 | "2;[2];[[2]]", 17 | "2;[3];[[3]]", 18 | "3;[1,2,3];[[3], [2, 1]]", 19 | "5;[1,2,3];[[3,2],[1]]", 20 | "6;[1,2,3];[[3,2,1]]", 21 | "2;[1,2,3];[[3],[2],[1]]", 22 | "1;[1,2,3];[[3],[2],[1]]", 23 | }, delimiter = ';') 24 | void test(int limit, String inputString, String expectedString) { 25 | List input = parseList(inputString); 26 | List> expected = parseListList(expectedString); 27 | // make sure we parsed correctly 28 | assertEqualsIgnoringWhitespace(inputString, input, "failed to parse input"); 29 | assertEqualsIgnoringWhitespace(expectedString, expected, "failed to parse expected"); 30 | 31 | assertEquals(expected, BinPack.pack(input, limit, i -> i)); 32 | } 33 | 34 | private static List parseList(String string) { 35 | return Stream.of(string.replaceAll("[\\[\\]]", "").split(",")) 36 | .map(String::strip) 37 | .filter(s -> !s.isBlank()) 38 | .map(Long::parseLong) 39 | .toList(); 40 | } 41 | 42 | private static List> parseListList(String string) { 43 | return Stream.of(string.replaceAll("((^\\[)|(]$))", "").split("]\\s*,\\s*\\[")) 44 | .map(BinBackTest::parseList) 45 | .filter(l -> !l.isEmpty()) 46 | .toList(); 47 | } 48 | 49 | private static void assertEqualsIgnoringWhitespace(Object expected, Object actual, String message) { 50 | assertEquals( 51 | expected.toString().replaceAll("\\s", ""), 52 | actual.toString().replaceAll("\\s", ""), 53 | message 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/CacheByZoomTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class CacheByZoomTest { 10 | 11 | @Test 12 | void testCacheZoom() { 13 | List calls = new ArrayList<>(); 14 | CacheByZoom cached = CacheByZoom.create(i -> { 15 | calls.add(i); 16 | return i + 1; 17 | }); 18 | assertEquals(3, cached.get(2)); 19 | assertEquals(3, cached.get(2)); 20 | assertEquals(6, cached.get(5)); 21 | assertEquals(List.of(2, 5), calls); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/CloseShieldOutputStreamTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.mockito.Mockito.mock; 4 | import static org.mockito.Mockito.verify; 5 | import static org.mockito.Mockito.verifyNoMoreInteractions; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class CloseShieldOutputStreamTest { 12 | 13 | @Test 14 | void test() throws IOException { 15 | final OutputStream delegate = mock(OutputStream.class); 16 | final OutputStream os = new CloseShieldOutputStream(delegate); 17 | 18 | os.close(); 19 | verifyNoMoreInteractions(delegate); 20 | 21 | os.write(1); 22 | verify(delegate).write(1); 23 | verifyNoMoreInteractions(delegate); 24 | 25 | os.write(new byte[]{2}); 26 | verify(delegate).write(new byte[]{2}); 27 | verifyNoMoreInteractions(delegate); 28 | 29 | os.write(new byte[]{3}, 4, 5); 30 | verify(delegate).write(new byte[]{3}, 4, 5); 31 | verifyNoMoreInteractions(delegate); 32 | 33 | os.flush(); 34 | verify(delegate).flush(); 35 | verifyNoMoreInteractions(delegate); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/CountingOutputStreamTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.mockito.Mockito.mock; 5 | import static org.mockito.Mockito.verify; 6 | import static org.mockito.Mockito.verifyNoMoreInteractions; 7 | 8 | import com.onthegomap.planetiler.stats.Counter; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import org.junit.jupiter.api.Test; 12 | 13 | class CountingOutputStreamTest { 14 | 15 | @Test 16 | void test() throws IOException { 17 | 18 | final OutputStream delegate = mock(OutputStream.class); 19 | final var c = Counter.newSingleThreadCounter(); 20 | final OutputStream os = new CountingOutputStream(delegate, c::incBy); 21 | 22 | os.close(); 23 | verify(delegate).close(); 24 | assertEquals(0, c.get()); 25 | 26 | os.write(1); 27 | verify(delegate).write(1); 28 | verifyNoMoreInteractions(delegate); 29 | assertEquals(1L, c.get()); 30 | 31 | os.write(new byte[]{2, 3}); 32 | verify(delegate).write(new byte[]{2, 3}); 33 | verifyNoMoreInteractions(delegate); 34 | assertEquals(1L + 2L, c.get()); 35 | 36 | os.write(new byte[]{4, 5, 6}, 7, 8); 37 | verify(delegate).write(new byte[]{4, 5, 6}, 7, 8); 38 | verifyNoMoreInteractions(delegate); 39 | assertEquals(1L + 2L + 8L, c.get()); 40 | 41 | os.flush(); 42 | verify(delegate).flush(); 43 | verifyNoMoreInteractions(delegate); 44 | assertEquals(1L + 2L + 8L, c.get()); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/DuplicateClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertNotSame; 4 | import static org.junit.jupiter.api.Assertions.assertSame; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class DuplicateClassLoaderTest { 9 | 10 | @Test 11 | void testDuplicateClassLoader() throws Exception { 12 | var cl1 = DuplicateClassLoader.duplicateClassesWithPrefix("com.onthegomap.planetiler.util"); 13 | var cl2 = DuplicateClassLoader.duplicateClassesWithPrefix("com.onthegomap.planetiler.util"); 14 | var tc1 = cl1.loadClass("com.onthegomap.planetiler.util.TestClass"); 15 | var tc2 = cl2.loadClass("com.onthegomap.planetiler.util.TestClass"); 16 | assertSame(tc1, cl1.loadClass("com.onthegomap.planetiler.util.TestClass")); 17 | assertNotSame(tc1, tc2); 18 | tc1.getConstructor().newInstance(); 19 | tc2.getConstructor().newInstance(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/GzipTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.junit.jupiter.api.Assertions.assertFalse; 6 | 7 | import java.io.IOException; 8 | import java.util.Arrays; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class GzipTest { 12 | 13 | @Test 14 | void testRoundTrip() throws IOException { 15 | String string = "abcdef"; 16 | byte[] small = Gzip.gzip(string.getBytes(UTF_8)); 17 | byte[] big = Gzip.gunzip(small); 18 | assertEquals(string, new String(big, UTF_8)); 19 | assertFalse(Arrays.equals(small, big)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/HashingTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 5 | 6 | import java.util.function.Function; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class HashingTest { 10 | 11 | @Test 12 | void testFnv1a32() { 13 | testHasher(Hashing::fnv1a32, Hashing.FNV1_32_INIT); 14 | assertEquals(123, Hashing.fnv1a32(123)); 15 | } 16 | 17 | @Test 18 | void testFnv1a64() { 19 | testHasher(Hashing::fnv1a64, Hashing.FNV1_64_INIT); 20 | assertEquals(123, Hashing.fnv1a64(123)); 21 | } 22 | 23 | private static byte[] bytes(int... bytes) { 24 | byte[] result = new byte[bytes.length]; 25 | for (int i = 0; i < bytes.length; i++) { 26 | int value = bytes[i]; 27 | assert value >= 0 && value < 256; 28 | result[i] = (byte) value; 29 | } 30 | return result; 31 | } 32 | 33 | private static void testHasher(Function hashFn, T init) { 34 | assertEquals(hashFn.apply(bytes()), hashFn.apply(bytes())); 35 | assertEquals(hashFn.apply(bytes(1)), hashFn.apply(bytes(1))); 36 | assertEquals(hashFn.apply(bytes(1, 2)), hashFn.apply(bytes(1, 2))); 37 | assertNotEquals(hashFn.apply(bytes(1)), hashFn.apply(bytes(2))); 38 | assertNotEquals(hashFn.apply(bytes(1)), hashFn.apply(bytes(1, 1))); 39 | 40 | assertEquals(init, hashFn.apply(bytes())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/HilbertTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.fail; 5 | 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.CsvSource; 8 | import org.junit.jupiter.params.provider.ValueSource; 9 | 10 | class HilbertTest { 11 | @ParameterizedTest 12 | @ValueSource(ints = {0, 1, 2, 3, 4, 5, 15}) 13 | void testRoundTrip(int level) { 14 | int max = (1 << level) * (1 << level); 15 | int step = Math.max(1, max / 100); 16 | for (int i = 0; i < max; i += step) { 17 | long decoded = Hilbert.hilbertPositionToXY(level, i); 18 | int x = Hilbert.extractX(decoded); 19 | int y = Hilbert.extractY(decoded); 20 | int reEncoded = Hilbert.hilbertXYToIndex(level, x, y); 21 | if (reEncoded != i) { 22 | fail("x=" + x + ", y=" + y + " index=" + i + " re-encoded=" + reEncoded); 23 | } 24 | } 25 | } 26 | 27 | @ParameterizedTest 28 | @CsvSource({ 29 | "0,0,0,0", 30 | 31 | "1,0,0,0", 32 | "1,0,1,1", 33 | "1,1,1,2", 34 | "1,1,0,3", 35 | 36 | "2,1,1,2", 37 | 38 | "15,0,0,0", 39 | "15,0,1,1", 40 | "15,1,1,2", 41 | "15,1,0,3", 42 | "15,32767,0,1073741823", 43 | "15,32767,32767,715827882", 44 | 45 | "16,0,0,0", 46 | "16,1,0,1", 47 | "16,1,1,2", 48 | "16,0,1,3", 49 | "16,65535,0,-1", 50 | "16,65535,65535,-1431655766", 51 | }) 52 | void testEncoding(int level, int x, int y, int encoded) { 53 | int actualEncoded = Hilbert.hilbertXYToIndex(level, x, y); 54 | assertEquals(encoded, actualEncoded); 55 | long decoded = Hilbert.hilbertPositionToXY(level, encoded); 56 | assertEquals(x, Hilbert.extractX(decoded)); 57 | assertEquals(y, Hilbert.extractY(decoded)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/LanguageUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.CsvSource; 9 | import org.junit.jupiter.params.provider.ValueSource; 10 | 11 | class LanguageUtilsTest { 12 | 13 | @ParameterizedTest 14 | @CsvSource(value = { 15 | "null,null", 16 | "abcaāíìś+, +", 17 | "abcaāíìś, null", 18 | "日本, 日本", 19 | "abca日āíìś+, 日+", 20 | "(abc), null", 21 | "日本 (Japan), 日本", 22 | "日本 [Japan - Nippon], 日本", 23 | " Japan - Nippon (Japan) - Japan - 日本 - Japan - Nippon (Japan), 日本", 24 | "Japan - 日本~+ , 日本~+", 25 | "Japan / 日本 / Japan , 日本", 26 | }, nullValues = "null") 27 | void testRemoveNonLatin(String in, String out) { 28 | assertEquals(out, LanguageUtils.removeLatinCharacters(in)); 29 | } 30 | 31 | @ParameterizedTest 32 | @ValueSource(strings = { 33 | "name:es", 34 | "name:en-US", 35 | "name:en-001", 36 | "name:fr-x-gallo", 37 | "name:ko-Latn", 38 | "name:be-tarask", 39 | "name:ja_rm", 40 | "name:ja_kana", 41 | "name:vls", 42 | "name:zh-hant-CN", 43 | "name:zh_pinyin", 44 | "name:zh_zhuyin", 45 | "name:zh-Latn-tongyong", 46 | "name:zh-Latn-pinyin", 47 | "name:zh-Latn-wadegiles", 48 | "name:yue-Latn-jyutping", 49 | "name:tec", 50 | "name:be-tarask", 51 | "name:nan-Latn-pehoeji", 52 | "name:zh-Latn-pinyin", 53 | }) 54 | void testIsValidOsmNameTag(String in) { 55 | assertTrue(LanguageUtils.isValidOsmNameTag(in)); 56 | } 57 | 58 | @ParameterizedTest 59 | @ValueSource(strings = { 60 | "nombre", 61 | "name:", 62 | "name:xxxxx", 63 | "name:TEC", 64 | }) 65 | void testIsNotValidOsmNameTag(String in) { 66 | assertFalse(LanguageUtils.isValidOsmNameTag(in)); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/LogUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class LogUtilTest { 9 | @Test 10 | void testStageHandling() { 11 | LogUtil.clearStage(); 12 | assertNull(LogUtil.getStage()); 13 | LogUtil.setStage("test"); 14 | assertEquals("test", LogUtil.getStage()); 15 | LogUtil.setStage(LogUtil.getStage(), "child"); 16 | assertEquals("test:child", LogUtil.getStage()); 17 | LogUtil.clearStage(); 18 | assertNull(LogUtil.getStage()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/MemoizedTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static com.onthegomap.planetiler.util.Memoized.memoize; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.junit.jupiter.api.Assertions.assertThrows; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | import com.onthegomap.planetiler.ExpectedException; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class MemoizedTest { 12 | int calls = 0; 13 | 14 | @Test 15 | void testMemoize() { 16 | Memoized memoized = memoize(i -> { 17 | calls++; 18 | return i + 1; 19 | }); 20 | assertEquals(0, calls); 21 | assertEquals(1, memoized.apply(0)); 22 | assertEquals(1, calls); 23 | assertEquals(1, memoized.apply(0)); 24 | assertEquals(1, memoized.tryApply(0).get()); 25 | assertEquals(1, calls); 26 | assertEquals(2, memoized.apply(1)); 27 | assertEquals(2, memoized.apply(1)); 28 | assertEquals(2, calls); 29 | } 30 | 31 | @Test 32 | void testThrowException() { 33 | Memoized memoized = memoize(i -> { 34 | calls++; 35 | throw new ExpectedException(); 36 | }); 37 | assertEquals(0, calls); 38 | assertThrows(ExpectedException.class, () -> memoized.apply(0)); 39 | assertThrows(ExpectedException.class, () -> memoized.apply(0)); 40 | assertTrue(memoized.tryApply(0).isFailure()); 41 | assertEquals(1, calls); 42 | assertThrows(ExpectedException.class, () -> memoized.apply(1)); 43 | assertThrows(ExpectedException.class, () -> memoized.apply(1)); 44 | assertTrue(memoized.tryApply(1).isFailure()); 45 | assertEquals(2, calls); 46 | } 47 | 48 | @Test 49 | void testTryCast() { 50 | Memoized memoized = memoize(i -> { 51 | calls++; 52 | return i + 1; 53 | }); 54 | assertEquals(1, memoized.tryApply(0, Number.class).get()); 55 | var failed = memoized.tryApply(0, String.class); 56 | assertTrue(failed.isFailure()); 57 | assertThrows(ClassCastException.class, failed::get); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/ResourceUsageTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertThrows; 4 | 5 | import java.util.OptionalLong; 6 | import org.junit.jupiter.api.Test; 7 | 8 | class ResourceUsageTest { 9 | 10 | @Test 11 | void testEmpty() { 12 | new ResourceUsage("testing it out").checkAgainstLimits(true, false); 13 | } 14 | 15 | @Test 16 | void testFakeResource() { 17 | var resource = new ResourceUsage.Global("testing resource", "get more", () -> OptionalLong.of(10L)); 18 | var check = new ResourceUsage("testing it out") 19 | .add(resource, 9, "description"); 20 | 21 | check.checkAgainstLimits(false, false); 22 | check.add(resource, 2, "more"); 23 | assertThrows(IllegalStateException.class, () -> check.checkAgainstLimits(false, false)); 24 | check.checkAgainstLimits(true, false); 25 | } 26 | 27 | @Test 28 | void testTooMuchRam() { 29 | var check = new ResourceUsage("testing it out") 30 | .addMemory(Runtime.getRuntime().maxMemory() - 1, "test"); 31 | 32 | check.checkAgainstLimits(false, false); 33 | check.addMemory(2, "more"); 34 | assertThrows(IllegalStateException.class, () -> check.checkAgainstLimits(false, false)); 35 | check.checkAgainstLimits(true, false); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/SlidingWindowTests.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertFalse; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.TimeUnit; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.Timeout; 9 | 10 | class SlidingWindowTests { 11 | @Test 12 | @Timeout(10) 13 | void testSlidingWindow() throws InterruptedException { 14 | var slidingWindow = new SlidingWindow(5); 15 | var latch1 = new CountDownLatch(1); 16 | var latch2 = new CountDownLatch(1); 17 | Thread t1 = new Thread(() -> { 18 | latch1.countDown(); 19 | slidingWindow.waitUntilInsideWindow(9); 20 | latch2.countDown(); 21 | }); 22 | t1.start(); 23 | latch1.await(); 24 | assertFalse(latch2.await(100, TimeUnit.MILLISECONDS)); 25 | slidingWindow.advanceTail(4); 26 | assertFalse(latch2.await(100, TimeUnit.MILLISECONDS)); 27 | slidingWindow.advanceTail(5); 28 | latch2.await(); 29 | t1.join(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/TestClass.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | public class TestClass {} 4 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/ThreadLocalTransliteratorTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class ThreadLocalTransliteratorTest { 8 | @Test 9 | void test() { 10 | var t1 = new ThreadLocalTransliterator().getInstance("Any-Latin"); 11 | var t2 = new ThreadLocalTransliterator().getInstance("Any-Latin"); 12 | assertEquals("rì běn", t1.transliterate("日本")); 13 | assertEquals("rì běn", t2.transliterate("日本")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/TryTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertInstanceOf; 5 | import static org.junit.jupiter.api.Assertions.assertThrows; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | class TryTest { 11 | @Test 12 | void success() { 13 | var result = Try.apply(() -> 1); 14 | assertEquals(Try.success(1), result); 15 | assertEquals(1, result.get()); 16 | } 17 | 18 | @Test 19 | void failure() { 20 | var exception = new IllegalStateException(); 21 | var result = Try.apply(() -> { 22 | throw exception; 23 | }); 24 | assertEquals(Try.failure(exception), result); 25 | assertThrows(IllegalStateException.class, result::get); 26 | } 27 | 28 | @Test 29 | void cast() { 30 | var result = Try.apply(() -> 1); 31 | assertEquals(Try.success(1), result.cast(Number.class)); 32 | assertTrue(result.cast(String.class).isFailure()); 33 | assertInstanceOf(ClassCastException.class, result.cast(String.class).exception()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/VarIntTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import com.carrotsearch.hppc.ByteArrayList; 6 | import java.io.IOException; 7 | import java.nio.ByteBuffer; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class VarIntTest { 11 | 12 | @Test 13 | void testRoundTrip() throws IOException { 14 | ByteArrayList dir = new ByteArrayList(); 15 | VarInt.putVarLong(0, dir); 16 | VarInt.putVarLong(1, dir); 17 | VarInt.putVarLong(Long.MAX_VALUE, dir); 18 | VarInt.putVarLong(Long.MIN_VALUE, dir); 19 | ByteBuffer output = ByteBuffer.wrap(dir.toArray()); 20 | assertEquals(0, VarInt.getVarLong(output)); 21 | assertEquals(1, VarInt.getVarLong(output)); 22 | assertEquals(Long.MAX_VALUE, VarInt.getVarLong(output)); 23 | assertEquals(Long.MIN_VALUE, VarInt.getVarLong(output)); 24 | } 25 | 26 | @Test 27 | void testUnsignedEncoding() throws IOException { 28 | byte[] rawbytes = {0, 1, 127, (byte) 0xe5, (byte) 0x8e, (byte) 0x26}; 29 | ByteBuffer buf = ByteBuffer.wrap(rawbytes); 30 | 31 | assertEquals(0, VarInt.getVarLong(buf)); 32 | assertEquals(1, VarInt.getVarLong(buf)); 33 | assertEquals(127, VarInt.getVarLong(buf)); 34 | assertEquals(624485, VarInt.getVarLong(buf)); 35 | 36 | byte[] max_safe_js_integer = 37 | {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0xf}; 38 | assertEquals(9007199254740991L, VarInt.getVarLong(ByteBuffer.wrap(max_safe_js_integer))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/util/ZoomFunctionTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | 6 | import java.util.Map; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class ZoomFunctionTest { 10 | 11 | private static void assertValueInRange(ZoomFunction fn, int min, int max, T value) { 12 | for (int i = min; i <= max; i++) { 13 | assertEquals(value, fn.apply(i), "z" + i); 14 | } 15 | } 16 | 17 | @Test 18 | void testNullBelowZoom() { 19 | var fn = ZoomFunction.minZoom(10, "value"); 20 | assertValueInRange(fn, 0, 9, null); 21 | assertValueInRange(fn, 10, 14, "value"); 22 | } 23 | 24 | @Test 25 | void testNullAboveZoom() { 26 | var fn = ZoomFunction.maxZoom(10, "value"); 27 | assertValueInRange(fn, 0, 10, "value"); 28 | assertValueInRange(fn, 11, 14, null); 29 | } 30 | 31 | @Test 32 | void testValueInRange() { 33 | var fn = ZoomFunction.zoomRange(10, 12, "value"); 34 | assertValueInRange(fn, 0, 9, null); 35 | assertValueInRange(fn, 10, 12, "value"); 36 | assertValueInRange(fn, 13, 14, null); 37 | } 38 | 39 | @Test 40 | void testMultipleThresholds() { 41 | var fn = ZoomFunction.fromMaxZoomThresholds(Map.of( 42 | 3, "3", 43 | 5, "5" 44 | )); 45 | assertValueInRange(fn, 0, 3, "3"); 46 | assertValueInRange(fn, 4, 5, "5"); 47 | assertValueInRange(fn, 6, 14, null); 48 | } 49 | 50 | @Test 51 | void testConvertMetersToPixels() { 52 | var fn = ZoomFunction.meterThresholds() 53 | .put(7, 20_000) 54 | .put(8, 14_000) 55 | .put(11, 8_000); 56 | assertNull(fn.apply(6)); 57 | assertEquals(16, fn.apply(7).doubleValue(), 1d); 58 | assertEquals(23, fn.apply(8).doubleValue(), 1d); 59 | assertNull(fn.apply(9)); 60 | assertNull(fn.apply(10)); 61 | assertEquals(105, fn.apply(11).doubleValue(), 1d); 62 | assertNull(fn.apply(12)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/worker/WeightedHandoffQueueTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.worker; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.Timeout; 8 | 9 | class WeightedHandoffQueueTest { 10 | 11 | @Test 12 | @Timeout(10) 13 | void testEmpty() { 14 | WeightedHandoffQueue q = new WeightedHandoffQueue<>(1, 1); 15 | q.close(); 16 | assertNull(q.get()); 17 | } 18 | 19 | @Test 20 | @Timeout(10) 21 | void testOneItem() { 22 | WeightedHandoffQueue q = new WeightedHandoffQueue<>(1, 1); 23 | q.accept("a", 1); 24 | assertEquals("a", q.get()); 25 | q.close(); 26 | assertNull(q.get()); 27 | } 28 | 29 | @Test 30 | @Timeout(10) 31 | void testOneItemCloseFirst() { 32 | WeightedHandoffQueue q = new WeightedHandoffQueue<>(2, 1); 33 | q.accept("a", 1); 34 | q.close(); 35 | assertEquals("a", q.get()); 36 | assertNull(q.get()); 37 | } 38 | 39 | @Test 40 | @Timeout(10) 41 | void testMoreItemsThanBatchSize() { 42 | WeightedHandoffQueue q = new WeightedHandoffQueue<>(3, 2); 43 | q.accept("a", 1); 44 | q.accept("b", 1); 45 | q.accept("c", 1); 46 | q.close(); 47 | assertEquals("a", q.get()); 48 | assertEquals("b", q.get()); 49 | assertEquals("c", q.get()); 50 | assertNull(q.get()); 51 | } 52 | 53 | @Test 54 | @Timeout(10) 55 | void testManyItems() { 56 | WeightedHandoffQueue q = new WeightedHandoffQueue<>(100, 100); 57 | for (int i = 0; i < 950; i++) { 58 | q.accept(i, 1); 59 | } 60 | q.close(); 61 | for (int i = 0; i < 950; i++) { 62 | assertEquals((Integer) i, q.get()); 63 | } 64 | assertNull(q.get()); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /planetiler-core/src/test/java/com/onthegomap/planetiler/worker/WorkerTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.worker; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertThrows; 4 | 5 | import com.onthegomap.planetiler.ExpectedException; 6 | import com.onthegomap.planetiler.stats.Stats; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.Timeout; 9 | 10 | class WorkerTest { 11 | 12 | @Test 13 | @Timeout(10) 14 | void testExceptionHandled() { 15 | var worker = new Worker("prefix", Stats.inMemory(), 4, workerNum -> { 16 | if (workerNum == 1) { 17 | throw new ExpectedException(); 18 | } else { 19 | Thread.sleep(5000); 20 | } 21 | }); 22 | assertThrows(RuntimeException.class, worker::await); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/bostonbuildings.mbtiles: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/bostonbuildings.mbtiles -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/bottomrightearth.poly: -------------------------------------------------------------------------------- 1 | bottom-right 2 | 1 3 | 180 -1 4 | 180 -85 5 | 1 -85 6 | 180 -1 7 | END 8 | END 9 | -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/box1degree.pmtiles: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/box1degree.pmtiles -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/chesapeake.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/chesapeake.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/empty-geom.gpkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/empty-geom.gpkg -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/feature.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "geometry": { 4 | "type": "Point", 5 | "coordinates": [102.0, 0.5] 6 | }, 7 | "properties": { 8 | "name": "point" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/featurecollection.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [{ 4 | "type": "Feature", 5 | "geometry": { 6 | "type": "Point", 7 | "coordinates": [102.0, 0.5] 8 | }, 9 | "properties": { 10 | "name": "point" 11 | } 12 | }, { 13 | "type": "Feature", 14 | "geometry": { 15 | "type": "LineString", 16 | "coordinates": [[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]] 17 | }, 18 | "properties": { 19 | "name": "line", 20 | "prop1": 0.0 21 | } 22 | }, { 23 | "type": "Feature", 24 | "geometry": { 25 | "type": "Polygon", 26 | "coordinates": [ 27 | [ 28 | [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], 29 | [100.0, 1.0], [100.0, 0.0] 30 | ] 31 | ] 32 | }, 33 | "properties": { 34 | "name": "polygon" 35 | } 36 | }] 37 | } -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/geopackage-unindexed.gpkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/geopackage-unindexed.gpkg -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/geopackage.gpkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/geopackage.gpkg -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/geopackage.gpkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/geopackage.gpkg.zip -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_546_terschelling.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_546_terschelling.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_1.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_1.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_2.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_2.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_3.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_3.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_4.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_4.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_5.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_5.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_6.wkb: -------------------------------------------------------------------------------- 1 |  @n@mH@n2@m:@nH@m0@n`@mb@n\@m`@nL@mh@nL@ml@n.@m|@n@mH@nF@m@n>@m@nb@m@np@m0@nV@m<@nF@m -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_7.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_7.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_8.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_8.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/issue_700/exception_9.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/issue_700/exception_9.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/jakartabuildings.mbtiles: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/jakartabuildings.mbtiles -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/kobroor.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/kobroor.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/log4j2-test.properties: -------------------------------------------------------------------------------- 1 | appenders=console 2 | appender.console.type=Console 3 | appender.console.name=STDOUT 4 | appender.console.layout.type=PatternLayout 5 | appender.console.layout.pattern=$${uptime:now} %level{length=3} %X{stage}- %msg%n%throwable 6 | packages=com.onthegomap.planetiler.util.log4j 7 | rootLogger.level=warn 8 | rootLogger.appenderRefs=stdout 9 | rootLogger.appenderRef.stdout.ref=STDOUT 10 | 11 | # suppress warning about unreadable duckdb statistics 12 | logger.apachecorrupt.name=org.apache.parquet.CorruptStatistics 13 | logger.apachecorrupt.level=error 14 | -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/mdshore.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/mdshore.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/mergelines/i90.wkb.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/mergelines/i90.wkb.gz -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/mergelines/mergelines_1759_point_line.wkb.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/mergelines/mergelines_1759_point_line.wkb.gz -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/mergelines/mergelines_200433_lines.wkb.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/mergelines/mergelines_200433_lines.wkb.gz -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/mergelines/mergelines_239823_lines.wkb.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/mergelines/mergelines_239823_lines.wkb.gz -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/monaco-latest-without-changesets.osm.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/monaco-latest-without-changesets.osm.pbf -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/monaco-latest.lz4.osm.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/monaco-latest.lz4.osm.pbf -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/monaco-latest.osm.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/monaco-latest.osm.pbf -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/natural_earth_vector.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/natural_earth_vector.sqlite -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/natural_earth_vector.sqlite.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/natural_earth_vector.sqlite.zip -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/newlines.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Feature","geometry":{"type":"Point","coordinates":[102,0.5]},"properties":{"name":"point"}} 2 | {"type":"Feature","geometry":{"type":"LineString","coordinates":[[102,0],[103,1],[104,0],[105,1]]},"properties":{"name":"line","prop1":0}} 3 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[100,0],[101,0],[101,1],[100,1],[100,0]]]},"properties":{"name":"polygon"}} 4 | -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/njshore.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/njshore.wkb -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/all_data_types.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/all_data_types.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.customgeometryname.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.customgeometryname.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.geoarrow_from_gdal.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.geoarrow_from_gdal.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.gzip.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.gzip.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.lz4hadoop.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.lz4hadoop.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.lz4raw.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.lz4raw.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.none.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.none.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.snappy.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.snappy.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/parquet/boston.zstd.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/parquet/boston.zstd.parquet -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/shapefile-copy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/shapefile-copy.zip -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/shapefile.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/shapefile.zip -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/test.properties: -------------------------------------------------------------------------------- 1 | # used for ArgumentsTest 2 | key1=value1fromfile 3 | key2=value2fromfile 4 | -------------------------------------------------------------------------------- /planetiler-core/src/test/resources/water-polygons-split-3857.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onthegomap/planetiler/4266e06de4d73ff253f89863ab4718b5cd0ce827/planetiler-core/src/test/resources/water-polygons-split-3857.zip -------------------------------------------------------------------------------- /planetiler-custommap/.gitignore: -------------------------------------------------------------------------------- 1 | *.mbtiles 2 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/Source.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap; 2 | 3 | import com.onthegomap.planetiler.custommap.configschema.DataSourceType; 4 | import java.nio.file.Path; 5 | 6 | /** A parsed source definition from a config file. */ 7 | public record Source( 8 | String id, 9 | DataSourceType type, 10 | String url, 11 | Path localPath, 12 | String projection 13 | ) { 14 | 15 | public String defaultFileUrl() { 16 | String result = url 17 | .replaceFirst("^https?://", "") 18 | .replaceAll("[\\W&&[^.]]+", "_"); 19 | if (type == DataSourceType.OSM && !result.endsWith(".pbf")) { 20 | result = result + ".osm.pbf"; 21 | } 22 | return result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/AttributeDefinition.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import java.util.Map; 5 | 6 | public record AttributeDefinition( 7 | String key, 8 | @JsonProperty("include_when") Object includeWhen, 9 | @JsonProperty("exclude_when") Object excludeWhen, 10 | @JsonProperty("min_zoom") Object minZoom, 11 | @JsonProperty("min_zoom_by_value") Map minZoomByValue, 12 | @JsonProperty("min_tile_cover_size") Double minTileCoverSize, 13 | @JsonProperty("else") Object fallback, 14 | // pass-through to value expression 15 | @JsonProperty("value") Object value, 16 | @JsonProperty("tag_value") String tagValue, 17 | @JsonProperty("arg_value") String argValue, 18 | Object type, 19 | Object coalesce 20 | ) {} 21 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/DataSource.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record DataSource( 6 | DataSourceType type, 7 | Object url, 8 | @JsonProperty("local_path") Object localPath, 9 | Object projection 10 | ) {} 11 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/DataSourceType.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum DataSourceType { 6 | @JsonProperty("osm") 7 | OSM, 8 | @JsonProperty("shapefile") 9 | SHAPEFILE, 10 | @JsonProperty("geopackage") 11 | GEOPACKAGE, 12 | @JsonProperty("geojson") 13 | GEOJSON 14 | } 15 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/FeatureItem.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import java.util.Collection; 6 | import java.util.List; 7 | 8 | public record FeatureItem( 9 | @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) List source, 10 | @JsonProperty("min_zoom") Object minZoom, 11 | @JsonProperty("max_zoom") Object maxZoom, 12 | @JsonProperty("min_size") Object minSize, 13 | @JsonProperty FeatureGeometry geometry, 14 | @JsonProperty("include_when") Object includeWhen, 15 | @JsonProperty("exclude_when") Object excludeWhen, 16 | Collection attributes 17 | ) { 18 | 19 | @Override 20 | public Collection attributes() { 21 | return attributes == null ? List.of() : attributes; 22 | } 23 | 24 | @Override 25 | public FeatureGeometry geometry() { 26 | return geometry == null ? FeatureGeometry.ANY : geometry; 27 | } 28 | 29 | @Override 30 | public List source() { 31 | return source == null ? List.of() : source; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/FeatureLayer.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import java.util.Collection; 5 | 6 | public record FeatureLayer( 7 | String id, 8 | Collection features, 9 | @JsonProperty("tile_post_process") PostProcess postProcess 10 | ) {} 11 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/MergeLineStrings.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record MergeLineStrings( 6 | @JsonProperty("min_length") double minLength, 7 | @JsonProperty("tolerance") double tolerance, 8 | @JsonProperty("buffer") double buffer 9 | ) {} 10 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/MergePolygons.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record MergePolygons( 6 | @JsonProperty("min_area") double minArea 7 | ) {} 8 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/PostProcess.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record PostProcess( 6 | @JsonProperty("merge_line_strings") MergeLineStrings mergeLineStrings, 7 | @JsonProperty("merge_polygons") MergePolygons mergePolygons 8 | ) {} 9 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/SchemaConfig.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.onthegomap.planetiler.util.YAML; 5 | import java.nio.file.Path; 6 | import java.util.Collection; 7 | import java.util.Map; 8 | 9 | /** 10 | * An object representation of a vector tile server schema. This object is mapped to a schema YML file using SnakeYAML. 11 | */ 12 | public record SchemaConfig( 13 | @JsonProperty("schema_name") String schemaName, 14 | @JsonProperty("schema_description") String schemaDescription, 15 | String attribution, 16 | Map sources, 17 | Object definitions, 18 | @JsonProperty("tag_mappings") Map inputMappings, 19 | Collection layers, 20 | Object examples, 21 | Map args 22 | ) { 23 | 24 | private static final String DEFAULT_ATTRIBUTION = """ 25 | © OpenStreetMap contributors 26 | """.trim(); 27 | 28 | @Override 29 | public String attribution() { 30 | return attribution == null ? DEFAULT_ATTRIBUTION : attribution; 31 | } 32 | 33 | @Override 34 | public Map args() { 35 | return args == null ? Map.of() : args; 36 | } 37 | 38 | public static SchemaConfig load(Path path) { 39 | return YAML.load(path, SchemaConfig.class); 40 | } 41 | 42 | public static SchemaConfig load(String string) { 43 | return YAML.load(string, SchemaConfig.class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/ZoomOverride.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.configschema; 2 | 3 | /** 4 | * Configuration item that instructs the renderer to override the default zoom range for features which contain specific 5 | * tag combinations. 6 | */ 7 | public record ZoomOverride( 8 | Integer min, 9 | Integer max, 10 | Object tag) {} 11 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/expression/EvaluationException.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.expression; 2 | 3 | /** 4 | * Exception that occurs at runtime when evaluating a {@link ConfigExpressionScript}. 5 | */ 6 | public class EvaluationException extends RuntimeException { 7 | 8 | public EvaluationException(String script, Exception cause) { 9 | super("Error evaluating script: %s".formatted(script), cause); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/expression/ParseException.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.expression; 2 | 3 | /** 4 | * Exception that occurs at compile-time when preparing an embedded expression. 5 | */ 6 | public class ParseException extends RuntimeException { 7 | 8 | public ParseException(String script, Exception cause) { 9 | super("Error parsing: %s".formatted(script), cause); 10 | } 11 | 12 | public ParseException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/expression/ScriptContext.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.expression; 2 | 3 | import com.google.common.base.Function; 4 | import com.onthegomap.planetiler.custommap.TagValueProducer; 5 | import com.onthegomap.planetiler.reader.WithTags; 6 | import java.util.Map; 7 | 8 | /** 9 | * The runtime environment of an executing expression script that returns variables by their name. 10 | */ 11 | public interface ScriptContext extends Function, WithTags { 12 | static ScriptContext empty() { 13 | return key -> null; 14 | } 15 | 16 | @Override 17 | default Map tags() { 18 | // TODO remove this when MultiExpression can take any object 19 | return Map.of(); 20 | } 21 | 22 | default TagValueProducer tagValueProducer() { 23 | return TagValueProducer.EMPTY; 24 | } 25 | 26 | default Object argument(String key) { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/expression/stdlib/BuiltInFunction.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.expression.stdlib; 2 | 3 | import com.google.api.expr.v1alpha1.Decl; 4 | import java.util.List; 5 | import org.projectnessie.cel.interpreter.functions.Overload; 6 | 7 | /** 8 | * Groups together a built-in function's type signature and implementation that is available to dynamic expressions. 9 | */ 10 | record BuiltInFunction(Decl signature, List implementations) { 11 | BuiltInFunction(Decl signature, Overload... implementations) { 12 | this(signature, List.of(implementations)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/expression/stdlib/PlanetilerLib.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.expression.stdlib; 2 | 3 | import java.util.List; 4 | import org.projectnessie.cel.EnvOption; 5 | import org.projectnessie.cel.Library; 6 | import org.projectnessie.cel.ProgramOption; 7 | import org.projectnessie.cel.interpreter.functions.Overload; 8 | 9 | /** Creates a {@link Library} of built-in functions that can be made available to dynamic expressions. */ 10 | class PlanetilerLib implements Library { 11 | 12 | private final List builtInFunctions; 13 | 14 | PlanetilerLib(List builtInFunctions) { 15 | this.builtInFunctions = builtInFunctions; 16 | } 17 | 18 | @Override 19 | public List getCompileOptions() { 20 | return List.of(EnvOption.declarations( 21 | builtInFunctions.stream().map(BuiltInFunction::signature).toList() 22 | )); 23 | } 24 | 25 | @Override 26 | public List getProgramOptions() { 27 | return List.of(ProgramOption.functions( 28 | builtInFunctions.stream().flatMap(b -> b.implementations().stream()).toArray(Overload[]::new) 29 | )); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/resources/samples/highway_areas.yml: -------------------------------------------------------------------------------- 1 | schema_name: Highway areas 2 | schema_description: Features that represent the physical area of roads 3 | attribution: © 4 | OpenStreetMap contributors 5 | sources: 6 | osm: 7 | type: osm 8 | url: geofabrik:poland 9 | tag_mappings: 10 | bridge: boolean 11 | layer: long 12 | layers: 13 | - id: highway_area 14 | features: 15 | - source: osm 16 | geometry: polygon 17 | min_zoom: 14 18 | include_when: 19 | area:highway: 20 | attributes: 21 | - key: highway 22 | tag_value: area:highway 23 | - key: layer 24 | - key: surface 25 | - key: bridge 26 | - source: osm 27 | geometry: polygon 28 | min_zoom: 14 29 | include_when: 30 | man_made: bridge 31 | attributes: 32 | - key: man_made 33 | value: bridge 34 | - key: layer 35 | - key: surface 36 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/resources/samples/manholes.yml: -------------------------------------------------------------------------------- 1 | schema_name: Manhole covers 2 | schema_description: Manhole covers 3 | attribution: © 4 | OpenStreetMap contributors 5 | sources: 6 | osm: 7 | type: osm 8 | url: geofabrik:rhode-island 9 | layers: 10 | - id: manhole 11 | features: 12 | - source: 13 | - osm 14 | geometry: point 15 | min_zoom: 14 16 | include_when: 17 | man_made: manhole 18 | attributes: 19 | - key: man_made 20 | - key: manhole 21 | - key: operator 22 | - key: ref 23 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/resources/samples/natural_earth.yml: -------------------------------------------------------------------------------- 1 | schema_name: Natural Earth 2 | schema_description: Natural Earth Oceans and Lakes example 3 | args: 4 | minzoom: 0 5 | maxzoom: 5 6 | 7 | sources: 8 | natural_earth: 9 | type: geopackage 10 | url: "https://naciscdn.org/naturalearth/packages/natural_earth_vector.gpkg.zip" 11 | projection: EPSG:4326 12 | 13 | layers: 14 | - id: ne_water_bodies 15 | features: 16 | - source: natural_earth 17 | include_when: 18 | '${ feature.source_layer }': 19 | - ne_10m_ocean 20 | - ne_10m_lakes 21 | - ne_50m_ocean 22 | - ne_50m_lakes 23 | - ne_110m_ocean 24 | - ne_110m_lakes 25 | min_zoom: 26 | 5: 27 | '${ feature.source_layer }': 28 | - ne_10m_ocean 29 | 4: 30 | '${ feature.source_layer }': 31 | - ne_10m_lakes 32 | 2: 33 | '${ feature.source_layer }': 34 | - ne_50m_ocean 35 | - ne_50m_lakes 36 | 0: 37 | '${ feature.source_layer }': 38 | - ne_110m_ocean 39 | - ne_110m_lakes 40 | max_zoom: 41 | 5: 42 | '${ feature.source_layer }': 43 | - ne_10m_ocean 44 | - ne_10m_lakes 45 | 4: 46 | '${ feature.source_layer }': 47 | - ne_50m_ocean 48 | 3: 49 | '${ feature.source_layer }': 50 | - ne_50m_lakes 51 | 1: 52 | '${ feature.source_layer }': 53 | - ne_110m_lakes 54 | - ne_110m_ocean 55 | attributes: 56 | - key: class 57 | value: 58 | ocean: 59 | '${ feature.source_layer }': 60 | - ne_10m_ocean 61 | - ne_50m_ocean 62 | - ne_110m_ocean 63 | lake: 64 | '${ feature.source_layer }': 65 | - ne_10m_lakes 66 | - ne_50m_lakes 67 | - ne_110m_lakes 68 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/resources/samples/power.yml: -------------------------------------------------------------------------------- 1 | schema_name: Power 2 | schema_description: Features that represent electrical power grid 3 | attribution: © 4 | OpenStreetMap contributors 5 | sources: 6 | osm: 7 | type: osm 8 | url: geofabrik:new-jersey 9 | layers: 10 | - id: power 11 | features: 12 | - source: osm 13 | geometry: point 14 | min_zoom: 13 15 | include_when: 16 | power: 17 | - pole 18 | attributes: 19 | - key: power 20 | - key: ref 21 | - key: height 22 | - key: operator 23 | - source: osm 24 | geometry: line 25 | min_zoom: 7 26 | include_when: 27 | power: 28 | - line 29 | attributes: 30 | - key: power 31 | - key: voltage 32 | - key: cables 33 | - key: operator 34 | -------------------------------------------------------------------------------- /planetiler-custommap/src/main/resources/samples/railways.yml: -------------------------------------------------------------------------------- 1 | schema_name: Railways 2 | schema_description: Railways (layers outputting merged & un-merged lines) 3 | attribution: © OpenStreetMap contributors 4 | args: 5 | area: 6 | description: Geofabrik area to download 7 | default: greater-london 8 | osm_url: 9 | description: OSM URL to download 10 | default: '${ args.area == "planet" ? "aws:latest" : ("geofabrik:" + args.area) }' 11 | sources: 12 | osm: 13 | type: osm 14 | url: '${ args.osm_url }' 15 | layers: 16 | - id: railways_merged 17 | features: 18 | - source: osm 19 | geometry: line 20 | min_zoom: 4 21 | min_size: 0 22 | include_when: 23 | __all__: 24 | - railway: rail 25 | - usage: main 26 | exclude_when: 27 | service: __any__ 28 | tile_post_process: 29 | merge_line_strings: 30 | min_length: 0 31 | tolerance: -1 32 | buffer: -1 33 | - id: railways_unmerged 34 | features: 35 | - source: osm 36 | geometry: line 37 | min_zoom: 4 38 | include_when: 39 | __all__: 40 | - railway: rail 41 | - usage: main 42 | exclude_when: 43 | service: __any__ 44 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/SchemaTests.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap; 2 | 3 | import static org.junit.jupiter.api.DynamicTest.dynamicTest; 4 | 5 | import com.onthegomap.planetiler.custommap.configschema.SchemaConfig; 6 | import com.onthegomap.planetiler.custommap.validator.SchemaValidator; 7 | import com.onthegomap.planetiler.validator.SchemaSpecification; 8 | import java.nio.file.Path; 9 | import java.util.List; 10 | import org.junit.jupiter.api.DynamicTest; 11 | import org.junit.jupiter.api.TestFactory; 12 | 13 | class SchemaTests { 14 | @TestFactory 15 | List shortbread() { 16 | return testSchema("shortbread.yml", "shortbread.spec.yml"); 17 | } 18 | 19 | private List testSchema(String schema, String spec) { 20 | var base = Path.of("src", "main", "resources", "samples"); 21 | var result = SchemaValidator.validate( 22 | SchemaConfig.load(base.resolve(schema)), 23 | SchemaSpecification.load(base.resolve(spec)) 24 | ); 25 | return result.results().stream() 26 | .map(test -> dynamicTest(test.example().name(), () -> { 27 | if (test.issues().isFailure()) { 28 | throw test.issues().exception(); 29 | } 30 | if (!test.issues().get().isEmpty()) { 31 | throw new AssertionError("Validation failed:\n" + String.join("\n", test.issues().get())); 32 | } 33 | })).toList(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/SchemaYAMLLoadTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertFalse; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | 6 | import com.onthegomap.planetiler.config.Arguments; 7 | import com.onthegomap.planetiler.custommap.configschema.SchemaConfig; 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import org.junit.jupiter.api.Test; 13 | 14 | class SchemaYAMLLoadTest { 15 | 16 | /** 17 | * Test to ensure that all bundled schemas load to POJOs. 18 | * 19 | * @throws Exception 20 | */ 21 | @Test 22 | void testSchemaLoad() throws IOException { 23 | testSchemasInFolder(Paths.get("src", "main", "resources", "samples")); 24 | testSchemasInFolder(Paths.get("src", "test", "resources", "validSchema")); 25 | } 26 | 27 | private void testSchemasInFolder(Path path) throws IOException { 28 | var schemaFiles = Files.walk(path) 29 | .filter(p -> p.getFileName().toString().endsWith(".yml")) 30 | .filter(p -> !p.getFileName().toString().endsWith("spec.yml")) 31 | .toList(); 32 | 33 | assertFalse(schemaFiles.isEmpty(), "No files found"); 34 | 35 | for (Path schemaFile : schemaFiles) { 36 | var schemaConfig = SchemaConfig.load(schemaFile); 37 | var root = Contexts.buildRootContext(Arguments.of(), schemaConfig.args()); 38 | assertNotNull(schemaConfig, () -> "Failed to unmarshall " + schemaFile.toString()); 39 | new ConfiguredProfile(schemaConfig, root); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/TestContexts.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap; 2 | 3 | import com.onthegomap.planetiler.custommap.expression.ScriptEnvironment; 4 | 5 | public class TestContexts { 6 | public static final Contexts.Root ROOT = Contexts.emptyRoot(); 7 | public static final ScriptEnvironment ROOT_CONTEXT = ROOT.description(); 8 | public static final ScriptEnvironment PROCESS_FEATURE = 9 | Contexts.ProcessFeature.description(ROOT); 10 | public static final ScriptEnvironment FEATURE_POST_MATCH = 11 | Contexts.FeaturePostMatch.description(ROOT); 12 | public static final ScriptEnvironment FEATURE_ATTRIBUTE = 13 | Contexts.FeatureAttribute.description(ROOT); 14 | } 15 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/TypeConversionTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertInstanceOf; 5 | 6 | import java.util.List; 7 | import java.util.stream.Stream; 8 | import org.junit.jupiter.params.ParameterizedTest; 9 | import org.junit.jupiter.params.provider.MethodSource; 10 | 11 | class TypeConversionTest { 12 | record Case(Object in, Class clazz, Object out) { 13 | Case { 14 | if (out != null) { 15 | assertInstanceOf(clazz, out); 16 | } 17 | } 18 | } 19 | 20 | private static Stream testTo(Class clazz, Object out, Object... in) { 21 | return Stream.of(in).map(i -> new Case(i, clazz, out)); 22 | } 23 | 24 | private static Stream testConvertTo(Object out, Object... in) { 25 | return testTo(out.getClass(), out, in); 26 | } 27 | 28 | static List cases() { 29 | return Stream.of( 30 | testConvertTo(1, "1", 1L, 1.1), 31 | testConvertTo(1L, "1", 1L, 1.1), 32 | testConvertTo(1d, "1", "1.0", "1e0", 1L, 1d), 33 | testConvertTo(1.1, "1.1", 1.1), 34 | testConvertTo("1", "1", 1, 1L, 1d), 35 | testConvertTo("1.1", "1.1", 1.1d, 1.1f), 36 | testConvertTo("1000", 1000, 1000d), 37 | testConvertTo("NaN", Double.NaN), 38 | testConvertTo(true, 1, 1L, 1d, "true", "TRUE"), 39 | testConvertTo(false, 0, 0L, 0d, "false", "FALSE", "no"), 40 | testConvertTo("true", true), 41 | testConvertTo("false", false), 42 | testTo(String.class, null, (String) null), 43 | testTo(Integer.class, null, (String) null) 44 | ).flatMap(d -> d).toList(); 45 | } 46 | 47 | @ParameterizedTest 48 | @MethodSource("cases") 49 | void testConversion(Case testCase) { 50 | Object out = TypeConversion.convert(testCase.in, testCase.clazz); 51 | assertEquals(testCase.out, out); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/expression/BooleanExpressionScriptTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.expression; 2 | 3 | import static com.onthegomap.planetiler.expression.Expression.and; 4 | import static com.onthegomap.planetiler.expression.Expression.or; 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | import com.onthegomap.planetiler.custommap.TestContexts; 8 | import com.onthegomap.planetiler.expression.Expression; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class BooleanExpressionScriptTest { 12 | @Test 13 | void testSimplify() { 14 | assertEquals(Expression.TRUE, 15 | and(or(BooleanExpressionScript.script("1+1<3", TestContexts.ROOT_CONTEXT))).simplify()); 16 | assertEquals(Expression.FALSE, 17 | and(or(BooleanExpressionScript.script("1+1>3", TestContexts.ROOT_CONTEXT))).simplify()); 18 | 19 | var other = 20 | BooleanExpressionScript.script("feature.tags.natural", TestContexts.PROCESS_FEATURE); 21 | assertEquals(other, and(or(other)).simplify()); 22 | } 23 | 24 | @Test 25 | void testSimplifyInlinesArguments() { 26 | assertEquals(Expression.TRUE, 27 | and(or(BooleanExpressionScript.script("args.threads > 0", TestContexts.ROOT_CONTEXT))).simplify()); 28 | assertEquals(Expression.FALSE, 29 | and(or(BooleanExpressionScript.script("args.threads < 0", TestContexts.ROOT_CONTEXT))).simplify()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/util/TestConfigurableUtils.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.util; 2 | 3 | import java.nio.file.Path; 4 | 5 | public class TestConfigurableUtils { 6 | public static Path pathToTestResource(String resource) { 7 | return resolve(Path.of("planetiler-custommap", "src", "test", "resources", "validSchema", resource)); 8 | } 9 | 10 | public static Path pathToTestInvalidResource(String resource) { 11 | return resolve(Path.of("planetiler-custommap", "src", "test", "resources", "invalidSchema", resource)); 12 | } 13 | 14 | public static Path pathToSample(String resource) { 15 | return resolve(Path.of("planetiler-custommap", "src", "main", "resources", "samples", resource)); 16 | } 17 | 18 | private static Path resolve(Path pathFromRoot) { 19 | Path cwd = Path.of("").toAbsolutePath(); 20 | return cwd.resolveSibling(pathFromRoot); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/util/VerifyMonaco.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.custommap.util; 2 | 3 | import com.onthegomap.planetiler.mbtiles.Mbtiles; 4 | import com.onthegomap.planetiler.mbtiles.Verify; 5 | import java.io.IOException; 6 | import java.nio.file.Path; 7 | import java.util.Map; 8 | import org.locationtech.jts.geom.Envelope; 9 | import org.locationtech.jts.geom.LineString; 10 | import org.locationtech.jts.geom.Point; 11 | import org.locationtech.jts.geom.Polygon; 12 | 13 | /** 14 | * A utility to check the contents of an mbtiles file generated for Monaco. 15 | */ 16 | public class VerifyMonaco { 17 | 18 | public static final Envelope MONACO_BOUNDS = new Envelope(7.40921, 7.44864, 43.72335, 43.75169); 19 | 20 | /** 21 | * Returns a verification result with a basic set of checks against an openmaptiles map built from an extract for 22 | * Monaco. 23 | */ 24 | public static Verify verify(Mbtiles mbtiles) { 25 | Verify verify = Verify.verify(mbtiles); 26 | verify.checkMinFeatureCount(MONACO_BOUNDS, "building", Map.of(), 13, 14, 100, Polygon.class); 27 | verify.checkMinFeatureCount(MONACO_BOUNDS, "transportation", Map.of(), 10, 14, 5, LineString.class); 28 | verify.checkMinFeatureCount(MONACO_BOUNDS, "landcover", Map.of( 29 | "class", "grass", 30 | "subclass", "park" 31 | ), 14, 10, Polygon.class); 32 | verify.checkMinFeatureCount(MONACO_BOUNDS, "water", Map.of("class", "ocean"), 0, 14, 1, Polygon.class); 33 | verify.checkMinFeatureCount(MONACO_BOUNDS, "place", Map.of("class", "country"), 2, 14, 1, Point.class); 34 | return verify; 35 | } 36 | 37 | public static void main(String[] args) throws IOException { 38 | try (var mbtiles = Mbtiles.newReadOnlyDatabase(Path.of(args[0]))) { 39 | var result = verify(mbtiles); 40 | result.print(); 41 | result.failIfErrors(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/invalidSchema/bad_geometry_type.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | layers: 9 | - id: testLayer 10 | features: 11 | - source: 12 | - osm 13 | geometry: smurf 14 | include_when: 15 | natural: water 16 | attributes: 17 | - key: water 18 | - value: wet 19 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/invalidSchema/invalid_post_process.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | layers: 9 | - id: testLayer 10 | features: 11 | - source: 12 | - osm 13 | geometry: line 14 | include_when: 15 | natural: water 16 | attributes: 17 | - key: water 18 | - value: wet 19 | tile_post_process: 20 | merge_everything: 21 | min_length: 1 22 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/invalidSchema/no_layers.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/data_type_attributes.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | tag_mappings: 9 | b_type: boolean 10 | l_type: long 11 | i_type: integer 12 | d_type: direction 13 | s_type: string 14 | double_type: double 15 | is_intermittent: 16 | input: intermittent 17 | type: boolean 18 | bridge: 19 | type: boolean 20 | layers: 21 | - id: testLayer 22 | features: 23 | - source: 24 | - osm 25 | geometry: line 26 | attributes: 27 | - key: b_type 28 | - key: l_type 29 | - key: i_type 30 | - key: double_type 31 | - key: d_type 32 | - key: s_type 33 | - key: intermittent 34 | - key: is_intermittent 35 | - key: bridge 36 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/local_path.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | local_path: data/rhode-island.osm.pbf 9 | layers: 10 | - id: testLayer 11 | features: 12 | - source: 13 | - osm 14 | geometry: polygon 15 | include_when: 16 | natural: water 17 | attributes: 18 | - key: natural 19 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/road_motorway.yml: -------------------------------------------------------------------------------- 1 | schema_name: OWG Simple Schema 2 | schema_description: Simple vector tile schema 3 | attribution: © 4 | OpenStreetMap contributors 5 | sources: 6 | water_polygons: 7 | type: shapefile 8 | url: https://osmdata.openstreetmap.de/download/water-polygons-split-3857.zip 9 | osm: 10 | type: osm 11 | url: geofabrik:rhode-island 12 | gpkg: 13 | type: geopackage 14 | url: https://example.com/geopackage.gpkg.zip 15 | tag_mappings: 16 | bridge: boolean # input=bridge, output=bridge, type=boolean 17 | layer: long 18 | tunnel: boolean 19 | layers: 20 | - id: road 21 | features: 22 | - source: 23 | - osm 24 | min_zoom: 4 25 | geometry: line 26 | include_when: 27 | highway: motorway 28 | attributes: 29 | - key: highway 30 | - key: bridge 31 | include_when: 32 | bridge: true 33 | min_zoom: 11 34 | - key: tunnel 35 | value: true 36 | include_when: 37 | tunnel: true 38 | min_zoom: 11 39 | - key: name 40 | min_zoom: 12 41 | - key: layer 42 | min_zoom: 13 43 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/static_attribute.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | layers: 9 | - id: testLayer 10 | features: 11 | - source: 12 | - osm 13 | geometry: polygon 14 | include_when: 15 | natural: water 16 | attributes: 17 | - key: natural 18 | value: aTestConstantValue 19 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/tag_attribute.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | layers: 9 | - id: testLayer 10 | features: 11 | - source: 12 | - osm 13 | geometry: polygon 14 | include_when: 15 | natural: water 16 | attributes: 17 | - key: natural 18 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/tag_attribute_null.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | layers: 9 | - id: testLayer 10 | features: 11 | - source: 12 | - osm 13 | geometry: polygon 14 | include_when: 15 | natural: water 16 | attributes: 17 | - key: non_existent 18 | - key: non_existent_typed 19 | type: string 20 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/tag_include.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | layers: 9 | - id: testLayer 10 | features: 11 | - source: 12 | - osm 13 | min_zoom: 10 14 | geometry: polygon 15 | include_when: 16 | natural: water 17 | attributes: 18 | - key: test_include 19 | value: ok 20 | include_when: 21 | natural: water 22 | - key: test_exclude 23 | value: bad 24 | include_when: 25 | natural: mud 26 | - key: test_zoom_tag 27 | min_zoom: 12 28 | -------------------------------------------------------------------------------- /planetiler-custommap/src/test/resources/validSchema/zoom_filter.yml: -------------------------------------------------------------------------------- 1 | schema_name: Test Case Schema 2 | schema_description: Test case tile schema 3 | attribution: Test attribution 4 | sources: 5 | osm: 6 | type: osm 7 | url: geofabrik:rhode-island 8 | tag_mappings: 9 | lanes: long 10 | layers: 11 | - id: testLayer 12 | features: 13 | - source: 14 | - osm 15 | geometry: line 16 | min_zoom: 17 | default_value: 4 18 | overrides: 19 | - value: 5 20 | if: 21 | highway: trunk 22 | - value: 7 23 | if: 24 | highway: primary 25 | include_when: 26 | highway: 27 | attributes: 28 | - key: highway 29 | min_zoom_by_value: 30 | trunk: 5 31 | primary: 7 32 | - key: lanes 33 | min_zoom_by_value: 34 | 4: 9 35 | 3: 9 36 | 2: 10 37 | - key: toll 38 | min_zoom: 8 39 | -------------------------------------------------------------------------------- /planetiler-dist/README.md: -------------------------------------------------------------------------------- 1 | This module creates an executable jar and container distribution of planetiler with 2 | a [thin wrapper](./src/main/java/com/onthegomap/planetiler/Main.java) that delegates to the executable tasks in other 3 | Planetiler modules. 4 | -------------------------------------------------------------------------------- /planetiler-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 4.0.0 9 | 10 | planetiler-examples 11 | 12 | 13 | com.onthegomap.planetiler 14 | planetiler-parent 15 | ${revision} 16 | 17 | 18 | 19 | 20 | com.onthegomap.planetiler 21 | planetiler-core 22 | ${project.parent.version} 23 | 24 | 25 | 26 | 27 | com.onthegomap.planetiler 28 | planetiler-core 29 | ${project.parent.version} 30 | test-jar 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | io.github.zlika 39 | reproducible-build-maven-plugin 40 | 41 | 42 | maven-deploy-plugin 43 | 44 | 45 | true 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /planetiler-examples/src/test/java/com/onthegomap/planetiler/examples/ToiletsOverlayLowLevelApiTest.java: -------------------------------------------------------------------------------- 1 | package com.onthegomap.planetiler.examples; 2 | 3 | import static com.onthegomap.planetiler.TestUtils.assertContains; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | 6 | import com.onthegomap.planetiler.TestUtils; 7 | import com.onthegomap.planetiler.geo.GeoUtils; 8 | import com.onthegomap.planetiler.mbtiles.Mbtiles; 9 | import java.io.IOException; 10 | import java.nio.file.Path; 11 | import java.util.Map; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.io.TempDir; 14 | import org.locationtech.jts.geom.Point; 15 | 16 | class ToiletsOverlayLowLevelApiTest { 17 | 18 | @Test 19 | void integrationTest(@TempDir Path tmpDir) throws IOException { 20 | Path dbPath = tmpDir.resolve("output.mbtiles"); 21 | ToiletsOverlayLowLevelApi.run( 22 | // Override input source locations 23 | TestUtils.pathToResource("monaco-latest.osm.pbf"), 24 | // Override temp dir location 25 | tmpDir, 26 | // Override output location 27 | dbPath 28 | ); 29 | try (Mbtiles mbtiles = Mbtiles.newReadOnlyDatabase(dbPath)) { 30 | Map metadata = mbtiles.metadata().toMap(); 31 | assertEquals("Toilets Overlay", metadata.get("name")); 32 | assertContains("openstreetmap.org/copyright", metadata.get("attribution")); 33 | 34 | TestUtils.assertNumFeatures(mbtiles, "toilets", 14, Map.of(), GeoUtils.WORLD_LAT_LON_BOUNDS, 35 | 34, Point.class); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /scripts/build-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | ./mvnw -B -ntp install jib:dockerBuild -Pflatten 6 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | PROJECT="${1:-planetiler-dist}" 8 | 9 | ./mvnw -DskipTests=true --projects "${PROJECT}" -am clean package 10 | -------------------------------------------------------------------------------- /scripts/check-doc-links.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | find . -name '*.md' -not -path '*/target/*' -not -path '*/planetiler-openmaptiles/*' -not -path '*/data/*' -print0 | xargs -I {} -n 1 -0 markdown-link-check --quiet --config .github/workflows/docs_mlc_config.json {} 8 | -------------------------------------------------------------------------------- /scripts/check-mbtiles.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | java -ea -jar planetiler-dist/target/*-with-deps.jar verify-mbtiles $* 8 | -------------------------------------------------------------------------------- /scripts/check-monaco.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | java -ea -jar planetiler-dist/target/*-with-deps.jar verify-monaco $* 8 | -------------------------------------------------------------------------------- /scripts/fasttests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | ./mvnw -T 1C -Pfast clean test 8 | -------------------------------------------------------------------------------- /scripts/format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | ./mvnw spotless:apply 6 | -------------------------------------------------------------------------------- /scripts/push-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | VERSION="${1:-$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)}" 5 | 6 | TAGS="" 7 | if [ -n "${IMAGE_TAGS:-}" ]; then 8 | TAGS="-Djib.to.tags=${IMAGE_TAGS// /}" 9 | fi 10 | 11 | ./mvnw -B -ntp -DskipTests "${TAGS}" -Pjib-multi-arch \ 12 | -Dimage.version="${VERSION}" \ 13 | -Djib.to.auth.username="${GITHUB_ACTOR}" \ 14 | -Djib.to.auth.password="${GITHUB_TOKEN}" \ 15 | package jib:build --file pom.xml 16 | 17 | ./mvnw -B -Dgpg.passphrase="${OSSRH_GPG_SECRET_KEY_PASSWORD}" -DskipTests -Prelease -Pflatten deploy 18 | -------------------------------------------------------------------------------- /scripts/serve-tiles-docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | set -x 7 | 8 | docker run --rm -it -v "$(git rev-parse --show-toplevel)/data":/data -p 8080:8080 \ 9 | maptiler/tileserver-gl -p 8080 10 | -------------------------------------------------------------------------------- /scripts/set-versions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | if (( $# != 1 )); then 6 | echo "Usage: set_versions.sh " >&2 7 | exit 1 8 | fi 9 | 10 | version="$1" 11 | 12 | ./mvnw -B -ntp versions:set-property versions:commit -Dproperty="revision" -DnewVersion="${version}" 13 | ./mvnw -B -ntp versions:set-property versions:commit -Dproperty="planetiler.version" -DnewVersion="${version}" -f planetiler-examples/standalone.pom.xml 14 | -------------------------------------------------------------------------------- /scripts/sonar.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | mvn verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pcoverage 6 | -------------------------------------------------------------------------------- /scripts/test-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -exuo pipefail 4 | 5 | version="${1:-$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)}" 6 | 7 | if [ "${SKIP_EXAMPLE_PROJECT:-false}" == "true" ]; then 8 | echo "skipping example project" 9 | else 10 | echo "::group::Test building example project" 11 | (cd planetiler-examples && mvn -B -ntp -Dplanetiler.version="${version}" verify --file standalone.pom.xml) 12 | echo "::endgroup::" 13 | fi 14 | 15 | echo "Test java build" 16 | echo "::group::OpenMapTiles monaco (java)" 17 | rm -f data/out*.mbtiles 18 | # vary threads to stress-test determinism check 19 | java -jar planetiler-dist/target/*with-deps.jar --download --area=monaco --output=data/jar-monaco.mbtiles --threads=32 20 | ./scripts/check-monaco.sh data/jar-monaco.mbtiles 21 | echo "::endgroup::" 22 | echo "::group::Example (java)" 23 | java -jar planetiler-dist/target/*with-deps.jar example-toilets --download --area=monaco --output=data/jar-example.mbtiles 24 | ./scripts/check-mbtiles.sh data/jar-example.mbtiles 25 | echo "::endgroup::" 26 | 27 | echo "::endgroup::" 28 | echo "::group::OpenMapTiles monaco (docker)" 29 | # vary threads to stress-test determinism check 30 | docker run -v "$(pwd)/data":/data ghcr.io/onthegomap/planetiler:"${version}" --area=monaco --output=data/docker-monaco.mbtiles --threads=4 31 | ./scripts/check-monaco.sh data/docker-monaco.mbtiles 32 | echo "::endgroup::" 33 | echo "::group::Example (docker)" 34 | docker run -v "$(pwd)/data":/data ghcr.io/onthegomap/planetiler:"${version}" example-toilets --area=monaco --output=data/docker-example.mbtiles 35 | ./scripts/check-mbtiles.sh data/docker-example.mbtiles 36 | echo "::endgroup::" 37 | 38 | echo "::group::Compare" 39 | java -jar planetiler-dist/target/*with-deps.jar compare data/jar-monaco.mbtiles data/docker-monaco.mbtiles 40 | echo "::endgroup::" 41 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.issue.ignore.multicriteria=js1659,js3358,js106,js125,js2699,js3776,js1121,js107,js1192 2 | # subjective 3 | sonar.issue.ignore.multicriteria.js1659.ruleKey=java:S1659 4 | sonar.issue.ignore.multicriteria.js1659.resourceKey=**/*.java 5 | sonar.issue.ignore.multicriteria.js3358.ruleKey=java:S3358 6 | sonar.issue.ignore.multicriteria.js3358.resourceKey=**/*.java 7 | sonar.issue.ignore.multicriteria.js106.ruleKey=java:S106 8 | sonar.issue.ignore.multicriteria.js106.resourceKey=**/*.java 9 | sonar.issue.ignore.multicriteria.js125.ruleKey=java:S125 10 | sonar.issue.ignore.multicriteria.js125.resourceKey=**/*.java 11 | sonar.issue.ignore.multicriteria.js3776.ruleKey=java:S3776 12 | sonar.issue.ignore.multicriteria.js3776.resourceKey=**/*.java 13 | sonar.issue.ignore.multicriteria.js1121.ruleKey=java:S1121 14 | sonar.issue.ignore.multicriteria.js1121.resourceKey=**/*.java 15 | sonar.issue.ignore.multicriteria.js107.ruleKey=java:S107 16 | sonar.issue.ignore.multicriteria.js107.resourceKey=**/*.java 17 | sonar.issue.ignore.multicriteria.js1192.ruleKey=java:S1192 18 | sonar.issue.ignore.multicriteria.js1192.resourceKey=**/*.java 19 | 20 | # tests use assertion helpers 21 | sonar.issue.ignore.multicriteria.js2699.ruleKey=java:S2699 22 | sonar.issue.ignore.multicriteria.js2699.resourceKey=**/*.java 23 | --------------------------------------------------------------------------------