All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.onthegomap.planetiler.files.TileSchemeEncoding Maven / Gradle / Ivy

Go to download

Planetiler is tool to build planet-scale vector tilesets from OpenStreetMap data fast.

The newest version!
package com.onthegomap.planetiler.files;

import com.onthegomap.planetiler.geo.TileCoord;
import com.onthegomap.planetiler.geo.TileOrder;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;

/**
 * Tile scheme encoding i.e. encoding and decoding of tile coordinates to a relative path.
 * 

* The tile scheme is a template string that supports the following templates: {x}, {y}, {z}, {xs}, {ys}. {xs} and {ys} * are "safe" options that split the x/s coordinate into two folders, ensuring that each folder has less than 1000 * children. *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Tile SchemeExample Path
{z}/{x}/{y}.pbf3/1/2.pbf
{x}/{y}/{z}.pbf1/2/3.pbf
{x}-{y}-{z}.pbf1-2-3.pbf
{x}/a/{y}/b{z}.pbf1/a/2/b3.pbf
{z}/{x}/{y}.pbf.gz3/1/2.pbf.gz
{z}/{xs}/{ys}.pbf3/000/001/000/002.pbf
{z}/{x}/{ys}.pbf3/1/000/002.pbf
{z}/{xs}/{y}.pbf3/000/001/2.pbf
*/ public class TileSchemeEncoding { static final String X_TEMPLATE = "{x}"; static final String X_SAFE_TEMPLATE = "{xs}"; static final String Y_TEMPLATE = "{y}"; static final String Y_SAFE_TEMPLATE = "{ys}"; static final String Z_TEMPLATE = "{z}"; private final String tileScheme; private final Path basePath; /** * @param tileScheme the tile scheme to use e.g. {z}/{x}/{y}.pbf * @param basePath the base path to append the generated relative tile path to */ public TileSchemeEncoding(String tileScheme, Path basePath) { this.tileScheme = validate(tileScheme); this.basePath = basePath; } public Function encoder() { final boolean xSafe = tileScheme.contains(X_SAFE_TEMPLATE); final boolean ySafe = tileScheme.contains(Y_SAFE_TEMPLATE); return tileCoord -> { String p = tileScheme.replace(Z_TEMPLATE, Integer.toString(tileCoord.z())); if (xSafe) { final String colStr = String.format("%06d", tileCoord.x()); p = p.replace(X_SAFE_TEMPLATE, Paths.get(colStr.substring(0, 3), colStr.substring(3)).toString()); } else { p = p.replace(X_TEMPLATE, Integer.toString(tileCoord.x())); } if (ySafe) { final String rowStr = String.format("%06d", tileCoord.y()); p = p.replace(Y_SAFE_TEMPLATE, Paths.get(rowStr.substring(0, 3), rowStr.substring(3)).toString()); } else { p = p.replace(Y_TEMPLATE, Integer.toString(tileCoord.y())); } return basePath.resolve(Paths.get(p)); }; } Function> decoder() { final String tmpPath = basePath.resolve(tileScheme).toAbsolutePath().toString(); @SuppressWarnings("java:S1075") final String escapedPathSeparator = "\\" + File.separator; final Pattern pathPattern = Pattern.compile( Pattern.quote(tmpPath) .replace(X_TEMPLATE, "\\E(?\\d+)\\Q") .replace(Y_TEMPLATE, "\\E(?\\d+)\\Q") .replace(Z_TEMPLATE, "\\E(?\\d+)\\Q") .replace(X_SAFE_TEMPLATE, "\\E(?\\d+)" + escapedPathSeparator + "(?\\d+)\\Q") .replace(Y_SAFE_TEMPLATE, "\\E(?\\d+)" + escapedPathSeparator + "(?\\d+)\\Q") ); final boolean xSafe = tileScheme.contains(X_SAFE_TEMPLATE); final boolean ySafe = tileScheme.contains(Y_SAFE_TEMPLATE); return path -> { final Matcher m = pathPattern.matcher(path.toAbsolutePath().toString()); if (!m.matches()) { return Optional.empty(); } final int x = xSafe ? Integer.parseInt(m.group("x0") + m.group("x1")) : Integer.parseInt(m.group("x")); final int y = ySafe ? Integer.parseInt(m.group("y0") + m.group("y1")) : Integer.parseInt(m.group("y")); final int z = Integer.parseInt(m.group("z")); return Optional.of(TileCoord.ofXYZ(x, y, z)); }; } int searchDepth() { return Paths.get(tileScheme).getNameCount() + StringUtils.countMatches(tileScheme, X_SAFE_TEMPLATE) + StringUtils.countMatches(tileScheme, Y_SAFE_TEMPLATE); } TileOrder preferredTileOrder() { // there's only TMS currently - but once there are more, this can be changed according to the scheme return TileOrder.TMS; } private static String validate(String tileScheme) { if (Paths.get(tileScheme).isAbsolute()) { throw new IllegalArgumentException("tile scheme is not allowed to be absolute"); } if (StringUtils.countMatches(tileScheme, Z_TEMPLATE) != 1 || StringUtils.countMatches(tileScheme, X_TEMPLATE) + StringUtils.countMatches(tileScheme, X_SAFE_TEMPLATE) != 1 || StringUtils.countMatches(tileScheme, Y_TEMPLATE) + StringUtils.countMatches(tileScheme, Y_SAFE_TEMPLATE) != 1) { throw new IllegalArgumentException( "tile scheme must contain ('%s' OR '%s') AND ('%s' OR '%s' ) AND '%s'" .formatted(X_TEMPLATE, X_SAFE_TEMPLATE, Y_TEMPLATE, Y_SAFE_TEMPLATE, Z_TEMPLATE)); } if (tileScheme.contains("\\E") || tileScheme.contains("\\Q")) { throw new IllegalArgumentException("regex quotes are not allowed"); } return tileScheme; } @Override public boolean equals(Object o) { return this == o || (o instanceof TileSchemeEncoding that && Objects.equals(tileScheme, that.tileScheme) && Objects.equals(basePath, that.basePath)); } @Override public int hashCode() { return Objects.hash(tileScheme, basePath); } @Override public String toString() { return "TileSchemeEncoding[" + "tileScheme='" + tileScheme + '\'' + ", basePath=" + basePath + ']'; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy