mirror of
https://github.com/cfpwastaken/planetiler-openmaptiles.git
synced 2026-02-04 12:31:10 +00:00
Migrate to eclipse formatter to support multiple IDEs (#122)
This commit is contained in:
@@ -27,15 +27,15 @@ import java.util.List;
|
|||||||
* <p>
|
* <p>
|
||||||
* Layer implementations extend these interfaces to subscribe to elements from different sources:
|
* Layer implementations extend these interfaces to subscribe to elements from different sources:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link LakeCenterlineProcessor}</li>
|
* <li>{@link LakeCenterlineProcessor}</li>
|
||||||
* <li>{@link NaturalEarthProcessor}</li>
|
* <li>{@link NaturalEarthProcessor}</li>
|
||||||
* <li>{@link OsmWaterPolygonProcessor}</li>
|
* <li>{@link OsmWaterPolygonProcessor}</li>
|
||||||
* <li>{@link OsmAllProcessor} to process every OSM feature</li>
|
* <li>{@link OsmAllProcessor} to process every OSM feature</li>
|
||||||
* <li>{@link OsmRelationPreprocessor} to process every OSM relation during first pass through OSM file</li>
|
* <li>{@link OsmRelationPreprocessor} to process every OSM relation during first pass through OSM file</li>
|
||||||
* <li>A {@link Tables.RowHandler} implementation in {@code Tables.java} to process input features filtered and parsed
|
* <li>A {@link Tables.RowHandler} implementation in {@code Tables.java} to process input features filtered and parsed
|
||||||
* according to the imposm3 mappings defined in the OpenMapTiles schema. Each element corresponds to a row in the
|
* according to the imposm3 mappings defined in the OpenMapTiles schema. Each element corresponds to a row in the table
|
||||||
* table that imposm3 would have generated, with generated methods for accessing the data that would have been in each
|
* that imposm3 would have generated, with generated methods for accessing the data that would have been in each
|
||||||
* column</li>
|
* column</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* Layers can also subscribe to notifications when we finished processing an input source by implementing
|
* Layers can also subscribe to notifications when we finished processing an input source by implementing
|
||||||
* {@link FinishHandler} or post-process features in that layer before rendering the output tile by implementing
|
* {@link FinishHandler} or post-process features in that layer before rendering the output tile by implementing
|
||||||
@@ -118,9 +118,8 @@ public class BasemapProfile extends ForwardingProfile {
|
|||||||
return new RowDispatch(constructor.create(), handlers);
|
return new RowDispatch(constructor.create(), handlers);
|
||||||
}).simplify().index();
|
}).simplify().index();
|
||||||
wikidataMappings = Tables.MAPPINGS
|
wikidataMappings = Tables.MAPPINGS
|
||||||
.mapResults(constructor ->
|
.mapResults(constructor -> handlerMap.getOrDefault(constructor.rowClass(), List.of()).stream()
|
||||||
handlerMap.getOrDefault(constructor.rowClass(), List.of()).stream()
|
.anyMatch(handler -> !IgnoreWikidata.class.isAssignableFrom(handler.handlerClass()))
|
||||||
.anyMatch(handler -> !IgnoreWikidata.class.isAssignableFrom(handler.handlerClass()))
|
|
||||||
).filterResults(b -> b).simplify().index();
|
).filterResults(b -> b).simplify().index();
|
||||||
|
|
||||||
// register a handler for all OSM elements that forwards to imposm3 "table row" handler methods
|
// register a handler for all OSM elements that forwards to imposm3 "table row" handler methods
|
||||||
@@ -149,8 +148,8 @@ public class BasemapProfile extends ForwardingProfile {
|
|||||||
if (elem instanceof OsmElement.Node) {
|
if (elem instanceof OsmElement.Node) {
|
||||||
return wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_POINT, tags), false);
|
return wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_POINT, tags), false);
|
||||||
} else if (elem instanceof OsmElement.Way) {
|
} else if (elem instanceof OsmElement.Way) {
|
||||||
return wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_POLYGON, tags), false)
|
return wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_POLYGON, tags), false) ||
|
||||||
|| wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_LINE, tags), false);
|
wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_LINE, tags), false);
|
||||||
} else if (elem instanceof OsmElement.Relation) {
|
} else if (elem instanceof OsmElement.Relation) {
|
||||||
return wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_POLYGON, tags), false);
|
return wikidataMappings.getOrElse(SimpleFeature.create(EMPTY_POLYGON, tags), false);
|
||||||
} else {
|
} else {
|
||||||
@@ -201,14 +200,13 @@ public class BasemapProfile extends ForwardingProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layers should implement this interface to subscribe to elements from <a href="https://www.naturalearthdata.com/">natural
|
* Layers should implement this interface to subscribe to elements from
|
||||||
* earth</a>.
|
* <a href="https://www.naturalearthdata.com/">natural earth</a>.
|
||||||
*/
|
*/
|
||||||
public interface NaturalEarthProcessor {
|
public interface NaturalEarthProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process an element from {@code table} in the<a href="https://www.naturalearthdata.com/">natural earth
|
* Process an element from {@code table} in the<a href="https://www.naturalearthdata.com/">natural earth source</a>.
|
||||||
* source</a>.
|
|
||||||
*
|
*
|
||||||
* @see Profile#processFeature(SourceFeature, FeatureCollector)
|
* @see Profile#processFeature(SourceFeature, FeatureCollector)
|
||||||
*/
|
*/
|
||||||
@@ -216,8 +214,8 @@ public class BasemapProfile extends ForwardingProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layers should implement this interface to subscribe to elements from <a href="https://github.com/lukasmartinelli/osm-lakelines">OSM
|
* Layers should implement this interface to subscribe to elements from
|
||||||
* lake centerlines source</a>.
|
* <a href="https://github.com/lukasmartinelli/osm-lakelines">OSM lake centerlines source</a>.
|
||||||
*/
|
*/
|
||||||
public interface LakeCenterlineProcessor {
|
public interface LakeCenterlineProcessor {
|
||||||
|
|
||||||
@@ -231,8 +229,8 @@ public class BasemapProfile extends ForwardingProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layers should implement this interface to subscribe to elements from <a href="https://osmdata.openstreetmap.de/data/water-polygons.html">OSM
|
* Layers should implement this interface to subscribe to elements from
|
||||||
* water polygons source</a>.
|
* <a href="https://osmdata.openstreetmap.de/data/water-polygons.html">OSM water polygons source</a>.
|
||||||
*/
|
*/
|
||||||
public interface OsmWaterPolygonProcessor {
|
public interface OsmWaterPolygonProcessor {
|
||||||
|
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ import org.yaml.snakeyaml.LoaderOptions;
|
|||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates code in the {@code generated} package from the OpenMapTiles schema crawled from a tag or branch in the <a
|
* Generates code in the {@code generated} package from the OpenMapTiles schema crawled from a tag or branch in the
|
||||||
* href="https://github.com/openmaptiles/openmaptiles">OpenMapTiles GitHub repo</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles">OpenMapTiles GitHub repo</a>.
|
||||||
* <p>
|
* <p>
|
||||||
* {@code OpenMapTilesSchema.java} contains the output layer definitions (i.e. attributes and allowed values) so that
|
* {@code OpenMapTilesSchema.java} contains the output layer definitions (i.e. attributes and allowed values) so that
|
||||||
* layer implementations in {@code layers} package can reference them instead of hard-coding.
|
* layer implementations in {@code layers} package can reference them instead of hard-coding.
|
||||||
* <p>
|
* <p>
|
||||||
* {@code Tables.java} contains the <a href="https://github.com/omniscale/imposm3">imposm3</a> table definitions from
|
* {@code Tables.java} contains the <a href="https://github.com/omniscale/imposm3">imposm3</a> table definitions from
|
||||||
* mapping.yaml files in the OpenMapTiles repo. Layers in the {@code layer} package can extend the {@code Handler}
|
* mapping.yaml files in the OpenMapTiles repo. Layers in the {@code layer} package can extend the {@code Handler}
|
||||||
* nested class for a table definition to "subscribe" to OSM elements that imposm3 would put in that table.
|
* nested class for a table definition to "subscribe" to OSM elements that imposm3 would put in that table.
|
||||||
* <p>
|
* <p>
|
||||||
* To run use {@code ./scripts/regenerate-openmaptiles.sh}
|
* To run use {@code ./scripts/regenerate-openmaptiles.sh}
|
||||||
@@ -169,8 +169,7 @@ public class Generate {
|
|||||||
|
|
||||||
emitLayerSchemaDefinitions(config.tileset, layers, packageName, output, tag);
|
emitLayerSchemaDefinitions(config.tileset, layers, packageName, output, tag);
|
||||||
emitTableDefinitions(tables, packageName, output, tag);
|
emitTableDefinitions(tables, packageName, output, tag);
|
||||||
LOGGER.info(
|
LOGGER.info("Done!");
|
||||||
"Done generating code in 'generated' package, now run IntelliJ 'Reformat Code' operation with 'Optimize imports' and 'Cleanup code' options selected.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generates {@code OpenMapTilesSchema.java} */
|
/** Generates {@code OpenMapTilesSchema.java} */
|
||||||
@@ -178,56 +177,57 @@ public class Generate {
|
|||||||
Path output, String tag)
|
Path output, String tag)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
StringBuilder schemaClass = new StringBuilder();
|
StringBuilder schemaClass = new StringBuilder();
|
||||||
schemaClass.append("""
|
schemaClass.append(
|
||||||
%s
|
|
||||||
package %s;
|
|
||||||
|
|
||||||
import static com.onthegomap.planetiler.expression.Expression.*;
|
|
||||||
import com.onthegomap.planetiler.config.PlanetilerConfig;
|
|
||||||
import com.onthegomap.planetiler.stats.Stats;
|
|
||||||
import com.onthegomap.planetiler.expression.MultiExpression;
|
|
||||||
import com.onthegomap.planetiler.basemap.Layer;
|
|
||||||
import com.onthegomap.planetiler.util.Translations;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All vector tile layer definitions, attributes, and allowed values generated from the
|
|
||||||
* <a href="https://github.com/openmaptiles/openmaptiles/blob/%s/openmaptiles.yaml">OpenMapTiles vector tile schema %s</a>.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class OpenMapTilesSchema {
|
|
||||||
public static final String NAME = %s;
|
|
||||||
public static final String DESCRIPTION = %s;
|
|
||||||
public static final String VERSION = %s;
|
|
||||||
public static final String ATTRIBUTION = %s;
|
|
||||||
public static final List<String> LANGUAGES = List.of(%s);
|
|
||||||
|
|
||||||
/** Returns a list of expected layer implementation instances from the {@code layers} package. */
|
|
||||||
public static List<Layer> createInstances(Translations translations, PlanetilerConfig config, Stats stats) {
|
|
||||||
return List.of(
|
|
||||||
%s
|
|
||||||
);
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
.formatted(
|
%s
|
||||||
GENERATED_FILE_HEADER,
|
package %s;
|
||||||
packageName,
|
|
||||||
escapeJavadoc(tag),
|
import static com.onthegomap.planetiler.expression.Expression.*;
|
||||||
escapeJavadoc(tag),
|
import com.onthegomap.planetiler.config.PlanetilerConfig;
|
||||||
Format.quote(info.name),
|
import com.onthegomap.planetiler.stats.Stats;
|
||||||
Format.quote(info.description),
|
import com.onthegomap.planetiler.expression.MultiExpression;
|
||||||
Format.quote(info.version),
|
import com.onthegomap.planetiler.basemap.Layer;
|
||||||
Format.quote(info.attribution),
|
import com.onthegomap.planetiler.util.Translations;
|
||||||
info.languages.stream().map(Format::quote).collect(joining(", ")),
|
import java.util.List;
|
||||||
layers.stream()
|
import java.util.Map;
|
||||||
.map(
|
import java.util.Set;
|
||||||
l -> "new com.onthegomap.planetiler.basemap.layers.%s(translations, config, stats)"
|
|
||||||
.formatted(lowerUnderscoreToUpperCamel(l.layer.id)))
|
/**
|
||||||
.collect(joining("," + LINE_SEPARATOR))
|
* All vector tile layer definitions, attributes, and allowed values generated from the
|
||||||
.indent(6).trim()
|
* <a href="https://github.com/openmaptiles/openmaptiles/blob/%s/openmaptiles.yaml">OpenMapTiles vector tile schema %s</a>.
|
||||||
));
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class OpenMapTilesSchema {
|
||||||
|
public static final String NAME = %s;
|
||||||
|
public static final String DESCRIPTION = %s;
|
||||||
|
public static final String VERSION = %s;
|
||||||
|
public static final String ATTRIBUTION = %s;
|
||||||
|
public static final List<String> LANGUAGES = List.of(%s);
|
||||||
|
|
||||||
|
/** Returns a list of expected layer implementation instances from the {@code layers} package. */
|
||||||
|
public static List<Layer> createInstances(Translations translations, PlanetilerConfig config, Stats stats) {
|
||||||
|
return List.of(
|
||||||
|
%s
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.formatted(
|
||||||
|
GENERATED_FILE_HEADER,
|
||||||
|
packageName,
|
||||||
|
escapeJavadoc(tag),
|
||||||
|
escapeJavadoc(tag),
|
||||||
|
Format.quote(info.name),
|
||||||
|
Format.quote(info.description),
|
||||||
|
Format.quote(info.version),
|
||||||
|
Format.quote(info.attribution),
|
||||||
|
info.languages.stream().map(Format::quote).collect(joining(", ")),
|
||||||
|
layers.stream()
|
||||||
|
.map(
|
||||||
|
l -> "new com.onthegomap.planetiler.basemap.layers.%s(translations, config, stats)"
|
||||||
|
.formatted(lowerUnderscoreToUpperCamel(l.layer.id)))
|
||||||
|
.collect(joining("," + LINE_SEPARATOR))
|
||||||
|
.indent(6).trim()
|
||||||
|
));
|
||||||
for (var layer : layers) {
|
for (var layer : layers) {
|
||||||
String layerCode = generateCodeForLayer(tag, layer);
|
String layerCode = generateCodeForLayer(tag, layer);
|
||||||
schemaClass.append(layerCode);
|
schemaClass.append(layerCode);
|
||||||
@@ -344,68 +344,70 @@ public class Generate {
|
|||||||
String tag)
|
String tag)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
StringBuilder tablesClass = new StringBuilder();
|
StringBuilder tablesClass = new StringBuilder();
|
||||||
tablesClass.append("""
|
tablesClass.append(
|
||||||
%s
|
"""
|
||||||
package %s;
|
%s
|
||||||
|
package %s;
|
||||||
|
|
||||||
import static com.onthegomap.planetiler.expression.Expression.*;
|
import static com.onthegomap.planetiler.expression.Expression.*;
|
||||||
|
|
||||||
import com.onthegomap.planetiler.expression.Expression;
|
import com.onthegomap.planetiler.expression.Expression;
|
||||||
import com.onthegomap.planetiler.expression.MultiExpression;
|
import com.onthegomap.planetiler.expression.MultiExpression;
|
||||||
import com.onthegomap.planetiler.FeatureCollector;
|
import com.onthegomap.planetiler.FeatureCollector;
|
||||||
import com.onthegomap.planetiler.reader.SourceFeature;
|
import com.onthegomap.planetiler.reader.SourceFeature;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OSM element parsers generated from the <a href="https://github.com/omniscale/imposm3">imposm3</a> table definitions
|
* OSM element parsers generated from the <a href="https://github.com/omniscale/imposm3">imposm3</a> table definitions
|
||||||
* in the <a href="https://github.com/openmaptiles/openmaptiles/blob/%s/openmaptiles.yaml">OpenMapTiles vector tile schema</a>.
|
* in the <a href="https://github.com/openmaptiles/openmaptiles/blob/%s/openmaptiles.yaml">OpenMapTiles vector tile schema</a>.
|
||||||
*
|
*
|
||||||
* These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the
|
* These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the
|
||||||
* columns in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each
|
* columns in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each
|
||||||
* "table" but implementing the table's {@code Handler} interface and use the element's typed API to access
|
* "table" but implementing the table's {@code Handler} interface and use the element's typed API to access
|
||||||
* attributes.
|
* attributes.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class Tables {
|
public class Tables {
|
||||||
/** A parsed OSM element that would appear in a "row" of the imposm3 table. */
|
/** A parsed OSM element that would appear in a "row" of the imposm3 table. */
|
||||||
public interface Row {
|
public interface Row {
|
||||||
|
|
||||||
/** Returns the original OSM element. */
|
/** Returns the original OSM element. */
|
||||||
SourceFeature source();
|
SourceFeature source();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A functional interface that the constructor of a new table row can be coerced to. */
|
/** A functional interface that the constructor of a new table row can be coerced to. */
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface Constructor {
|
public interface Constructor {
|
||||||
|
|
||||||
Row create(SourceFeature source, String mappingKey);
|
Row create(SourceFeature source, String mappingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The {@code rowClass} of an imposm3 table row and its constructor coerced to a {@link Constructor}. */
|
/** The {@code rowClass} of an imposm3 table row and its constructor coerced to a {@link Constructor}. */
|
||||||
public record RowClassAndConstructor(
|
public record RowClassAndConstructor(
|
||||||
Class<? extends Row> rowClass,
|
Class<? extends Row> rowClass,
|
||||||
Constructor create
|
Constructor create
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/** A functional interface that the typed handler method that a layer implementation can be coerced to. */
|
/** A functional interface that the typed handler method that a layer implementation can be coerced to. */
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface RowHandler<T extends Row> {
|
public interface RowHandler<T extends Row> {
|
||||||
|
|
||||||
/** Process a typed element according to the profile. */
|
/** Process a typed element according to the profile. */
|
||||||
void process(T element, FeatureCollector features);
|
void process(T element, FeatureCollector features);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The {@code handlerClass} of a layer handler and it's {@code process} method coerced to a {@link RowHandler}. */
|
/** The {@code handlerClass} of a layer handler and it's {@code process} method coerced to a {@link RowHandler}. */
|
||||||
public record RowHandlerAndClass<T extends Row>(
|
public record RowHandlerAndClass<T extends Row>(
|
||||||
Class<?> handlerClass,
|
Class<?> handlerClass,
|
||||||
RowHandler<T> handler
|
RowHandler<T> handler
|
||||||
) {}
|
) {}
|
||||||
""".formatted(GENERATED_FILE_HEADER, packageName, escapeJavadoc(tag)));
|
"""
|
||||||
|
.formatted(GENERATED_FILE_HEADER, packageName, escapeJavadoc(tag)));
|
||||||
|
|
||||||
List<String> classNames = new ArrayList<>();
|
List<String> classNames = new ArrayList<>();
|
||||||
Map<String, String> fieldNameToType = new TreeMap<>();
|
Map<String, String> fieldNameToType = new TreeMap<>();
|
||||||
@@ -489,17 +491,19 @@ public class Generate {
|
|||||||
));
|
));
|
||||||
""".formatted(
|
""".formatted(
|
||||||
classNames.stream().map(
|
classNames.stream().map(
|
||||||
className -> "MultiExpression.entry(new RowClassAndConstructor(%s.class, %s::new), %s.MAPPING)".formatted(
|
className -> "MultiExpression.entry(new RowClassAndConstructor(%s.class, %s::new), %s.MAPPING)".formatted(
|
||||||
className, className, className))
|
className, className, className))
|
||||||
.collect(joining("," + LINE_SEPARATOR)).indent(2).strip()
|
.collect(joining("," + LINE_SEPARATOR)).indent(2).strip()
|
||||||
).indent(2));
|
).indent(2));
|
||||||
|
|
||||||
String handlerCondition = classNames.stream().map(className ->
|
String handlerCondition = classNames.stream()
|
||||||
"""
|
.map(
|
||||||
if (handler instanceof %s.Handler typedHandler) {
|
className -> """
|
||||||
result.computeIfAbsent(%s.class, cls -> new ArrayList<>()).add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
|
if (handler instanceof %s.Handler typedHandler) {
|
||||||
}""".formatted(className, className)
|
result.computeIfAbsent(%s.class, cls -> new ArrayList<>()).add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
|
||||||
).collect(joining(LINE_SEPARATOR));
|
}"""
|
||||||
|
.formatted(className, className)
|
||||||
|
).collect(joining(LINE_SEPARATOR));
|
||||||
tablesClass.append("""
|
tablesClass.append("""
|
||||||
/**
|
/**
|
||||||
* Returns a map from imposm3 "table row" class to the layers that have a handler for it from a list of layer
|
* Returns a map from imposm3 "table row" class to the layers that have a handler for it from a list of layer
|
||||||
@@ -518,15 +522,15 @@ public class Generate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an {@link Expression} that implements the same logic as the <a href="https://imposm.org/docs/imposm3/latest/mapping.html">Imposm3
|
* Returns an {@link Expression} that implements the same logic as the
|
||||||
* Data Mapping</a> definition for a table.
|
* <a href="https://imposm.org/docs/imposm3/latest/mapping.html">Imposm3 Data Mapping</a> definition for a table.
|
||||||
*/
|
*/
|
||||||
static Expression parseImposm3MappingExpression(Imposm3Table table) {
|
static Expression parseImposm3MappingExpression(Imposm3Table table) {
|
||||||
if (table.type_mappings != null) {
|
if (table.type_mappings != null) {
|
||||||
return or(
|
return or(
|
||||||
table.type_mappings.entrySet().stream().map(entry ->
|
table.type_mappings.entrySet().stream()
|
||||||
parseImposm3MappingExpression(entry.getKey(), entry.getValue(), table.filters)
|
.map(entry -> parseImposm3MappingExpression(entry.getKey(), entry.getValue(), table.filters)
|
||||||
).toList()
|
).toList()
|
||||||
).simplify();
|
).simplify();
|
||||||
} else {
|
} else {
|
||||||
return parseImposm3MappingExpression(table.type, table.mapping, table.filters);
|
return parseImposm3MappingExpression(table.type, table.mapping, table.filters);
|
||||||
@@ -534,8 +538,8 @@ public class Generate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an {@link Expression} that implements the same logic as the <a href="https://imposm.org/docs/imposm3/latest/mapping.html#filters">Imposm3
|
* Returns an {@link Expression} that implements the same logic as the
|
||||||
* Data Mapping filters</a> for a table.
|
* <a href="https://imposm.org/docs/imposm3/latest/mapping.html#filters">Imposm3 Data Mapping filters</a> for a table.
|
||||||
*/
|
*/
|
||||||
static Expression parseImposm3MappingExpression(String type, JsonNode mapping, Imposm3Filters filters) {
|
static Expression parseImposm3MappingExpression(String type, JsonNode mapping, Imposm3Filters filters) {
|
||||||
return and(
|
return and(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -51,7 +51,8 @@ import com.onthegomap.planetiler.util.Translations;
|
|||||||
/**
|
/**
|
||||||
* Defines the logic for generating map elements in the {@code aerodrome_label} layer from source features.
|
* Defines the logic for generating map elements in the {@code aerodrome_label} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/aerodrome_label">OpenMapTiles
|
* This class is ported to Java from
|
||||||
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/aerodrome_label">OpenMapTiles
|
||||||
* aerodrome_layer sql files</a>.
|
* aerodrome_layer sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class AerodromeLabel implements
|
public class AerodromeLabel implements
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ import com.onthegomap.planetiler.util.Translations;
|
|||||||
/**
|
/**
|
||||||
* Defines the logic for generating map elements in the {@code aeroway} layer from source features.
|
* Defines the logic for generating map elements in the {@code aeroway} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/aeroway">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* aeroway sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/aeroway">OpenMapTiles aeroway sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Aeroway implements
|
public class Aeroway implements
|
||||||
OpenMapTilesSchema.Aeroway,
|
OpenMapTilesSchema.Aeroway,
|
||||||
@@ -54,8 +54,7 @@ public class Aeroway implements
|
|||||||
Tables.OsmAerowayPolygon.Handler,
|
Tables.OsmAerowayPolygon.Handler,
|
||||||
Tables.OsmAerowayPoint.Handler {
|
Tables.OsmAerowayPoint.Handler {
|
||||||
|
|
||||||
public Aeroway(Translations translations, PlanetilerConfig config, Stats stats) {
|
public Aeroway(Translations translations, PlanetilerConfig config, Stats stats) {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(Tables.OsmAerowayPolygon element, FeatureCollector features) {
|
public void process(Tables.OsmAerowayPolygon element, FeatureCollector features) {
|
||||||
|
|||||||
@@ -86,8 +86,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
* Defines the logic for generating map elements for country, state, and town boundaries in the {@code boundary} layer
|
* Defines the logic for generating map elements for country, state, and town boundaries in the {@code boundary} layer
|
||||||
* from source features.
|
* from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/boundary">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* boundary sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/boundary">OpenMapTiles boundary sql
|
||||||
|
* files</a>.
|
||||||
*/
|
*/
|
||||||
public class Boundary implements
|
public class Boundary implements
|
||||||
OpenMapTilesSchema.Boundary,
|
OpenMapTilesSchema.Boundary,
|
||||||
@@ -164,13 +165,13 @@ public class Boundary implements
|
|||||||
BoundaryInfo info = switch (table) {
|
BoundaryInfo info = switch (table) {
|
||||||
case "ne_110m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 0, 0);
|
case "ne_110m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 0, 0);
|
||||||
case "ne_50m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 1, 3);
|
case "ne_50m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 1, 3);
|
||||||
case "ne_10m_admin_0_boundary_lines_land" -> feature.hasTag("featurecla", "Lease Limit") ? null
|
case "ne_10m_admin_0_boundary_lines_land" -> feature.hasTag("featurecla", "Lease Limit") ? null :
|
||||||
: new BoundaryInfo(2, 4, 4);
|
new BoundaryInfo(2, 4, 4);
|
||||||
case "ne_10m_admin_1_states_provinces_lines" -> {
|
case "ne_10m_admin_1_states_provinces_lines" -> {
|
||||||
Double minZoom = Parse.parseDoubleOrNull(feature.getTag("min_zoom"));
|
Double minZoom = Parse.parseDoubleOrNull(feature.getTag("min_zoom"));
|
||||||
yield minZoom != null && minZoom <= 7 ? new BoundaryInfo(4, 1, 4) :
|
yield minZoom != null && minZoom <= 7 ? new BoundaryInfo(4, 1, 4) :
|
||||||
minZoom != null && minZoom <= 7.7 ? new BoundaryInfo(4, 4, 4) :
|
minZoom != null && minZoom <= 7.7 ? new BoundaryInfo(4, 4, 4) :
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
@@ -250,8 +251,8 @@ public class Boundary implements
|
|||||||
int minzoom =
|
int minzoom =
|
||||||
(maritime && minAdminLevel == 2) ? 4 :
|
(maritime && minAdminLevel == 2) ? 4 :
|
||||||
minAdminLevel <= 4 ? 5 :
|
minAdminLevel <= 4 ? 5 :
|
||||||
minAdminLevel <= 6 ? 9 :
|
minAdminLevel <= 6 ? 9 :
|
||||||
minAdminLevel <= 8 ? 11 : 12;
|
minAdminLevel <= 8 ? 11 : 12;
|
||||||
if (addCountryNames && !regionIds.isEmpty()) {
|
if (addCountryNames && !regionIds.isEmpty()) {
|
||||||
// save for later
|
// save for later
|
||||||
try {
|
try {
|
||||||
@@ -398,8 +399,7 @@ public class Boundary implements
|
|||||||
try {
|
try {
|
||||||
Geometry combined = polygonizer.getGeometry().union();
|
Geometry combined = polygonizer.getGeometry().union();
|
||||||
if (combined.isEmpty()) {
|
if (combined.isEmpty()) {
|
||||||
LOGGER.warn("Unable to form closed polygon for OSM relation " + regionId
|
LOGGER.warn("Unable to form closed polygon for OSM relation " + regionId + " (likely missing edges)");
|
||||||
+ " (likely missing edges)");
|
|
||||||
} else {
|
} else {
|
||||||
countryBoundaries.put(regionId, PreparedGeometryFactory.prepare(combined));
|
countryBoundaries.put(regionId, PreparedGeometryFactory.prepare(combined));
|
||||||
}
|
}
|
||||||
@@ -429,8 +429,7 @@ public class Boundary implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal set of information extracted from a boundary relation to be used when processing each way in that
|
* Minimal set of information extracted from a boundary relation to be used when processing each way in that relation.
|
||||||
* relation.
|
|
||||||
*/
|
*/
|
||||||
private record BoundaryRelation(
|
private record BoundaryRelation(
|
||||||
long id,
|
long id,
|
||||||
@@ -443,17 +442,15 @@ public class Boundary implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long estimateMemoryUsageBytes() {
|
public long estimateMemoryUsageBytes() {
|
||||||
return CLASS_HEADER_BYTES
|
return CLASS_HEADER_BYTES + MemoryEstimator.estimateSizeLong(id) + MemoryEstimator.estimateSizeInt(adminLevel) +
|
||||||
+ MemoryEstimator.estimateSizeLong(id)
|
estimateSize(disputed) + POINTER_BYTES + estimateSize(name) + POINTER_BYTES + estimateSize(claimedBy) +
|
||||||
+ MemoryEstimator.estimateSizeInt(adminLevel)
|
POINTER_BYTES + estimateSize(iso3166alpha3);
|
||||||
+ estimateSize(disputed)
|
|
||||||
+ POINTER_BYTES + estimateSize(name)
|
|
||||||
+ POINTER_BYTES + estimateSize(claimedBy)
|
|
||||||
+ POINTER_BYTES + estimateSize(iso3166alpha3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Information to hold onto from processing a way in a boundary relation to determine the left/right region ID later. */
|
/**
|
||||||
|
* Information to hold onto from processing a way in a boundary relation to determine the left/right region ID later.
|
||||||
|
*/
|
||||||
private record CountryBoundaryComponent(
|
private record CountryBoundaryComponent(
|
||||||
int adminLevel,
|
int adminLevel,
|
||||||
boolean disputed,
|
boolean disputed,
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* Defines the logic for generating map elements for buildings in the {@code building} layer from source features.
|
* Defines the logic for generating map elements for buildings in the {@code building} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/building">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* building sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/building">OpenMapTiles building sql
|
||||||
|
* files</a>.
|
||||||
*/
|
*/
|
||||||
public class Building implements
|
public class Building implements
|
||||||
OpenMapTilesSchema.Building,
|
OpenMapTilesSchema.Building,
|
||||||
@@ -154,10 +155,8 @@ public class Building implements
|
|||||||
parseDoubleOrNull(element.buildingminLevel())
|
parseDoubleOrNull(element.buildingminLevel())
|
||||||
);
|
);
|
||||||
|
|
||||||
int renderHeight = (int) Math.ceil(height != null ? height
|
int renderHeight = (int) Math.ceil(height != null ? height : levels != null ? (levels * 3.66) : 5);
|
||||||
: levels != null ? (levels * 3.66) : 5);
|
int renderMinHeight = (int) Math.floor(minHeight != null ? minHeight : minLevels != null ? (minLevels * 3.66) : 0);
|
||||||
int renderMinHeight = (int) Math.floor(minHeight != null ? minHeight
|
|
||||||
: minLevels != null ? (minLevels * 3.66) : 0);
|
|
||||||
|
|
||||||
if (renderHeight < 3660 && renderMinHeight < 3660) {
|
if (renderHeight < 3660 && renderMinHeight < 3660) {
|
||||||
var feature = features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
|
var feature = features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
|
||||||
|
|||||||
@@ -45,15 +45,15 @@ import com.onthegomap.planetiler.util.Translations;
|
|||||||
/**
|
/**
|
||||||
* Defines the logic for generating map elements in the {@code housenumber} layer from source features.
|
* Defines the logic for generating map elements in the {@code housenumber} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/housenumber">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* housenumber sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/housenumber">OpenMapTiles housenumber sql
|
||||||
|
* files</a>.
|
||||||
*/
|
*/
|
||||||
public class Housenumber implements
|
public class Housenumber implements
|
||||||
OpenMapTilesSchema.Housenumber,
|
OpenMapTilesSchema.Housenumber,
|
||||||
Tables.OsmHousenumberPoint.Handler {
|
Tables.OsmHousenumberPoint.Handler {
|
||||||
|
|
||||||
public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) {
|
public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(Tables.OsmHousenumberPoint element, FeatureCollector features) {
|
public void process(Tables.OsmHousenumberPoint element, FeatureCollector features) {
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ import java.util.Set;
|
|||||||
* Defines the logic for generating map elements for natural land cover polygons like ice, sand, and forest in the
|
* Defines the logic for generating map elements for natural land cover polygons like ice, sand, and forest in the
|
||||||
* {@code landcover} layer from source features.
|
* {@code landcover} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/landcover">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* landcover sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/landcover">OpenMapTiles landcover sql
|
||||||
|
* files</a>.
|
||||||
*/
|
*/
|
||||||
public class Landcover implements
|
public class Landcover implements
|
||||||
OpenMapTilesSchema.Landcover,
|
OpenMapTilesSchema.Landcover,
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ import java.util.Set;
|
|||||||
* Defines the logic for generating map elements for man-made land use polygons like cemeteries, zoos, and hospitals in
|
* Defines the logic for generating map elements for man-made land use polygons like cemeteries, zoos, and hospitals in
|
||||||
* the {@code landuse} layer from source features.
|
* the {@code landuse} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/landuse">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* landuse sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/landuse">OpenMapTiles landuse sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Landuse implements
|
public class Landuse implements
|
||||||
OpenMapTilesSchema.Landuse,
|
OpenMapTilesSchema.Landuse,
|
||||||
@@ -75,8 +75,7 @@ public class Landuse implements
|
|||||||
FieldValues.CLASS_NEIGHBOURHOOD
|
FieldValues.CLASS_NEIGHBOURHOOD
|
||||||
);
|
);
|
||||||
|
|
||||||
public Landuse(Translations translations, PlanetilerConfig config, Stats stats) {
|
public Landuse(Translations translations, PlanetilerConfig config, Stats stats) {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features) {
|
public void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features) {
|
||||||
|
|||||||
@@ -65,8 +65,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
* Defines the logic for generating map elements for mountain peak label points in the {@code mountain_peak} layer from
|
* Defines the logic for generating map elements for mountain peak label points in the {@code mountain_peak} layer from
|
||||||
* source features.
|
* source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/mountain_peak">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* mountain_peak sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/mountain_peak">OpenMapTiles mountain_peak
|
||||||
|
* sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class MountainPeak implements
|
public class MountainPeak implements
|
||||||
BasemapProfile.NaturalEarthProcessor,
|
BasemapProfile.NaturalEarthProcessor,
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ import java.util.Locale;
|
|||||||
* Defines the logic for generating map elements for designated parks polygons and their label points in the {@code
|
* Defines the logic for generating map elements for designated parks polygons and their label points in the {@code
|
||||||
* park} layer from source features.
|
* park} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/park">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* park sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/park">OpenMapTiles park sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Park implements
|
public class Park implements
|
||||||
OpenMapTilesSchema.Park,
|
OpenMapTilesSchema.Park,
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ import org.locationtech.jts.geom.Point;
|
|||||||
* Defines the logic for generating label points for populated places like continents, countries, cities, and towns in
|
* Defines the logic for generating label points for populated places like continents, countries, cities, and towns in
|
||||||
* the {@code place} layer from source features.
|
* the {@code place} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/place">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* place sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/place">OpenMapTiles place sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Place implements
|
public class Place implements
|
||||||
OpenMapTilesSchema.Place,
|
OpenMapTilesSchema.Place,
|
||||||
@@ -142,11 +142,11 @@ public class Place implements
|
|||||||
// ORDER BY "rank" ASC NULLS LAST,
|
// ORDER BY "rank" ASC NULLS LAST,
|
||||||
.orderByInt(rank == null ? 15 : rank, 0, 15) // 4 bits
|
.orderByInt(rank == null ? 15 : rank, 0, 15) // 4 bits
|
||||||
// place ASC NULLS LAST,
|
// place ASC NULLS LAST,
|
||||||
.thenByInt(place == null ? 15 : place.ordinal(), 0, 15) // 4 bits
|
.thenByInt(place == null ? 15 : place.ordinal(), 0, 15) // 4 bits
|
||||||
// population DESC NULLS LAST,
|
// population DESC NULLS LAST,
|
||||||
.thenByLog(population, MAX_CITY_POPULATION, 1, 1 << (SORT_KEY_BITS - 13) - 1)
|
.thenByLog(population, MAX_CITY_POPULATION, 1, 1 << (SORT_KEY_BITS - 13) - 1)
|
||||||
// length(name) ASC
|
// length(name) ASC
|
||||||
.thenByInt(name == null ? 0 : name.length(), 0, 31) // 5 bits
|
.thenByInt(name == null ? 0 : name.length(), 0, 31) // 5 bits
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,9 +340,9 @@ public class Place implements
|
|||||||
|
|
||||||
int minzoom = rank != null && rank == 1 ? 2 :
|
int minzoom = rank != null && rank == 1 ? 2 :
|
||||||
rank != null && rank <= 8 ? Math.max(3, rank - 1) :
|
rank != null && rank <= 8 ? Math.max(3, rank - 1) :
|
||||||
placeType.ordinal() <= PlaceType.TOWN.ordinal() ? 7 :
|
placeType.ordinal() <= PlaceType.TOWN.ordinal() ? 7 :
|
||||||
placeType.ordinal() <= PlaceType.VILLAGE.ordinal() ? 8 :
|
placeType.ordinal() <= PlaceType.VILLAGE.ordinal() ? 8 :
|
||||||
placeType.ordinal() <= PlaceType.SUBURB.ordinal() ? 11 : 14;
|
placeType.ordinal() <= PlaceType.SUBURB.ordinal() ? 11 : 14;
|
||||||
|
|
||||||
var feature = features.point(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
|
var feature = features.point(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
|
||||||
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
|
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
|
||||||
@@ -420,9 +420,7 @@ public class Place implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information extracted from a natural earth place label that will be inspected when joining with OpenStreetMap
|
* Information extracted from a natural earth place label that will be inspected when joining with OpenStreetMap data.
|
||||||
* data.
|
|
||||||
*/
|
*/
|
||||||
private record NaturalEarthPoint(String name, String wikidata, int scaleRank, Set<String> names) {}
|
private record NaturalEarthPoint(String name, String wikidata, int scaleRank, Set<String> names) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ import java.util.Map;
|
|||||||
* Defines the logic for generating map elements for things like shops, parks, and schools in the {@code poi} layer from
|
* Defines the logic for generating map elements for things like shops, parks, and schools in the {@code poi} layer from
|
||||||
* source features.
|
* source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/poi">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* poi sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/poi">OpenMapTiles poi sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Poi implements
|
public class Poi implements
|
||||||
OpenMapTilesSchema.Poi,
|
OpenMapTilesSchema.Poi,
|
||||||
@@ -136,19 +136,8 @@ public class Poi implements
|
|||||||
setupPoiFeature(element, features.centroidIfConvex(LAYER_NAME));
|
setupPoiFeature(element, features.centroidIfConvex(LAYER_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends
|
private <T extends Tables.WithSubclass & Tables.WithStation & Tables.WithFunicular & Tables.WithSport & Tables.WithInformation & Tables.WithReligion & Tables.WithMappingKey & Tables.WithName & Tables.WithIndoor & Tables.WithLayer & Tables.WithSource> void setupPoiFeature(
|
||||||
Tables.WithSubclass &
|
T element, FeatureCollector.Feature output) {
|
||||||
Tables.WithStation &
|
|
||||||
Tables.WithFunicular &
|
|
||||||
Tables.WithSport &
|
|
||||||
Tables.WithInformation &
|
|
||||||
Tables.WithReligion &
|
|
||||||
Tables.WithMappingKey &
|
|
||||||
Tables.WithName &
|
|
||||||
Tables.WithIndoor &
|
|
||||||
Tables.WithLayer &
|
|
||||||
Tables.WithSource>
|
|
||||||
void setupPoiFeature(T element, FeatureCollector.Feature output) {
|
|
||||||
String rawSubclass = element.subclass();
|
String rawSubclass = element.subclass();
|
||||||
if ("station".equals(rawSubclass) && "subway".equals(element.station())) {
|
if ("station".equals(rawSubclass) && "subway".equals(element.station())) {
|
||||||
rawSubclass = "subway";
|
rawSubclass = "subway";
|
||||||
|
|||||||
@@ -80,8 +80,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
* Defines the logic for generating map elements for roads, shipways, railroads, and paths in the {@code transportation}
|
* Defines the logic for generating map elements for roads, shipways, railroads, and paths in the {@code transportation}
|
||||||
* layer from source features.
|
* layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/transportation">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* transportation sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/transportation">OpenMapTiles transportation
|
||||||
|
* sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Transportation implements
|
public class Transportation implements
|
||||||
OpenMapTilesSchema.Transportation,
|
OpenMapTilesSchema.Transportation,
|
||||||
@@ -214,7 +215,7 @@ public class Transportation implements
|
|||||||
private static String railwayClass(String value) {
|
private static String railwayClass(String value) {
|
||||||
return value == null ? null :
|
return value == null ? null :
|
||||||
RAILWAY_RAIL_VALUES.contains(value) ? "rail" :
|
RAILWAY_RAIL_VALUES.contains(value) ? "rail" :
|
||||||
RAILWAY_TRANSIT_VALUES.contains(value) ? "transit" : null;
|
RAILWAY_TRANSIT_VALUES.contains(value) ? "transit" : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String highwayClass(String highway, String publicTransport, String construction, String manMade) {
|
static String highwayClass(String highway, String publicTransport, String construction, String manMade) {
|
||||||
@@ -320,8 +321,8 @@ public class Transportation implements
|
|||||||
Geometry wayGeometry = element.source().worldGeometry();
|
Geometry wayGeometry = element.source().worldGeometry();
|
||||||
if (greatBritain.intersects(wayGeometry)) {
|
if (greatBritain.intersects(wayGeometry)) {
|
||||||
Transportation.RouteNetwork networkType =
|
Transportation.RouteNetwork networkType =
|
||||||
"motorway".equals(element.highway()) ? Transportation.RouteNetwork.GB_MOTORWAY
|
"motorway".equals(element.highway()) ? Transportation.RouteNetwork.GB_MOTORWAY :
|
||||||
: Transportation.RouteNetwork.GB_TRUNK;
|
Transportation.RouteNetwork.GB_TRUNK;
|
||||||
String network = "motorway".equals(element.highway()) ? "omt-gb-motorway" : "omt-gb-trunk";
|
String network = "motorway".equals(element.highway()) ? "omt-gb-motorway" : "omt-gb-trunk";
|
||||||
result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1,
|
result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1,
|
||||||
0));
|
0));
|
||||||
@@ -434,7 +435,7 @@ public class Transportation implements
|
|||||||
private boolean isPierPolygon(Tables.OsmHighwayLinestring element) {
|
private boolean isPierPolygon(Tables.OsmHighwayLinestring element) {
|
||||||
if ("pier".equals(element.manMade())) {
|
if ("pier".equals(element.manMade())) {
|
||||||
try {
|
try {
|
||||||
if (element.source().worldGeometry() instanceof LineString lineString && lineString.isClosed()) {
|
if (element.source().worldGeometry()instanceof LineString lineString && lineString.isClosed()) {
|
||||||
// ignore this because it's a polygon
|
// ignore this because it's a polygon
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ import java.util.function.Function;
|
|||||||
* Defines the logic for generating map elements for road, shipway, rail, and path names in the {@code
|
* Defines the logic for generating map elements for road, shipway, rail, and path names in the {@code
|
||||||
* transportation_name} layer from source features.
|
* transportation_name} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/transportation_name">OpenMapTiles
|
* This class is ported to Java from
|
||||||
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/transportation_name">OpenMapTiles
|
||||||
* transportation_name sql files</a>.
|
* transportation_name sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class TransportationName implements
|
public class TransportationName implements
|
||||||
@@ -222,7 +223,7 @@ public class TransportationName implements
|
|||||||
|
|
||||||
int minzoom = FieldValues.CLASS_TRUNK.equals(baseClass) ? 8 :
|
int minzoom = FieldValues.CLASS_TRUNK.equals(baseClass) ? 8 :
|
||||||
FieldValues.CLASS_MOTORWAY.equals(baseClass) ? 6 :
|
FieldValues.CLASS_MOTORWAY.equals(baseClass) ? 6 :
|
||||||
isLink ? 13 : 12; // fallback - get from line minzoom, but floor at 12
|
isLink ? 13 : 12; // fallback - get from line minzoom, but floor at 12
|
||||||
|
|
||||||
// inherit min zoom threshold from visible road, and ensure we never show a label on a road that's not visible yet.
|
// inherit min zoom threshold from visible road, and ensure we never show a label on a road that's not visible yet.
|
||||||
minzoom = Math.max(minzoom, transportation.getMinzoom(element, highwayClass));
|
minzoom = Math.max(minzoom, transportation.getMinzoom(element, highwayClass));
|
||||||
@@ -235,8 +236,8 @@ public class TransportationName implements
|
|||||||
.setAttr(Fields.REF, ref)
|
.setAttr(Fields.REF, ref)
|
||||||
.setAttr(Fields.REF_LENGTH, ref != null ? ref.length() : null)
|
.setAttr(Fields.REF_LENGTH, ref != null ? ref.length() : null)
|
||||||
.setAttr(Fields.NETWORK,
|
.setAttr(Fields.NETWORK,
|
||||||
(relation != null && relation.networkType() != null) ? relation.networkType().name
|
(relation != null && relation.networkType() != null) ? relation.networkType().name :
|
||||||
: !nullOrEmpty(ref) ? "road" : null)
|
!nullOrEmpty(ref) ? "road" : null)
|
||||||
.setAttr(Fields.CLASS, highwayClass)
|
.setAttr(Fields.CLASS, highwayClass)
|
||||||
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, null, highway))
|
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, null, highway))
|
||||||
.setMinPixelSize(0)
|
.setMinPixelSize(0)
|
||||||
@@ -314,7 +315,7 @@ public class TransportationName implements
|
|||||||
Function<Map<String, Object>, Double> lengthLimitCalculator =
|
Function<Map<String, Object>, Double> lengthLimitCalculator =
|
||||||
zoom >= 14 ? (p -> 0d) :
|
zoom >= 14 ? (p -> 0d) :
|
||||||
minLength > 0 ? (p -> minLength) :
|
minLength > 0 ? (p -> minLength) :
|
||||||
this::getMinLengthForName;
|
this::getMinLengthForName;
|
||||||
var result = FeatureMerge.mergeLineStrings(items, lengthLimitCalculator, tolerance, BUFFER_SIZE);
|
var result = FeatureMerge.mergeLineStrings(items, lengthLimitCalculator, tolerance, BUFFER_SIZE);
|
||||||
if (limitMerge) {
|
if (limitMerge) {
|
||||||
// remove temp keys that were just used to improve line merging
|
// remove temp keys that were just used to improve line merging
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ import com.onthegomap.planetiler.util.Translations;
|
|||||||
/**
|
/**
|
||||||
* Defines the logic for generating map elements for oceans and lakes in the {@code water} layer from source features.
|
* Defines the logic for generating map elements for oceans and lakes in the {@code water} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/water">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* water sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/water">OpenMapTiles water sql files</a>.
|
||||||
*/
|
*/
|
||||||
public class Water implements
|
public class Water implements
|
||||||
OpenMapTilesSchema.Water,
|
OpenMapTilesSchema.Water,
|
||||||
|
|||||||
@@ -61,8 +61,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
* Defines the logic for generating map elements for ocean and lake names in the {@code water_name} layer from source
|
* Defines the logic for generating map elements for ocean and lake names in the {@code water_name} layer from source
|
||||||
* features.
|
* features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/water_name">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* water_name sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/water_name">OpenMapTiles water_name sql
|
||||||
|
* files</a>.
|
||||||
*/
|
*/
|
||||||
public class WaterName implements
|
public class WaterName implements
|
||||||
OpenMapTilesSchema.WaterName,
|
OpenMapTilesSchema.WaterName,
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* Defines the logic for generating river map elements in the {@code waterway} layer from source features.
|
* Defines the logic for generating river map elements in the {@code waterway} layer from source features.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is ported to Java from <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/waterway">OpenMapTiles
|
* This class is ported to Java from
|
||||||
* waterway sql files</a>.
|
* <a href="https://github.com/openmaptiles/openmaptiles/tree/master/layers/waterway">OpenMapTiles waterway sql
|
||||||
|
* files</a>.
|
||||||
*/
|
*/
|
||||||
public class Waterway implements
|
public class Waterway implements
|
||||||
OpenMapTilesSchema.Waterway,
|
OpenMapTilesSchema.Waterway,
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ import java.util.stream.Stream;
|
|||||||
* Utilities to extract common name fields (name, name_en, name_de, name:latin, name:nonlatin, name_int) that the
|
* Utilities to extract common name fields (name, name_en, name_de, name:latin, name:nonlatin, name_int) that the
|
||||||
* OpenMapTiles schema uses across any map element with a name.
|
* OpenMapTiles schema uses across any map element with a name.
|
||||||
* <p>
|
* <p>
|
||||||
* Ported from <a href="https://github.com/openmaptiles/openmaptiles-tools/blob/master/sql/zzz_language.sql">openmaptiles-tools</a>.
|
* Ported from
|
||||||
|
* <a href="https://github.com/openmaptiles/openmaptiles-tools/blob/master/sql/zzz_language.sql">openmaptiles-tools</a>.
|
||||||
*/
|
*/
|
||||||
public class LanguageUtils {
|
public class LanguageUtils {
|
||||||
|
|
||||||
@@ -101,12 +102,12 @@ public class LanguageUtils {
|
|||||||
* element should have, derived from name, int_name, name:en, and name:de tags on the input element.
|
* element should have, derived from name, int_name, name:en, and name:de tags on the input element.
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>name is the original name value from the element</li>
|
* <li>name is the original name value from the element</li>
|
||||||
* <li>name_en is the original name:en value from the element, or name if missing</li>
|
* <li>name_en is the original name:en value from the element, or name if missing</li>
|
||||||
* <li>name_de is the original name:de value from the element, or name/ name_en if missing</li>
|
* <li>name_de is the original name:de value from the element, or name/ name_en if missing</li>
|
||||||
* <li>name:latin is the first of name, int_name, or any name: attribute that contains only latin characters</li>
|
* <li>name:latin is the first of name, int_name, or any name: attribute that contains only latin characters</li>
|
||||||
* <li>name:nonlatin is any nonlatin part of name if present</li>
|
* <li>name:nonlatin is any nonlatin part of name if present</li>
|
||||||
* <li>name_int is the first of int_name name:en name:latin name</li>
|
* <li>name_int is the first of int_name name:en name:latin name</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> getNamesWithoutTranslations(Map<String, Object> tags) {
|
public static Map<String, Object> getNamesWithoutTranslations(Map<String, Object> tags) {
|
||||||
@@ -126,8 +127,8 @@ public class LanguageUtils {
|
|||||||
String nameDe = string(tags.get("name:de"));
|
String nameDe = string(tags.get("name:de"));
|
||||||
|
|
||||||
boolean isLatin = containsOnlyLatinCharacters(name);
|
boolean isLatin = containsOnlyLatinCharacters(name);
|
||||||
String latin = isLatin ? name
|
String latin = isLatin ? name :
|
||||||
: Stream.concat(Stream.of(nameEn, intName, nameDe), getAllNameTranslationsBesidesEnglishAndGerman(tags))
|
Stream.concat(Stream.of(nameEn, intName, nameDe), getAllNameTranslationsBesidesEnglishAndGerman(tags))
|
||||||
.filter(LanguageUtils::containsOnlyLatinCharacters)
|
.filter(LanguageUtils::containsOnlyLatinCharacters)
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
if (latin == null && translations != null && translations.getShouldTransliterate()) {
|
if (latin == null && translations != null && translations.getShouldTransliterate()) {
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ public abstract class AbstractLayerTest {
|
|||||||
if (vals[i - 1] > vals[i]) {
|
if (vals[i - 1] > vals[i]) {
|
||||||
fail(
|
fail(
|
||||||
Arrays.toString(vals) +
|
Arrays.toString(vals) +
|
||||||
System.lineSeparator() + "element at " + (i - 1) + " (" + vals[i - 1] + ") is greater than element at " + i
|
System.lineSeparator() + "element at " + (i - 1) + " (" + vals[i - 1] + ") is greater than element at " +
|
||||||
+ " (" + vals[i] + ")");
|
i + " (" + vals[i] + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -414,60 +414,60 @@ public class BoundaryTest extends AbstractLayerTest {
|
|||||||
|
|
||||||
// shared edge
|
// shared edge
|
||||||
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
||||||
newLineString(0, 0, 0, 10),
|
newLineString(0, 0, 0, 10),
|
||||||
Map.of(),
|
Map.of(),
|
||||||
OSM_SOURCE,
|
OSM_SOURCE,
|
||||||
null,
|
null,
|
||||||
3,
|
3,
|
||||||
Stream.concat(
|
Stream.concat(
|
||||||
profile.preprocessOsmRelation(country1).stream(),
|
profile.preprocessOsmRelation(country1).stream(),
|
||||||
profile.preprocessOsmRelation(country2).stream()
|
profile.preprocessOsmRelation(country2).stream()
|
||||||
).map(r -> new OsmReader.RelationMember<>("", r)).toList()
|
).map(r -> new OsmReader.RelationMember<>("", r)).toList()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
// other 2 edges of country 1
|
// other 2 edges of country 1
|
||||||
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
||||||
newLineString(0, 0, 5, 10),
|
newLineString(0, 0, 5, 10),
|
||||||
Map.of(),
|
Map.of(),
|
||||||
OSM_SOURCE,
|
OSM_SOURCE,
|
||||||
null,
|
null,
|
||||||
4,
|
4,
|
||||||
profile.preprocessOsmRelation(country1).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
profile.preprocessOsmRelation(country1).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
||||||
.toList()
|
.toList()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
||||||
newLineString(0, 10, 5, 10),
|
newLineString(0, 10, 5, 10),
|
||||||
Map.of(),
|
Map.of(),
|
||||||
OSM_SOURCE,
|
OSM_SOURCE,
|
||||||
null,
|
null,
|
||||||
4,
|
4,
|
||||||
profile.preprocessOsmRelation(country1).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
profile.preprocessOsmRelation(country1).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
||||||
.toList()
|
.toList()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
// other 2 edges of country 2
|
// other 2 edges of country 2
|
||||||
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
||||||
newLineString(0, 0, -5, 10),
|
newLineString(0, 0, -5, 10),
|
||||||
Map.of(),
|
Map.of(),
|
||||||
OSM_SOURCE,
|
OSM_SOURCE,
|
||||||
null,
|
null,
|
||||||
4,
|
4,
|
||||||
profile.preprocessOsmRelation(country2).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
profile.preprocessOsmRelation(country2).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
||||||
.toList()
|
.toList()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
assertFeatures(14, List.of(), process(SimpleFeature.createFakeOsmFeature(
|
||||||
newLineString(0, 10, -5, 10),
|
newLineString(0, 10, -5, 10),
|
||||||
Map.of(),
|
Map.of(),
|
||||||
OSM_SOURCE,
|
OSM_SOURCE,
|
||||||
null,
|
null,
|
||||||
4,
|
4,
|
||||||
profile.preprocessOsmRelation(country2).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
profile.preprocessOsmRelation(country2).stream().map(r -> new OsmReader.RelationMember<>("", r))
|
||||||
.toList()
|
.toList()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
List<FeatureCollector.Feature> features = new ArrayList<>();
|
List<FeatureCollector.Feature> features = new ArrayList<>();
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ public class LandcoverTest extends AbstractLayerTest {
|
|||||||
throws GeometryException {
|
throws GeometryException {
|
||||||
assertEquals(expected,
|
assertEquals(expected,
|
||||||
profile.postProcessLayerFeatures("landcover", zoom, in).stream().map(
|
profile.postProcessLayerFeatures("landcover", zoom, in).stream().map(
|
||||||
VectorTile.Feature::attrs)
|
VectorTile.Feature::attrs)
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ public class LanduseTest extends AbstractLayerTest {
|
|||||||
"_minzoom", 9,
|
"_minzoom", 9,
|
||||||
"_maxzoom", 14
|
"_maxzoom", 14
|
||||||
)), process(polygonFeature(Map.of(
|
)), process(polygonFeature(Map.of(
|
||||||
"landuse", "railway",
|
"landuse", "railway",
|
||||||
"amenity", "school"
|
"amenity", "school"
|
||||||
))));
|
))));
|
||||||
assertFeatures(13, List.of(Map.of("_layer", "poi"), Map.of(
|
assertFeatures(13, List.of(Map.of("_layer", "poi"), Map.of(
|
||||||
"_layer", "landuse",
|
"_layer", "landuse",
|
||||||
"class", "school",
|
"class", "school",
|
||||||
@@ -66,8 +66,8 @@ public class LanduseTest extends AbstractLayerTest {
|
|||||||
"_layer", "landuse",
|
"_layer", "landuse",
|
||||||
"class", "cemetery"
|
"class", "cemetery"
|
||||||
)), process(polygonFeature(Map.of(
|
)), process(polygonFeature(Map.of(
|
||||||
"amenity", "grave_yard"
|
"amenity", "grave_yard"
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class ParkTest extends AbstractLayerTest {
|
|||||||
"name", "Grand Canyon National Park",
|
"name", "Grand Canyon National Park",
|
||||||
"name_int", "Grand Canyon National Park",
|
"name_int", "Grand Canyon National Park",
|
||||||
"name:latin", "Grand Canyon National Park",
|
"name:latin", "Grand Canyon National Park",
|
||||||
// "name:es", "es name", // don't include all translations
|
// "name:es", "es name", // don't include all translations
|
||||||
"_minzoom", 5,
|
"_minzoom", 5,
|
||||||
"_maxzoom", 14
|
"_maxzoom", 14
|
||||||
)), process(polygonFeature(Map.of(
|
)), process(polygonFeature(Map.of(
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ public class WaterTest extends AbstractLayerTest {
|
|||||||
"_minzoom", 6,
|
"_minzoom", 6,
|
||||||
"_maxzoom", 14
|
"_maxzoom", 14
|
||||||
)), process(polygonFeature(Map.of(
|
)), process(polygonFeature(Map.of(
|
||||||
"leisure", "swimming_pool"
|
"leisure", "swimming_pool"
|
||||||
))));
|
))));
|
||||||
assertFeatures(14, List.of(), process(polygonFeature(Map.of(
|
assertFeatures(14, List.of(), process(polygonFeature(Map.of(
|
||||||
"natural", "bay"
|
"natural", "bay"
|
||||||
))));
|
))));
|
||||||
|
|||||||
Reference in New Issue
Block a user