planetiler/flatmap-core/src/main/java/com/onthegomap/flatmap/reader/osm/OsmInputFile.java

130 wiersze
4.7 KiB
Java
Czysty Zwykły widok Historia

2021-08-06 09:56:24 +00:00
package com.onthegomap.flatmap.reader.osm;
2021-04-07 00:33:35 +00:00
import com.google.protobuf.ByteString;
2021-04-12 10:05:32 +00:00
import com.graphhopper.reader.ReaderElement;
2021-04-16 11:13:05 +00:00
import com.graphhopper.reader.osm.pbf.PbfDecoder;
import com.graphhopper.reader.osm.pbf.PbfStreamSplitter;
import com.graphhopper.reader.osm.pbf.Sink;
2021-09-10 00:46:20 +00:00
import com.onthegomap.flatmap.config.Bounds;
2021-08-05 11:09:52 +00:00
import com.onthegomap.flatmap.worker.WorkerPipeline;
2021-04-16 11:13:05 +00:00
import java.io.BufferedInputStream;
2021-04-07 00:33:35 +00:00
import java.io.DataInputStream;
import java.io.IOException;
2021-05-01 20:08:20 +00:00
import java.nio.file.Files;
import java.nio.file.Path;
2021-04-16 11:13:05 +00:00
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
2021-06-05 12:02:51 +00:00
import java.util.concurrent.ThreadFactory;
2021-04-16 11:13:05 +00:00
import java.util.function.Consumer;
2021-04-07 00:33:35 +00:00
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
2021-05-01 20:08:20 +00:00
import org.locationtech.jts.geom.Envelope;
2021-04-07 00:33:35 +00:00
import org.openstreetmap.osmosis.osmbinary.Fileformat.Blob;
import org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader;
import org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox;
import org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock;
2021-09-10 00:46:20 +00:00
/**
* An input file in {@code .osm.pbf} format.
*
2021-10-20 01:57:47 +00:00
* @see <a href="https://wiki.openstreetmap.org/wiki/PBF_Format">OSM PBF Format</a>
2021-09-10 00:46:20 +00:00
*/
public class OsmInputFile implements Bounds.Provider, OsmSource {
2021-04-07 00:33:35 +00:00
2021-05-01 20:08:20 +00:00
private final Path path;
2021-04-07 00:33:35 +00:00
2021-05-01 20:08:20 +00:00
public OsmInputFile(Path path) {
this.path = path;
2021-04-07 00:33:35 +00:00
}
2021-09-10 00:46:20 +00:00
/**
* Returns the bounding box of this file from the header block.
*
* @throws IllegalArgumentException if an error is encountered reading the file
*/
2021-05-01 20:08:20 +00:00
@Override
2021-09-10 00:46:20 +00:00
public Envelope getLatLonBounds() {
2021-05-01 20:08:20 +00:00
try (var input = Files.newInputStream(path)) {
2021-09-10 00:46:20 +00:00
// Read the "bbox" field of the header block of the input file.
2021-08-02 00:26:22 +00:00
// https://wiki.openstreetmap.org/wiki/PBF_Format
2021-04-25 20:29:47 +00:00
var dataInput = new DataInputStream(input);
int headerSize = dataInput.readInt();
2021-08-02 00:26:22 +00:00
if (headerSize > 64 * 1024) {
2021-09-10 00:46:20 +00:00
throw new IllegalArgumentException("Header longer than 64 KiB: " + path);
2021-04-07 00:33:35 +00:00
}
2021-04-25 20:29:47 +00:00
byte[] buf = dataInput.readNBytes(headerSize);
2021-04-07 00:33:35 +00:00
BlobHeader header = BlobHeader.parseFrom(buf);
if (!header.getType().equals("OSMHeader")) {
2021-08-02 00:26:22 +00:00
throw new IllegalArgumentException("Expecting OSMHeader got " + header.getType() + " in " + path);
2021-04-07 00:33:35 +00:00
}
2021-04-25 20:29:47 +00:00
buf = dataInput.readNBytes(header.getDatasize());
2021-04-07 00:33:35 +00:00
Blob blob = Blob.parseFrom(buf);
2021-08-02 00:26:22 +00:00
ByteString data;
2021-04-07 00:33:35 +00:00
if (blob.hasRaw()) {
data = blob.getRaw();
} else if (blob.hasZlibData()) {
byte[] buf2 = new byte[blob.getRawSize()];
Inflater decompresser = new Inflater();
decompresser.setInput(blob.getZlibData().toByteArray());
decompresser.inflate(buf2);
decompresser.end();
data = ByteString.copyFrom(buf2);
2021-08-02 00:26:22 +00:00
} else {
2021-09-10 00:46:20 +00:00
throw new IllegalArgumentException("Header does not have raw or zlib data");
2021-04-07 00:33:35 +00:00
}
HeaderBlock headerblock = HeaderBlock.parseFrom(data);
HeaderBBox bbox = headerblock.getBbox();
2021-09-10 00:46:20 +00:00
// always specified in nanodegrees
2021-05-01 20:08:20 +00:00
return new Envelope(
2021-04-07 00:33:35 +00:00
bbox.getLeft() / 1e9,
bbox.getRight() / 1e9,
2021-05-01 20:08:20 +00:00
bbox.getBottom() / 1e9,
2021-04-07 00:33:35 +00:00
bbox.getTop() / 1e9
2021-05-01 20:08:20 +00:00
);
2021-04-07 00:33:35 +00:00
} catch (IOException | DataFormatException e) {
2021-08-02 00:26:22 +00:00
throw new IllegalArgumentException(e);
2021-04-07 00:33:35 +00:00
}
}
2021-04-12 10:05:32 +00:00
2021-09-10 00:46:20 +00:00
/**
* Reads all elements from the input file using {@code threads} threads to decode blocks in parallel and writes them
* to {@code next}.
*
* @throws IOException if an error is encountered reading the file
*/
2021-06-05 12:02:51 +00:00
public void readTo(Consumer<ReaderElement> next, String poolName, int threads) throws IOException {
ThreadFactory threadFactory = Executors.defaultThreadFactory();
ExecutorService executorService = Executors.newFixedThreadPool(threads, (runnable) -> {
Thread thread = threadFactory.newThread(runnable);
thread.setName(poolName + "-" + thread.getName());
return thread;
});
2021-05-01 20:08:20 +00:00
try (var stream = new BufferedInputStream(Files.newInputStream(path), 50_000)) {
2021-04-16 11:13:05 +00:00
PbfStreamSplitter streamSplitter = new PbfStreamSplitter(new DataInputStream(stream));
var sink = new ReaderElementSink(next);
PbfDecoder pbfDecoder = new PbfDecoder(streamSplitter, executorService, threads + 1, sink);
pbfDecoder.run();
} finally {
executorService.shutdownNow();
}
}
2021-09-10 00:46:20 +00:00
/** Starts a {@link WorkerPipeline} with all elements read from this input file. */
2021-05-28 10:08:13 +00:00
@Override
2021-08-05 11:09:52 +00:00
public WorkerPipeline.SourceStep<ReaderElement> read(String poolName, int threads) {
2021-06-05 12:02:51 +00:00
return next -> readTo(next, poolName, threads);
2021-04-12 10:05:32 +00:00
}
2021-04-14 11:20:19 +00:00
2021-04-16 11:13:05 +00:00
private static record ReaderElementSink(Consumer<ReaderElement> queue) implements Sink {
@Override
public void process(ReaderElement readerElement) {
queue.accept(readerElement);
}
@Override
public void complete() {
}
2021-04-14 11:20:19 +00:00
}
2021-04-07 00:33:35 +00:00
}