kopia lustrzana https://github.com/onthegomap/planetiler
137 wiersze
4.6 KiB
Java
137 wiersze
4.6 KiB
Java
package com.onthegomap.planetiler.util;
|
|
|
|
import static java.nio.file.StandardOpenOption.CREATE;
|
|
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
|
|
import static java.nio.file.StandardOpenOption.WRITE;
|
|
|
|
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
|
import com.fasterxml.jackson.databind.ObjectReader;
|
|
import com.fasterxml.jackson.databind.ObjectWriter;
|
|
import com.fasterxml.jackson.databind.RuntimeJsonMappingException;
|
|
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
|
|
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
|
|
import com.onthegomap.planetiler.config.PlanetilerConfig;
|
|
import com.onthegomap.planetiler.geo.TileCoord;
|
|
import java.io.BufferedInputStream;
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.stream.Collectors;
|
|
import java.util.zip.GZIPInputStream;
|
|
import java.util.zip.GZIPOutputStream;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
/**
|
|
* Holds tile weights to compute weighted average tile sizes.
|
|
* <p>
|
|
* {@link TopOsmTiles} can be used to get tile weights from 90 days of openstreetmap.org tile traffic.
|
|
*/
|
|
public class TileWeights {
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(TileWeights.class);
|
|
private static final CsvMapper MAPPER = new CsvMapper();
|
|
private static final CsvSchema SCHEMA = MAPPER
|
|
.schemaFor(Row.class)
|
|
.withHeader()
|
|
.withColumnSeparator('\t')
|
|
.withLineSeparator("\n");
|
|
private static final ObjectWriter WRITER = MAPPER.writer(SCHEMA);
|
|
private static final ObjectReader READER = MAPPER.readerFor(Row.class).with(SCHEMA);
|
|
private final Map<Integer, Long> byZoom = new HashMap<>();
|
|
private final Map<TileCoord, Long> weights = new HashMap<>();
|
|
|
|
public long getWeight(TileCoord coord) {
|
|
return weights.getOrDefault(coord, 0L);
|
|
}
|
|
|
|
/** Returns the sum of all tile weights at a specific zoom */
|
|
public long getZoomWeight(int zoom) {
|
|
return byZoom.getOrDefault(zoom, 0L);
|
|
}
|
|
|
|
/** Adds {@code weight} to the current weight for {@code coord} and returns this modified instance. */
|
|
public TileWeights put(TileCoord coord, long weight) {
|
|
weights.merge(coord, weight, Long::sum);
|
|
byZoom.merge(coord.z(), weight, Long::sum);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Write tile weights to a gzipped TSV file with {@code z, x, y, loads} columns.
|
|
*/
|
|
public void writeToFile(Path path) throws IOException {
|
|
try (
|
|
var output = new GZIPOutputStream(
|
|
new BufferedOutputStream(Files.newOutputStream(path, CREATE, TRUNCATE_EXISTING, WRITE)));
|
|
var writer = WRITER.writeValues(output)
|
|
) {
|
|
var sorted = weights.entrySet().stream()
|
|
.sorted(Comparator.comparingInt(e -> e.getKey().encoded()))
|
|
.iterator();
|
|
while (sorted.hasNext()) {
|
|
var entry = sorted.next();
|
|
TileCoord coord = entry.getKey();
|
|
writer.write(new Row(coord.z(), coord.x(), coord.y(), entry.getValue()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load tile weights from a gzipped TSV file with {@code z, x, y, loads} columns.
|
|
* <p>
|
|
* Duplicate entries will be added together.
|
|
*/
|
|
public static TileWeights readFromFile(Path path) {
|
|
TileWeights result = new TileWeights();
|
|
try (
|
|
var input = new GZIPInputStream(new BufferedInputStream(Files.newInputStream(path)));
|
|
var reader = READER.<Row>readValues(input)
|
|
) {
|
|
while (reader.hasNext()) {
|
|
var row = reader.next();
|
|
if (row.z >= PlanetilerConfig.MIN_MINZOOM && row.z <= PlanetilerConfig.MAX_MAXZOOM) {
|
|
int x = row.x % (1 << row.z);
|
|
int y = row.y % (1 << row.z);
|
|
result.put(TileCoord.ofXYZ(x, y, row.z()), row.loads());
|
|
}
|
|
}
|
|
} catch (IOException | RuntimeJsonMappingException e) {
|
|
LOGGER.warn("Unable to load tile weights from {}, will fall back to unweighted average: {}", path, e);
|
|
return new TileWeights();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
return byZoom.values().stream().noneMatch(e -> e > 0);
|
|
}
|
|
|
|
@JsonPropertyOrder({"z", "x", "y", "loads"})
|
|
private record Row(int z, int x, int y, long loads) {}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "TileWeights{\n" +
|
|
weights.entrySet().stream()
|
|
.map(result -> result.getKey() + ": " + result.getValue())
|
|
.collect(Collectors.joining("\n"))
|
|
.indent(2) +
|
|
'}';
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return o == this || (o instanceof TileWeights other && Objects.equals(other.weights, weights));
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return weights.hashCode();
|
|
}
|
|
}
|