simplify and improve performance of node map

pull/1/head
Mike Barry 2021-08-22 05:37:57 -04:00
rodzic 61eb58f427
commit 15a3659db6
39 zmienionych plików z 796 dodań i 1006 usunięć

Wyświetl plik

@ -16,10 +16,10 @@ The `flatmap-core` module includes the following software:
- org.geotools:gt-epsg-hsql
(LGPL, [BSD for HSQL](https://github.com/geotools/geotools/blob/main/licenses/HSQL.md)
, [EPSG](https://github.com/geotools/geotools/blob/main/licenses/EPSG.md))
- org.mapdb:mapdb (Apache license)
- org.msgpack:msgpack-core (Apache license)
- org.xerial:sqlite-jdbc (Apache license)
- com.ibm.icu:icu4j ([ICU license](https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE))
- com.google.guava:guava (Apache license)
- Adapted code:
- `DouglasPeuckerSimplifier` from [JTS](https://github.com/locationtech/jts) (EDL)
- `OsmMultipolygon` from [imposm3](https://github.com/omniscale/imposm3) (Apache license)

Wyświetl plik

@ -24,4 +24,44 @@
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<!-- for fatjar assembly descriptor -->
<dependencies>
<dependency>
<groupId>com.onthegomap</groupId>
<artifactId>flatmap-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>fatjar</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Wyświetl plik

@ -4,7 +4,6 @@ import static io.prometheus.client.Collector.NANOSECONDS_PER_SECOND;
import com.onthegomap.flatmap.collection.LongLongMap;
import com.onthegomap.flatmap.stats.Counter;
import com.onthegomap.flatmap.stats.ProcessInfo;
import com.onthegomap.flatmap.stats.ProgressLoggers;
import com.onthegomap.flatmap.stats.Stats;
import com.onthegomap.flatmap.util.FileUtils;
@ -21,20 +20,9 @@ public class LongLongMapBench {
public static void main(String[] args) throws InterruptedException {
Path path = Path.of("./llmaptest");
FileUtils.delete(path);
LongLongMap map = switch (args[0]) {
case "sparsemem2" -> LongLongMap.newInMemorySparseArray2();
case "sparsearraymemory" -> LongLongMap.newInMemorySparseArray();
case "hppc" -> new LongLongMap.HppcMap();
case "array" -> new LongLongMap.Array();
case "sparse2" -> LongLongMap.newFileBackedSparseArray2(path);
case "sqlite" -> LongLongMap.newSqlite(path);
case "sparsearray" -> LongLongMap.newFileBackedSparseArray(path);
case "mapdb" -> LongLongMap.newFileBackedSortedTable(path);
default -> throw new IllegalStateException("Unexpected value: " + args[0]);
};
long entries = Long.parseLong(args[1]);
int readers = Integer.parseInt(args[2]);
LongLongMap map = LongLongMap.from(args[0], args[1], path);
long entries = Long.parseLong(args[2]);
int readers = Integer.parseInt(args[3]);
class LocalCounter {
@ -59,7 +47,8 @@ public class LongLongMapBench {
}).awaitAndLog(loggers, Duration.ofSeconds(10));
map.get(1);
System.err.println("Storage: " + Format.formatStorage(map.fileSize(), false));
System.err.println("Storage: " + Format.formatStorage(map.bytesOnDisk(), false));
System.err.println("RAM: " + Format.formatStorage(map.estimateMemoryUsageBytes(), false));
Counter.Readable readCount = Counter.newMultiThreadCounter();
loggers = new ProgressLoggers("read")
@ -110,13 +99,14 @@ public class LongLongMapBench {
args[1],
args[2],
args[3],
Format.formatStorage(ProcessInfo.getMaxMemoryBytes(), false),
Format.formatStorage(map.fileSize(), false),
Format.formatStorage(map.estimateMemoryUsageBytes(), false),
Format.formatStorage(map.bytesOnDisk(), false),
Format.formatStorage(FileUtils.size(path), false),
writeRate.get(),
readRate
)
);
FileUtils.delete(path);
Thread.sleep(100);
System.exit(0);
}

Wyświetl plik

@ -45,11 +45,6 @@
<artifactId>sqlite-jdbc</artifactId>
<version>3.34.0</version>
</dependency>
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack-core</artifactId>
@ -120,6 +115,11 @@
<artifactId>icu4j</artifactId>
<version>69.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
</dependencies>
<build>

Wyświetl plik

@ -34,13 +34,13 @@ public class FlatMapRunner {
private final Timers.Finishable overallTimer;
private final Arguments arguments;
private Stats stats;
private final Stats stats;
private Profile profile = null;
private CommonParams config;
private FeatureSort featureDb;
private FeatureGroup featureMap;
private OsmInputFile osmInputFile;
private Path tmpDir;
private final Path tmpDir;
private Path output;
private boolean overwrite = false;
private boolean ran = false;
@ -59,16 +59,7 @@ public class FlatMapRunner {
}
private LongLongMap getLongLongMap() {
return switch (config.longLongMap()) {
case "mapdb" -> LongLongMap.newFileBackedSortedTable(nodeDbPath);
case "sparsearray" -> LongLongMap.newFileBackedSparseArray(nodeDbPath);
case "sparsemem2" -> LongLongMap.newInMemorySparseArray2();
case "sparse2" -> LongLongMap.newFileBackedSparseArray2(nodeDbPath);
case "ramsparsearray" -> LongLongMap.newInMemorySparseArray();
case "ramarray" -> LongLongMap.newArrayBacked();
case "sqlite" -> LongLongMap.newSqlite(nodeDbPath);
default -> throw new IllegalStateException("Unexpected llmap value: " + config.longLongMap());
};
return LongLongMap.from(config.nodeMapType(), config.nodeMapStorage(), nodeDbPath);
}
public FlatMapRunner addOsmSource(String name, Path defaultPath) {

Wyświetl plik

@ -0,0 +1,101 @@
package com.onthegomap.flatmap.collection;
import com.onthegomap.flatmap.util.DiskBacked;
import com.onthegomap.flatmap.util.MemoryEstimator;
import java.io.Closeable;
import java.io.IOException;
import java.util.function.IntFunction;
import java.util.stream.Stream;
interface AppendStore extends Closeable, MemoryEstimator.HasEstimate, DiskBacked {
long size();
default void checkIndex(long index) {
if (index >= size()) {
throw new IndexOutOfBoundsException("index: " + index + " size: " + size());
}
}
@Override
default long estimateMemoryUsageBytes() {
return 0;
}
@Override
default long bytesOnDisk() {
return 0;
}
interface Ints extends AppendStore {
void writeInt(int value);
int getInt(long index);
}
interface Longs extends AppendStore {
void writeLong(long value);
long getLong(long index);
}
final class SmallLongs implements Longs {
private static final int BITS = 31;
private static final long MASK = (1L << BITS) - 1L;
private final Ints[] ints = new Ints[10];
private long numWritten = 0;
SmallLongs(IntFunction<Ints> supplier) {
for (int i = 0; i < ints.length; i++) {
ints[i] = supplier.apply(i);
}
}
@Override
public void writeLong(long value) {
int block = (int) (value >>> BITS);
int offset = (int) (value & MASK);
ints[block].writeInt(offset);
numWritten++;
}
@Override
public long getLong(long index) {
for (int i = 0; i < ints.length; i++) {
Ints slab = ints[i];
long size = slab.size();
if (index < slab.size()) {
return slab.getInt(index) + (((long) i) << BITS);
}
index -= size;
}
throw new ArrayIndexOutOfBoundsException("index: " + index + " size: " + size());
}
@Override
public long size() {
return numWritten;
}
@Override
public void close() throws IOException {
for (var child : ints) {
child.close();
}
}
@Override
public long estimateMemoryUsageBytes() {
return Stream.of(ints).mapToLong(AppendStore::estimateMemoryUsageBytes).sum();
}
@Override
public long bytesOnDisk() {
return Stream.of(ints).mapToLong(AppendStore::bytesOnDisk).sum();
}
}
}

Wyświetl plik

@ -0,0 +1,158 @@
package com.onthegomap.flatmap.collection;
import com.onthegomap.flatmap.util.FileUtils;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
abstract class AppendStoreMmap implements AppendStore {
final DataOutputStream outputStream;
final int segmentBits;
final long segmentMask;
final long segmentBytes;
private final Path path;
long outIdx = 0;
private volatile MappedByteBuffer[] segments;
private volatile FileChannel channel;
AppendStoreMmap(Path path) {
this(path, 1 << 20); // 1MB
}
AppendStoreMmap(Path path, long segmentSizeBytes) {
segmentBits = (int) (Math.log(segmentSizeBytes) / Math.log(2));
segmentMask = (1L << segmentBits) - 1;
segmentBytes = segmentSizeBytes;
if (segmentSizeBytes % 8 != 0 || (1L << segmentBits != segmentSizeBytes)) {
throw new IllegalArgumentException("segment size must be a multiple of 8 and power of 2: " + segmentSizeBytes);
}
this.path = path;
try {
this.outputStream = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(path), 50_000));
} catch (IOException e) {
throw new IllegalStateException("Could not create SequentialWriteRandomReadFile output stream", e);
}
}
MappedByteBuffer[] getSegments() {
MappedByteBuffer[] result = segments;
if (result == null) {
synchronized (this) {
if ((result = segments) == null) {
try {
outputStream.close();
channel = FileChannel.open(path, StandardOpenOption.READ);
int segmentCount = (int) (outIdx / segmentBytes + 1);
result = new MappedByteBuffer[segmentCount];
int i = 0;
for (long segmentStart = 0; segmentStart < outIdx; segmentStart += segmentBytes) {
long segmentEnd = Math.min(segmentBytes, outIdx - segmentStart);
result[i++] = channel.map(FileChannel.MapMode.READ_ONLY, segmentStart, segmentEnd);
}
segments = result;
} catch (IOException e) {
throw new IllegalStateException("Failed preparing SequentialWriteRandomReadFile for reads", e);
}
}
}
}
return result;
}
@Override
public void close() throws IOException {
outputStream.close();
synchronized (this) {
if (channel != null) {
channel.close();
}
if (segments != null) {
Arrays.fill(segments, null);
}
}
}
@Override
public long bytesOnDisk() {
return FileUtils.size(path);
}
static class Ints extends AppendStoreMmap implements AppendStore.Ints {
Ints(Path path) {
super(path);
}
Ints(Path path, long segmentSizeBytes) {
super(path, segmentSizeBytes);
}
@Override
public void writeInt(int value) {
try {
outputStream.writeInt(value);
outIdx += 4;
} catch (IOException e) {
throw new IllegalStateException("Error writing int", e);
}
}
@Override
public int getInt(long index) {
checkIndex(index);
MappedByteBuffer[] segments = getSegments();
long byteOffset = index << 2;
int idx = (int) (byteOffset >>> segmentBits);
int offset = (int) (byteOffset & segmentMask);
return segments[idx].getInt(offset);
}
@Override
public long size() {
return outIdx >>> 2;
}
}
static class Longs extends AppendStoreMmap implements AppendStore.Longs {
Longs(Path path) {
super(path);
}
Longs(Path path, long segmentSizeBytes) {
super(path, segmentSizeBytes);
}
@Override
public void writeLong(long value) {
try {
outputStream.writeLong(value);
outIdx += 8;
} catch (IOException e) {
throw new IllegalStateException("Error writing long", e);
}
}
@Override
public long getLong(long index) {
checkIndex(index);
MappedByteBuffer[] segments = getSegments();
long byteOffset = index << 3;
int idx = (int) (byteOffset >>> segmentBits);
int offset = (int) (byteOffset & segmentMask);
return segments[idx].getLong(offset);
}
@Override
public long size() {
return outIdx >>> 3;
}
}
}

Wyświetl plik

@ -0,0 +1,116 @@
package com.onthegomap.flatmap.collection;
import java.util.ArrayList;
import java.util.List;
abstract class AppendStoreRam<T> implements AppendStore {
final List<T> arrays;
long size = 0;
final int slabSize;
final int slabBits;
final long slabMask;
AppendStoreRam(int segmentSizeBytes) {
this.slabBits = (int) (Math.log(segmentSizeBytes) / Math.log(2));
if (1 << slabBits != segmentSizeBytes) {
throw new IllegalArgumentException("Segment size must be a power of 2: " + segmentSizeBytes);
}
this.slabSize = (1 << slabBits);
this.slabMask = slabSize - 1;
this.arrays = new ArrayList<>();
}
T writeSlab() {
long idx = size++;
int slabIdx = (int) (idx >>> slabBits);
while (arrays.size() <= slabIdx) {
arrays.add(newSlab());
}
return arrays.get(slabIdx);
}
abstract T newSlab();
@Override
public long size() {
return size;
}
@Override
public void close() {
arrays.clear();
}
static class Ints extends AppendStoreRam<int[]> implements AppendStore.Ints {
Ints() {
this(1 << 20); // 1MB
}
Ints(int segmentSizeBytes) {
super(segmentSizeBytes >>> 2);
}
@Override
int[] newSlab() {
return new int[slabSize];
}
@Override
public void writeInt(int value) {
int offset = (int) (size & slabMask);
writeSlab()[offset] = value;
}
@Override
public int getInt(long index) {
checkIndex(index);
int slabIdx = (int) (index >>> slabBits);
int offset = (int) (index & slabMask);
int[] slab = arrays.get(slabIdx);
return slab[offset];
}
@Override
public long estimateMemoryUsageBytes() {
return arrays.size() * (slabSize * 4L + 24L + 8);
}
}
static class Longs extends AppendStoreRam<long[]> implements AppendStore.Longs {
Longs() {
this(1 << 20); // 1MB
}
Longs(int segmentSizeBytes) {
super(segmentSizeBytes >>> 3);
}
@Override
protected long[] newSlab() {
return new long[slabSize];
}
@Override
public void writeLong(long value) {
int offset = (int) (size & slabMask);
writeSlab()[offset] = value;
}
@Override
public long getLong(long index) {
checkIndex(index);
int slabIdx = (int) (index >>> slabBits);
int offset = (int) (index & slabMask);
return arrays.get(slabIdx)[offset];
}
@Override
public long estimateMemoryUsageBytes() {
return arrays.size() * (slabSize * 8L + 24L + 8);
}
}
}

Wyświetl plik

@ -27,7 +27,6 @@ import java.util.function.Supplier;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -116,10 +115,15 @@ class ExternalMergeSort implements FeatureSort {
}
@Override
public long getStorageSize() {
public long bytesOnDisk() {
return FileUtils.directorySize(dir);
}
@Override
public long estimateMemoryUsageBytes() {
return 0;
}
private static <T> T time(AtomicLong timer, Supplier<T> func) {
long start = System.nanoTime();
try {
@ -156,7 +160,7 @@ class ExternalMergeSort implements FeatureSort {
ProgressLoggers loggers = new ProgressLoggers("sort")
.addPercentCounter("chunks", chunks.size(), doneCounter)
.addFileSize(this::getStorageSize)
.addFileSize(this)
.newLine()
.addProcessStats()
.newLine()
@ -176,7 +180,7 @@ class ExternalMergeSort implements FeatureSort {
return features.get();
}
@NotNull
@Override
public Iterator<Entry> iterator() {
assert sorted;
@ -357,7 +361,7 @@ class ExternalMergeSort implements FeatureSort {
}
@Override
public int compareTo(@NotNull PeekableScanner o) {
public int compareTo(PeekableScanner o) {
return next.compareTo(o.next);
}
}

Wyświetl plik

@ -10,6 +10,7 @@ import com.onthegomap.flatmap.geo.TileCoord;
import com.onthegomap.flatmap.render.RenderedFeature;
import com.onthegomap.flatmap.stats.Stats;
import com.onthegomap.flatmap.util.CommonStringEncoder;
import com.onthegomap.flatmap.util.DiskBacked;
import com.onthegomap.flatmap.util.LayerStats;
import java.io.IOException;
import java.util.ArrayList;
@ -32,7 +33,8 @@ import org.msgpack.value.ValueFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class FeatureGroup implements Consumer<FeatureSort.Entry>, Iterable<FeatureGroup.TileFeatures> {
public final class FeatureGroup implements Consumer<FeatureSort.Entry>, Iterable<FeatureGroup.TileFeatures>,
DiskBacked {
public static final int Z_ORDER_BITS = 23;
public static final int Z_ORDER_MAX = (1 << (Z_ORDER_BITS - 1)) - 1;
@ -277,8 +279,9 @@ public final class FeatureGroup implements Consumer<FeatureSort.Entry>, Iterable
};
}
public long getStorageSize() {
return sorter.getStorageSize();
@Override
public long bytesOnDisk() {
return sorter.bytesOnDisk();
}
public FeatureSort sorter() {

Wyświetl plik

@ -2,15 +2,16 @@ package com.onthegomap.flatmap.collection;
import com.onthegomap.flatmap.config.CommonParams;
import com.onthegomap.flatmap.stats.Stats;
import com.onthegomap.flatmap.util.DiskBacked;
import com.onthegomap.flatmap.util.MemoryEstimator;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.jetbrains.annotations.NotNull;
public interface FeatureSort extends Iterable<FeatureSort.Entry> {
public interface FeatureSort extends Iterable<FeatureSort.Entry>, DiskBacked, MemoryEstimator.HasEstimate {
static FeatureSort newExternalMergeSort(Path tempDir, CommonParams config, Stats stats) {
return new ExternalMergeSort(tempDir, config, stats);
@ -40,11 +41,16 @@ public interface FeatureSort extends Iterable<FeatureSort.Entry> {
}
@Override
public long getStorageSize() {
public long estimateMemoryUsageBytes() {
return 0;
}
@NotNull
@Override
public long bytesOnDisk() {
return 0;
}
@Override
public Iterator<Entry> iterator() {
return list.iterator();
@ -66,12 +72,10 @@ public interface FeatureSort extends Iterable<FeatureSort.Entry> {
void add(Entry newEntry);
long getStorageSize();
record Entry(long sortKey, byte[] value) implements Comparable<Entry> {
@Override
public int compareTo(@NotNull Entry o) {
public int compareTo(Entry o) {
return Long.compare(sortKey, o.sortKey);
}

Wyświetl plik

@ -15,11 +15,10 @@ public record CommonParams(
boolean deferIndexCreation,
boolean optimizeDb,
boolean emitTilesInOrder,
int mbtilesFeatureMultiplier,
int mbtilesMinTilesPerBatch,
boolean forceOverwrite,
boolean gzipTempStorage,
String longLongMap,
String nodeMapType,
String nodeMapStorage,
// computed
Envelope worldBounds,
@ -35,11 +34,10 @@ public record CommonParams(
boolean deferIndexCreation,
boolean optimizeDb,
boolean emitTilesInOrder,
int mbtilesFeatureMultiplier,
int mbtilesMinTilesPerBatch,
boolean forceOverwrite,
boolean gzipTempStorage,
String longLongMap
String nodeMapType,
String nodeMapStorage
) {
this(
latLonBounds,
@ -50,11 +48,10 @@ public record CommonParams(
deferIndexCreation,
optimizeDb,
emitTilesInOrder,
mbtilesFeatureMultiplier,
mbtilesMinTilesPerBatch,
forceOverwrite,
gzipTempStorage,
longLongMap,
nodeMapType,
nodeMapStorage,
// computed
GeoUtils.toWorldBounds(latLonBounds),
@ -95,11 +92,10 @@ public record CommonParams(
arguments.get("defer_mbtiles_index_creation", "add index to mbtiles file after finished writing", false),
arguments.get("optimize_db", "optimize mbtiles after writing", false),
arguments.get("emit_tiles_in_order", "emit tiles in index order", true),
arguments.integer("mbtiles_feature_multiplier", "mbtiles feature multiplier", 100),
arguments.integer("mbtiles_min_tiles_per_batch", "min tiles per batch", 1),
arguments.get("force", "force overwriting output file", false),
arguments.get("gzip_temp", "gzip temporary feature storage (uses more CPU, but less disk space)", false),
arguments.get("llmap", "type of long long map", "mapdb")
arguments.get("nodemap_type", "type of node location map: noop, sortedtable, or sparsearray", "sortedtable"),
arguments.get("nodemap_storage", "storage for location map: mmap or ram", "mmap")
);
}
}

Wyświetl plik

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.algorithm.Area;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateSequence;
@ -357,7 +356,7 @@ public class GeoUtils {
}
@Override
public int compareTo(@NotNull PolyAndArea o) {
public int compareTo(PolyAndArea o) {
return -Double.compare(area, o.area);
}
}

Wyświetl plik

@ -3,7 +3,6 @@ package com.onthegomap.flatmap.geo;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.Point;
@ -48,7 +47,7 @@ public class PolygonIndex<T> {
return result.isEmpty() ? null : result.get(0);
}
@NotNull
private List<T> getContaining(Point point, List<?> items) {
List<T> result = new ArrayList<>(items.size());
for (int i = 0; i < items.size(); i++) {

Wyświetl plik

@ -1,7 +1,6 @@
package com.onthegomap.flatmap.geo;
import java.text.NumberFormat;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateXY;
@ -83,7 +82,7 @@ public record TileCoord(int encoded, int x, int y, int z) implements Comparable<
}
@Override
public int compareTo(@NotNull TileCoord o) {
public int compareTo(TileCoord o) {
return Long.compare(encoded, o.encoded);
}

Wyświetl plik

@ -28,7 +28,6 @@ import java.util.OptionalInt;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.slf4j.Logger;
@ -514,7 +513,7 @@ public final class Mbtiles implements Closeable {
}
@Override
public int compareTo(@NotNull TileEntry o) {
public int compareTo(TileEntry o) {
return tile.compareTo(o.tile);
}
}

Wyświetl plik

@ -10,6 +10,7 @@ import com.onthegomap.flatmap.geo.TileCoord;
import com.onthegomap.flatmap.stats.Counter;
import com.onthegomap.flatmap.stats.ProgressLoggers;
import com.onthegomap.flatmap.stats.Stats;
import com.onthegomap.flatmap.util.DiskBacked;
import com.onthegomap.flatmap.util.FileUtils;
import com.onthegomap.flatmap.util.Format;
import com.onthegomap.flatmap.util.LayerStats;
@ -30,7 +31,6 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.function.Consumer;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@ -90,7 +90,7 @@ public class MbtilesWriter {
}
}
public static void writeOutput(FeatureGroup features, Mbtiles output, LongSupplier fileSize, Profile profile,
public static void writeOutput(FeatureGroup features, Mbtiles output, DiskBacked fileSize, Profile profile,
CommonParams config, Stats stats) {
var timer = stats.startStage("mbtiles");
MbtilesWriter writer = new MbtilesWriter(features, output, config, profile, stats,
@ -130,7 +130,7 @@ public class MbtilesWriter {
.addRatePercentCounter("features", features.numFeatures(), writer.featuresProcessed)
.addRateCounter("tiles", writer::tilesEmitted)
.addFileSize(fileSize)
.add(" features ").addFileSize(features::getStorageSize)
.add(" features ").addFileSize(features)
.newLine()
.addProcessStats()
.newLine()

Wyświetl plik

@ -12,7 +12,6 @@ import com.onthegomap.flatmap.worker.WorkerPipeline;
import java.io.Closeable;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.Envelope;
public abstract class Reader implements Closeable {
@ -63,7 +62,7 @@ public abstract class Reader implements Closeable {
var loggers = new ProgressLoggers(sourceName)
.addRatePercentCounter("read", featureCount, featuresRead)
.addRateCounter("write", featuresWritten)
.addFileSize(writer::getStorageSize)
.addFileSize(writer)
.newLine()
.addProcessStats()
.newLine()
@ -78,7 +77,7 @@ public abstract class Reader implements Closeable {
timer.stop();
}
@NotNull
private FeatureRenderer getFeatureRenderer(FeatureGroup writer, CommonParams config,
Consumer<FeatureSort.Entry> next) {
var encoder = writer.newRenderedFeatureEncoder();

Wyświetl plik

@ -37,7 +37,6 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateList;
import org.locationtech.jts.geom.CoordinateSequence;
@ -105,7 +104,7 @@ public class OsmReader implements Closeable, MemoryEstimator.HasEstimate {
var loggers = new ProgressLoggers("osm_pass1")
.addRateCounter("nodes", PASS1_NODES, true)
.addFileSize(nodeDb::fileSize)
.addFileSize(nodeDb)
.addRateCounter("ways", PASS1_WAYS, true)
.addRateCounter("rels", PASS1_RELATIONS, true)
.newLine()
@ -214,11 +213,11 @@ public class OsmReader implements Closeable, MemoryEstimator.HasEstimate {
var logger = new ProgressLoggers("osm_pass2")
.addRatePercentCounter("nodes", PASS1_NODES.get(), nodesProcessed)
.addFileSize(nodeDb::fileSize)
.addFileSize(nodeDb)
.addRatePercentCounter("ways", PASS1_WAYS.get(), waysProcessed)
.addRatePercentCounter("rels", PASS1_RELATIONS.get(), relsProcessed)
.addRateCounter("features", () -> writer.sorter().size())
.addFileSize(writer::getStorageSize)
.addFileSize(writer)
.newLine()
.addProcessStats()
.addInMemoryObject("hppc", this)
@ -267,7 +266,6 @@ public class OsmReader implements Closeable, MemoryEstimator.HasEstimate {
return new WaySourceFeature(way, closed, area, nodeCache, rels);
}
@Nullable
private List<RelationMember<RelationInfo>> getRelationMembership(long id) {
LongArrayList relationIds = wayToRelations.get(id);
List<RelationMember<RelationInfo>> rels = null;

Wyświetl plik

@ -4,7 +4,6 @@ import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.geo.GeometryException;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.algorithm.Area;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequences;
@ -82,7 +81,7 @@ class CoordinateSequenceExtractor {
return GeoUtils.combineLineStrings(lineStrings);
}
@NotNull
static Geometry reassemblePolygons(List<List<CoordinateSequence>> groups) throws GeometryException {
int numGeoms = groups.size();
if (numGeoms == 1) {

Wyświetl plik

@ -3,6 +3,7 @@ package com.onthegomap.flatmap.stats;
import static com.onthegomap.flatmap.util.Format.*;
import com.graphhopper.util.Helper;
import com.onthegomap.flatmap.util.DiskBacked;
import com.onthegomap.flatmap.util.Format;
import com.onthegomap.flatmap.util.MemoryEstimator;
import com.onthegomap.flatmap.worker.WorkQueue;
@ -185,8 +186,8 @@ public class ProgressLoggers {
};
}
public ProgressLoggers addFileSize(LongSupplier longSupplier) {
loggers.add(string(() -> " " + padRight(formatBytes(longSupplier.getAsLong(), false), 5)));
public ProgressLoggers addFileSize(DiskBacked longSupplier) {
loggers.add(string(() -> " " + padRight(formatBytes(longSupplier.bytesOnDisk(), false), 5)));
return this;
}

Wyświetl plik

@ -297,7 +297,7 @@ public class PrometheusStats implements Stats {
this.osBean = ManagementFactory.getOperatingSystemMXBean();
}
private Map<Long, ProcessInfo.ThreadState> threads = new ConcurrentSkipListMap<>();
private final Map<Long, ProcessInfo.ThreadState> threads = new ConcurrentSkipListMap<>();
public List<MetricFamilySamples> collect() {

Wyświetl plik

@ -0,0 +1,6 @@
package com.onthegomap.flatmap.util;
public interface DiskBacked {
long bytesOnDisk();
}

Wyświetl plik

@ -80,6 +80,14 @@ public class FileUtils {
}
}
public static void createDirectory(Path path) {
try {
Files.createDirectories(path);
} catch (IOException e) {
throw new IllegalStateException("Unable to create directories " + path, e);
}
}
public static void createParentDirectories(Path path) {
try {
if (Files.isDirectory(path)) {

Wyświetl plik

@ -1,5 +1,7 @@
package com.onthegomap.flatmap.util;
import com.carrotsearch.hppc.ByteArrayList;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntObjectHashMap;
import com.carrotsearch.hppc.LongArrayList;
import com.carrotsearch.hppc.LongHashSet;
@ -37,6 +39,14 @@ public class MemoryEstimator {
return object == null ? 0 : (24L + 8L * object.buffer.length);
}
public static long size(IntArrayList object) {
return object == null ? 0 : (24L + 4L * object.buffer.length);
}
public static long size(ByteArrayList object) {
return object == null ? 0 : (24L + object.buffer.length);
}
public static long size(String string) {
return string == null ? 0 : 54 + string.getBytes().length;
}

Wyświetl plik

@ -47,7 +47,7 @@ public interface ZoomFunction<T> extends IntFunction<T> {
class MeterThresholds implements ZoomFunction<Number> {
private TreeMap<Integer, Number> levels = new TreeMap<>();
private final TreeMap<Integer, Number> levels = new TreeMap<>();
public MeterThresholds put(int zoom, double meters) {
levels.put(zoom, GeoUtils.metersToPixelAtEquator(zoom, meters));

Wyświetl plik

@ -14,7 +14,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,7 +37,7 @@ public class Worker {
}
@Override
public Thread newThread(@NotNull Runnable r) {
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);

Wyświetl plik

@ -0,0 +1,118 @@
package com.onthegomap.flatmap.collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.nio.file.Path;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class AppendStoreTest {
static abstract class IntsTest {
protected AppendStore.Ints store;
@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
public void writeThenRead(int num) {
for (int i = 0; i < num; i++) {
store.writeInt(i + 1);
}
for (int i = 0; i < num; i++) {
assertEquals(i + 1, store.getInt(i));
}
assertThrows(IndexOutOfBoundsException.class, () -> store.getInt(num));
assertThrows(IndexOutOfBoundsException.class, () -> store.getInt(num + 1));
}
@Test
public void readBig() {
store.writeInt(Integer.MAX_VALUE);
store.writeInt(Integer.MAX_VALUE - 1);
store.writeInt(Integer.MAX_VALUE - 2);
assertEquals(Integer.MAX_VALUE, store.getInt(0));
assertEquals(Integer.MAX_VALUE - 1, store.getInt(1));
assertEquals(Integer.MAX_VALUE - 2, store.getInt(2));
}
}
static abstract class LongsTest {
protected AppendStore.Longs store;
@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
public void writeThenRead(int num) {
for (int i = 0; i < num; i++) {
store.writeLong(i + 1);
}
for (int i = 0; i < num; i++) {
assertEquals(i + 1, store.getLong(i));
}
assertThrows(IndexOutOfBoundsException.class, () -> store.getLong(num));
assertThrows(IndexOutOfBoundsException.class, () -> store.getLong(num + 1));
}
private static final long maxInt = Integer.MAX_VALUE;
@ParameterizedTest
@ValueSource(longs = {maxInt - 1, maxInt, maxInt + 1, 2 * maxInt - 1, 2 * maxInt, 5 * maxInt - 1, 5 * maxInt + 1})
public void readBig(long value) {
store.writeLong(value);
assertEquals(value, store.getLong(0));
}
}
static class RamInt extends IntsTest {
@BeforeEach
public void setup() {
this.store = new AppendStoreRam.Ints(4 << 2);
}
}
static class MMapInt extends IntsTest {
@BeforeEach
public void setup(@TempDir Path path) {
this.store = new AppendStoreMmap.Ints(path.resolve("ints"), 4 << 2);
}
}
static class RamLong extends LongsTest {
@BeforeEach
public void setup() {
this.store = new AppendStoreRam.Longs(4 << 2);
}
}
static class MMapLong extends LongsTest {
@BeforeEach
public void setup(@TempDir Path path) {
this.store = new AppendStoreMmap.Longs(path.resolve("longs"), 4 << 2);
}
}
static class MMapSmallLong extends LongsTest {
@BeforeEach
public void setup(@TempDir Path path) {
this.store = new AppendStore.SmallLongs((i) -> new AppendStoreMmap.Ints(path.resolve("smalllongs" + i), 4 << 2));
}
}
static class RamSmallLong extends LongsTest {
@BeforeEach
public void setup() {
this.store = new AppendStore.SmallLongs((i) -> new AppendStoreRam.Ints(4 << 2));
}
}
}

Wyświetl plik

@ -3,10 +3,8 @@ package com.onthegomap.flatmap.collection;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.nio.file.Path;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public abstract class LongLongMapTest {
@ -72,67 +70,24 @@ public abstract class LongLongMapTest {
assertArrayEquals(expected, result);
}
public static class SortedTableFileTest extends LongLongMapTest {
@BeforeEach
public void setup(@TempDir Path dir) {
this.map = LongLongMap.newFileBackedSortedTable(dir.resolve("test-node-db-sorted"));
}
}
public static class SortedTableMemoryTest extends LongLongMapTest {
public static class SortedTable extends LongLongMapTest {
@BeforeEach
public void setup() {
this.map = LongLongMap.newInMemorySortedTable();
this.map = new LongLongMap.SortedTable(
new AppendStore.SmallLongs(
i -> new AppendStoreRam.Ints()
),
new AppendStoreRam.Longs()
);
}
}
public static class SparseArrayTest extends LongLongMapTest {
@BeforeEach
public void setup(@TempDir Path dir) {
this.map = LongLongMap.newFileBackedSparseArray(dir.resolve("test-sparse-array"), 80, 100);
}
}
public static class SparseArrayMemoryTest extends LongLongMapTest {
public static class SparseArray3 extends LongLongMapTest {
@BeforeEach
public void setup() {
this.map = LongLongMap.newInMemorySparseArray(80, 100);
}
}
public static class ArrayTest extends LongLongMapTest {
@BeforeEach
public void setup() {
this.map = LongLongMap.newArrayBacked();
}
}
public static class SqliteTest extends LongLongMapTest {
@BeforeEach
public void setup(@TempDir Path dir) {
this.map = LongLongMap.newSqlite(dir.resolve("sqlite-long-long-map-test"));
}
}
public static class SpareArray2Memory extends LongLongMapTest {
@BeforeEach
public void setup() {
this.map = new LongLongMap.SparseArray2Memory();
}
}
public static class SparseArray2 extends LongLongMapTest {
@BeforeEach
public void setup(@TempDir Path dir) {
this.map = new LongLongMap.SparseArray2(dir.resolve("temp-sparse-array-2"));
this.map = new LongLongMap.SparseArray(new AppendStoreRam.Longs());
}
}
}

Wyświetl plik

@ -29,7 +29,7 @@ public class OsmReaderTest {
};
private final Stats stats = Stats.inMemory();
private final Profile profile = new Profile.NullProfile();
private final LongLongMap longLongMap = LongLongMap.newInMemoryHashMap();
private final LongLongMap nodeMap = LongLongMap.newInMemorySortedTable();
private static Profile newProfile(
Function<OsmElement.Relation, List<OsmReader.RelationInfo>> processRelation) {
@ -645,7 +645,7 @@ public class OsmReaderTest {
record TestRelInfo(long id, String name) implements OsmReader.RelationInfo {}
OsmReader reader = new OsmReader(
osmSource,
longLongMap,
nodeMap,
new Profile.NullProfile() {
@Override
public List<OsmReader.RelationInfo> preprocessOsmRelation(OsmElement.Relation relation) {
@ -678,7 +678,7 @@ public class OsmReaderTest {
private OsmReader newOsmReader() {
return new OsmReader(
osmSource,
longLongMap,
nodeMap,
profile,
stats
);

Wyświetl plik

@ -33,8 +33,8 @@ public class ToiletsOverlay implements Profile {
@Override
public void processFeature(SourceFeature sourceFeature, FeatureCollector features) {
if (sourceFeature.hasTag("amenity", "toilets")) {
features.centroid("toilets")
if (sourceFeature.isPoint() && sourceFeature.hasTag("amenity", "toilets")) {
features.point("toilets")
.setZoomRange(0, 14)
// to limit toilets displayed at lower zoom levels:
// 1) set a z-order that defines a priority ordering of toilets. For mountains you might use "elevation"

Wyświetl plik

@ -67,7 +67,7 @@ public class ToiletsProfileTest {
assertContains("openstreetmap.org/copyright", metadata.get("attribution"));
TestUtils.assertNumFeatures(mbtiles, "toilets", 14, Map.of(), GeoUtils.WORLD_LAT_LON_BOUNDS,
36, Point.class);
34, Point.class);
}
}
}

Wyświetl plik

@ -165,7 +165,7 @@ public class Generate {
emitTableDefinitions(tables, packageName, output);
}
private static String GENERATED_FILE_HEADER = """
private static final String GENERATED_FILE_HEADER = """
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
All rights reserved.

Wyświetl plik

@ -576,8 +576,7 @@ public class OpenMapTilesSchema {
public static final String CLASS = "class";
/**
* <p>The OSM <a href="http://wiki.openstreetmap.org/wiki/Key:name"><code>name</code></a> value of the park
* (point
* features only).</p>
* (point features only).</p>
*/
public static final String NAME = "name";
/** <p>English name <code>name:en</code> if available, otherwise <code>name</code> (point features only).</p> */
@ -664,8 +663,7 @@ public class OpenMapTilesSchema {
public static final String DISPUTED_NAME = "disputed_name";
/**
* <p>ISO2 code of country, which wants to see the boundary line. For country boundaries only (<code>admin_level
* =
* 2</code>).</p>
* = 2</code>).</p>
*/
public static final String CLAIMED_BY = "claimed_by";
@ -784,9 +782,8 @@ public class OpenMapTilesSchema {
/**
* <p>Distinguish between more and less important roads or railways and roads under construction. Class is
* derived
* from the value of the <a href="http://wiki.openstreetmap.org/wiki/Key:highway"><code>highway</code></a>, <a
* href="http://wiki.openstreetmap.org/wiki/Key:construction"><code>construction</code></a>, <a
* derived from the value of the <a href="http://wiki.openstreetmap.org/wiki/Key:highway"><code>highway</code></a>,
* <a href="http://wiki.openstreetmap.org/wiki/Key:construction"><code>construction</code></a>, <a
* href="http://wiki.openstreetmap.org/wiki/Key:railway"><code>railway</code></a>, <a
* href="http://wiki.openstreetmap.org/wiki/Key:aerialway"><code>aerialway</code></a>, <a
* href="http://wiki.openstreetmap.org/wiki/Key:route"><code>route</code></a> tag (for shipping ways), or <a
@ -1058,8 +1055,7 @@ public class OpenMapTilesSchema {
/**
* <p>All <a href="http://wiki.openstreetmap.org/wiki/Buildings">OSM Buildings</a>. All building tags are imported
* (<a
* href="http://wiki.openstreetmap.org/wiki/Key:building"><code>building= </code></a>). The buildings are not yet
* (<a href="http://wiki.openstreetmap.org/wiki/Key:building"><code>building= </code></a>). The buildings are not yet
* ready for 3D rendering support and any help to improve this is welcomed.</p>
*/
public interface Building extends Layer {
@ -1194,8 +1190,7 @@ public class OpenMapTilesSchema {
public static final String NAME_DE = "name_de";
/**
* <p>The OSM <a href="http://wiki.openstreetmap.org/wiki/Key:ref"><code>ref</code></a> tag of the motorway or
* its
* network.</p>
* its network.</p>
*/
public static final String REF = "ref";
/**
@ -1440,9 +1435,8 @@ public class OpenMapTilesSchema {
* city (derived from population and city class). You can use the <strong>rank</strong> to limit density of labels
* or improve the text hierarchy. The rank value is a combination of the Natural Earth <code>scalerank</code>,
* <code>labelrank</code> and <code>datarank</code> values for countries and states and for cities consists out
* of
* a shifted Natural Earth <code>scalerank</code> combined with a local rank within a grid for cities that do not
* have a Natural Earth <code>scalerank</code>.</p>
* of a shifted Natural Earth <code>scalerank</code> combined with a local rank within a grid for cities that do
* not have a Natural Earth <code>scalerank</code>.</p>
*/
public static final String RANK = "rank";
}
@ -1472,9 +1466,8 @@ public class OpenMapTilesSchema {
/**
* <p>Everything in OpenStreetMap which contains a <code>addr:housenumber</code> tag useful for labelling
* housenumbers
* on a map. This adds significant size to <em>z14</em>. For buildings the centroid of the building is used as
* housenumber.</p>
* housenumbers on a map. This adds significant size to <em>z14</em>. For buildings the centroid of the building is
* used as housenumber.</p>
*/
public interface Housenumber extends Layer {
@ -1593,10 +1586,9 @@ public class OpenMapTilesSchema {
public static final String SUBCLASS = "subclass";
/**
* <p>The POIs are ranked ascending according to their importance within a grid. The <code>rank</code> value
* shows
* the local relative importance of a POI within it's cell in the grid. This can be used to reduce label density
* at <em>z14</em>. Since all POIs already need to be contained at <em>z14</em> you can use <code>less than
* rank=10</code> epxression to limit POIs. At some point like <em>z17</em> you can show all POIs.</p>
* shows the local relative importance of a POI within it's cell in the grid. This can be used to reduce label
* density at <em>z14</em>. Since all POIs already need to be contained at <em>z14</em> you can use <code>less
* than rank=10</code> epxression to limit POIs. At some point like <em>z17</em> you can show all POIs.</p>
*/
public static final String RANK = "rank";

Wyświetl plik

@ -66,7 +66,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
@ -313,7 +312,7 @@ public class Boundary implements
return FeatureMerge.mergeLineStrings(items, 1, tolerance, BUFFER_SIZE);
}
@NotNull
private BorderingRegions getBorderingRegions(
LongObjectMap<PreparedGeometry> countryBoundaries,
Set<Long> allRegions,
@ -368,7 +367,7 @@ public class Boundary implements
return new BorderingRegions(leftCountry, rightCountry);
}
@NotNull
private LongObjectMap<PreparedGeometry> prepareRegionPolygons() {
LOGGER.info("Creating polygons for " + regionGeometries.size() + " boundaries");
LongObjectMap<PreparedGeometry> countryBoundaries = new GHLongObjectHashMap<>();

Wyświetl plik

@ -68,8 +68,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
@ -108,7 +106,7 @@ public class TransportationName implements
private final boolean z13Paths;
private final Stats stats;
private PreparedGeometry greatBritain = null;
private AtomicBoolean loggedNoGb = new AtomicBoolean(false);
private final AtomicBoolean loggedNoGb = new AtomicBoolean(false);
public TransportationName(Translations translations, Arguments args, Stats stats) {
this.stats = stats;
@ -250,7 +248,6 @@ public class TransportationName implements
}
}
@Nullable
private RouteRelation getRouteRelation(Tables.OsmHighwayLinestring element,
List<OsmReader.RelationMember<RouteRelation>> relations, String ref) {
RouteRelation relation = relations.stream()
@ -330,7 +327,7 @@ public class TransportationName implements
}
private static record RouteRelation(
@NotNull String ref,
String ref,
RouteNetwork network,
long id
) implements OsmReader.RelationInfo {

Wyświetl plik

@ -26,7 +26,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.StreamSupport;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractLayerTest {
@ -134,7 +133,7 @@ public abstract class AbstractLayerTest {
return polygonFeatureWithArea(1, props);
}
@NotNull
protected ReaderFeature lineFeatureWithRelation(List<OsmReader.RelationInfo> relationInfos,
Map<String, Object> map) {
return new ReaderFeature(

Wyświetl plik

@ -11,7 +11,6 @@ import com.onthegomap.flatmap.reader.ReaderFeature;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
public class LandcoverTest extends AbstractLayerTest {
@ -181,7 +180,7 @@ public class LandcoverTest extends AbstractLayerTest {
), 6);
}
@NotNull
private VectorTileEncoder.Feature feature(org.locationtech.jts.geom.Polygon geom, Map<String, Object> m) {
return new VectorTileEncoder.Feature(
"landcover",