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<