org.dstadler.ctw.tiles.CreateTileOverlaysHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cover-the-world Show documentation
Show all versions of cover-the-world Show documentation
Read GPX tracks and produce a world-map with covered tiles.
package org.dstadler.ctw.tiles;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.mutable.MutableInt;
import org.dstadler.commons.collections.ConcurrentMappedCounter;
import org.dstadler.commons.logging.jdk.LoggerFactory;
import org.dstadler.ctw.geotools.GeoTools;
import org.dstadler.ctw.utils.Constants;
import org.dstadler.ctw.utils.OSMTile;
import org.geotools.feature.FeatureCollection;
/**
* Functionality which is shared by the applications which
* create overlay pngs for covered tiles and squares.
*/
public class CreateTileOverlaysHelper {
private static final Logger log = LoggerFactory.make();
// for printing stats when writing tiles
private static final AtomicLong lastLog = new AtomicLong();
protected static final ConcurrentMappedCounter EXPECTED = new ConcurrentMappedCounter<>();
protected static final ConcurrentMappedCounter ACTUAL = new ConcurrentMappedCounter<>();
protected static Set read(String file, String logName) throws IOException {
Set lines = new TreeSet<>();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
// 33U 608000.0 5337000.0,33U 609000.0 5338000.0
lines.add(line);
}
}
log.info("Found " + lines.size() + " covered " + logName);
return lines;
}
protected static void cleanTiles(File tileDir) {
if (tileDir.exists()) {
log.info("Removing previous tiles at " + tileDir);
Arrays.stream(Objects.requireNonNull(tileDir.listFiles())).forEach(s -> {
try {
FileUtils.deleteDirectory(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
public static void forEachZoom(Consumer task) throws InterruptedException {
// prepare counters
IntStream.rangeClosed(Constants.MIN_ZOOM, Constants.MAX_ZOOM).
forEach(zoom -> {
// indicate that this zoom is started
CreateTileOverlaysHelper.EXPECTED.add(zoom, 0);
CreateTileOverlaysHelper.ACTUAL.add(zoom, -1);
});
List aList = IntStream.rangeClosed(Constants.MIN_ZOOM, Constants.MAX_ZOOM).boxed()
.collect(Collectors.toList());
ForkJoinPool customThreadPool = new ForkJoinPool(Constants.MAX_ZOOM - Constants.MIN_ZOOM);
aList.forEach(
zoom -> customThreadPool.submit(
() -> task.accept(zoom)));
customThreadPool.shutdown();
if (!customThreadPool.awaitTermination(4,TimeUnit.HOURS)) {
throw new IllegalStateException("Timed out while waiting for all tasks to finish");
}
}
public static void writeTilesToFiles(File combinedDir, Set tilesOut, File tileDir,
FeatureCollection, ?> features, boolean borderOnly) {
MutableInt tilesNr = new MutableInt(1);
// process in parallel to make good use of CPU
// if this is called from a thread inside a custom thread pool, it should be used
// for scheduling the new tasks as well!
tilesOut.stream().parallel().forEach(
tile -> {
try {
writeTileToFile(combinedDir, tilesOut, tileDir, features, tile, tilesNr.getValue(), borderOnly);
} catch (IOException e) {
throw new RuntimeException(e);
}
tilesNr.increment();
CreateTileOverlaysHelper.ACTUAL.inc(tile.getZoom());
}
);
}
private static void writeTileToFile(File combinedDir,
Set tilesOut,
File tileDir,
FeatureCollection, ?> features,
OSMTile tile,
int tilesNr,
boolean borderOnly) throws IOException {
File file = tile.toFile(tileDir);
// Save the image in PNG format using the javax.imageio API
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
throw new IOException("Could not create directory at " + file.getParentFile());
}
if (borderOnly) {
GeoTools.writeBorder(features, tile.getRectangle(), file);
} else {
GeoTools.writeImage(features, tile.getRectangle(), file);
}
// whenever writing a tile, remove the combined overlay to re-create it in a follow-up step
/*if (written)*/
{
File combinedTile = new File(combinedDir, tile.toCoords() + ".png");
if (combinedTile.exists()) {
if (!combinedTile.delete()) {
throw new IOException("Could not delete file " + combinedTile);
}
}
}
if (lastLog.get() + TimeUnit.SECONDS.toMillis(5) < System.currentTimeMillis()) {
log.info(String.format(Locale.US, "features -> png: zoom %d: %s - %,d of %,d: %s%s",
tile.getZoom(), tileDir, tilesNr, tilesOut.size(), tile.toCoords(), CreateTileOverlaysHelper.concatProgress()));
lastLog.set(System.currentTimeMillis());
}
}
protected static String concatProgress() {
StringBuilder progress = new StringBuilder();
for (int zoom = Constants.MIN_ZOOM; zoom <= Constants.MAX_ZOOM; zoom++) {
long actual = ACTUAL.get(zoom);
if (actual == -1) {
progress.append(", ").append(zoom).append(":_");
continue;
}
long expected = EXPECTED.get(zoom);
// include if not started yet
if (expected == 0) {
progress.append(", ").append(zoom).append(":0%");
} else if (actual != expected) {
// otherwise include if not completed yet
progress.append(
String.format(", %d:%.0f%%",
zoom, ((double) actual) / expected * 100));
}
}
return progress.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy