kopia lustrzana https://github.com/onthegomap/planetiler
275 wiersze
10 KiB
Java
275 wiersze
10 KiB
Java
package com.onthegomap.flatmap.openmaptiles;
|
|
|
|
import static com.onthegomap.flatmap.openmaptiles.Expression.FALSE;
|
|
import static com.onthegomap.flatmap.openmaptiles.Expression.TRUE;
|
|
import static com.onthegomap.flatmap.openmaptiles.Expression.matchType;
|
|
|
|
import com.graphhopper.reader.ReaderElement;
|
|
import com.graphhopper.reader.ReaderElementUtils;
|
|
import com.graphhopper.reader.ReaderRelation;
|
|
import com.onthegomap.flatmap.Arguments;
|
|
import com.onthegomap.flatmap.FeatureCollector;
|
|
import com.onthegomap.flatmap.Profile;
|
|
import com.onthegomap.flatmap.SourceFeature;
|
|
import com.onthegomap.flatmap.Translations;
|
|
import com.onthegomap.flatmap.VectorTileEncoder;
|
|
import com.onthegomap.flatmap.geo.GeometryException;
|
|
import com.onthegomap.flatmap.monitoring.Stats;
|
|
import com.onthegomap.flatmap.openmaptiles.generated.OpenMapTilesSchema;
|
|
import com.onthegomap.flatmap.openmaptiles.generated.Tables;
|
|
import com.onthegomap.flatmap.read.OpenStreetMapReader;
|
|
import com.onthegomap.flatmap.read.ReaderFeature;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.function.Consumer;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
public class OpenMapTilesProfile implements Profile {
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(OpenMapTilesProfile.class);
|
|
|
|
public static final String LAKE_CENTERLINE_SOURCE = "lake_centerlines";
|
|
public static final String WATER_POLYGON_SOURCE = "water_polygons";
|
|
public static final String NATURAL_EARTH_SOURCE = "natural_earth";
|
|
public static final String OSM_SOURCE = "osm";
|
|
private final MultiExpression.MultiExpressionIndex<Tables.Constructor> osmPointMappings;
|
|
private final MultiExpression.MultiExpressionIndex<Tables.Constructor> osmLineMappings;
|
|
private final MultiExpression.MultiExpressionIndex<Tables.Constructor> osmPolygonMappings;
|
|
private final List<Layer> layers;
|
|
private final Map<Class<? extends Tables.Row>, List<Tables.RowHandler<Tables.Row>>> osmDispatchMap;
|
|
private final Map<String, FeaturePostProcessor> postProcessors;
|
|
private final List<NaturalEarthProcessor> naturalEarthProcessors;
|
|
private final List<OsmWaterPolygonProcessor> osmWaterProcessors;
|
|
private final List<LakeCenterlineProcessor> lakeCenterlineProcessors;
|
|
private final List<OsmAllProcessor> osmAllProcessors;
|
|
private final List<OsmRelationPreprocessor> osmRelationPreprocessors;
|
|
private final List<FinishHandler> finishHandlers;
|
|
|
|
private MultiExpression.MultiExpressionIndex<Tables.Constructor> indexForType(String type) {
|
|
return Tables.MAPPINGS
|
|
.filterKeys(constructor -> {
|
|
// exclude any mapping that generates a class we don't have a handler for
|
|
var clz = constructor.create(new ReaderFeature(null, Map.of(), 0), "").getClass();
|
|
return !osmDispatchMap.getOrDefault(clz, List.of()).isEmpty();
|
|
})
|
|
.replace(matchType(type), TRUE)
|
|
.replace(e -> e instanceof Expression.MatchType, FALSE)
|
|
.simplify()
|
|
.index();
|
|
}
|
|
|
|
public OpenMapTilesProfile(Translations translations, Arguments arguments, Stats stats) {
|
|
List<String> onlyLayers = arguments.get("only_layers", "Include only certain layers", new String[]{});
|
|
List<String> excludeLayers = arguments.get("exclude_layers", "Exclude certain layers", new String[]{});
|
|
this.layers = OpenMapTilesSchema.createInstances(translations, arguments, stats)
|
|
.stream()
|
|
.filter(l -> (onlyLayers.isEmpty() || onlyLayers.contains(l.name())) && !excludeLayers.contains(l.name()))
|
|
.toList();
|
|
osmDispatchMap = new HashMap<>();
|
|
Tables.generateDispatchMap(layers).forEach((clazz, handlers) -> {
|
|
osmDispatchMap.put(clazz, handlers.stream().map(handler -> {
|
|
@SuppressWarnings("unchecked") Tables.RowHandler<Tables.Row> rawHandler = (Tables.RowHandler<Tables.Row>) handler;
|
|
return rawHandler;
|
|
}).toList());
|
|
});
|
|
this.osmPointMappings = indexForType("point");
|
|
this.osmLineMappings = indexForType("linestring");
|
|
this.osmPolygonMappings = indexForType("polygon");
|
|
postProcessors = new HashMap<>();
|
|
osmAllProcessors = new ArrayList<>();
|
|
lakeCenterlineProcessors = new ArrayList<>();
|
|
naturalEarthProcessors = new ArrayList<>();
|
|
osmWaterProcessors = new ArrayList<>();
|
|
osmRelationPreprocessors = new ArrayList<>();
|
|
finishHandlers = new ArrayList<>();
|
|
for (Layer layer : layers) {
|
|
if (layer instanceof FeaturePostProcessor postProcessor) {
|
|
postProcessors.put(layer.name(), postProcessor);
|
|
}
|
|
if (layer instanceof OsmAllProcessor processor) {
|
|
osmAllProcessors.add(processor);
|
|
}
|
|
if (layer instanceof OsmWaterPolygonProcessor processor) {
|
|
osmWaterProcessors.add(processor);
|
|
}
|
|
if (layer instanceof LakeCenterlineProcessor processor) {
|
|
lakeCenterlineProcessors.add(processor);
|
|
}
|
|
if (layer instanceof NaturalEarthProcessor processor) {
|
|
naturalEarthProcessors.add(processor);
|
|
}
|
|
if (layer instanceof OsmRelationPreprocessor processor) {
|
|
osmRelationPreprocessors.add(processor);
|
|
}
|
|
if (layer instanceof FinishHandler processor) {
|
|
finishHandlers.add(processor);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void release() {
|
|
layers.forEach(Layer::release);
|
|
}
|
|
|
|
@Override
|
|
public List<VectorTileEncoder.Feature> postProcessLayerFeatures(String layer, int zoom,
|
|
List<VectorTileEncoder.Feature> items) throws GeometryException {
|
|
FeaturePostProcessor postProcesor = postProcessors.get(layer);
|
|
List<VectorTileEncoder.Feature> result = null;
|
|
if (postProcesor != null) {
|
|
result = postProcesor.postProcess(zoom, items);
|
|
}
|
|
return result == null ? items : result;
|
|
}
|
|
|
|
@Override
|
|
public List<OpenStreetMapReader.RelationInfo> preprocessOsmRelation(ReaderRelation relation) {
|
|
List<OpenStreetMapReader.RelationInfo> result = null;
|
|
for (int i = 0; i < osmRelationPreprocessors.size(); i++) {
|
|
List<OpenStreetMapReader.RelationInfo> thisResult = osmRelationPreprocessors.get(i)
|
|
.preprocessOsmRelation(relation);
|
|
if (thisResult != null) {
|
|
if (result == null) {
|
|
result = new ArrayList<>(thisResult);
|
|
} else {
|
|
result.addAll(thisResult);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void processFeature(SourceFeature sourceFeature, FeatureCollector features) {
|
|
switch (sourceFeature.getSource()) {
|
|
case OSM_SOURCE -> {
|
|
for (var match : getTableMatches(sourceFeature)) {
|
|
var row = match.match().create(sourceFeature, match.keys().get(0));
|
|
var handlers = osmDispatchMap.get(row.getClass());
|
|
if (handlers != null) {
|
|
for (Tables.RowHandler<Tables.Row> handler : handlers) {
|
|
handler.process(row, features);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < osmAllProcessors.size(); i++) {
|
|
osmAllProcessors.get(i).processAllOsm(sourceFeature, features);
|
|
}
|
|
}
|
|
case LAKE_CENTERLINE_SOURCE -> {
|
|
for (LakeCenterlineProcessor lakeCenterlineProcessor : lakeCenterlineProcessors) {
|
|
lakeCenterlineProcessor.processLakeCenterline(sourceFeature, features);
|
|
}
|
|
}
|
|
case NATURAL_EARTH_SOURCE -> {
|
|
for (NaturalEarthProcessor naturalEarthProcessor : naturalEarthProcessors) {
|
|
naturalEarthProcessor.processNaturalEarth(sourceFeature.getSourceLayer(), sourceFeature, features);
|
|
}
|
|
}
|
|
case WATER_POLYGON_SOURCE -> {
|
|
for (OsmWaterPolygonProcessor osmWaterProcessor : osmWaterProcessors) {
|
|
osmWaterProcessor.processOsmWater(sourceFeature, features);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
List<MultiExpression.MultiExpressionIndex.MatchWithTriggers<Tables.Constructor>> getTableMatches(
|
|
SourceFeature sourceFeature) {
|
|
List<MultiExpression.MultiExpressionIndex.MatchWithTriggers<Tables.Constructor>> result = null;
|
|
if (sourceFeature.isPoint()) {
|
|
result = osmPointMappings.getMatchesWithTriggers(sourceFeature.properties());
|
|
} else {
|
|
if (sourceFeature.canBeLine()) {
|
|
result = osmLineMappings.getMatchesWithTriggers(sourceFeature.properties());
|
|
if (sourceFeature.canBePolygon()) {
|
|
result.addAll(osmPolygonMappings.getMatchesWithTriggers(sourceFeature.properties()));
|
|
}
|
|
} else if (sourceFeature.canBePolygon()) {
|
|
result = osmPolygonMappings.getMatchesWithTriggers(sourceFeature.properties());
|
|
}
|
|
}
|
|
return result == null ? List.of() : result;
|
|
}
|
|
|
|
@Override
|
|
public void finish(String sourceName, FeatureCollector.Factory featureCollectors,
|
|
Consumer<FeatureCollector.Feature> next) {
|
|
for (var handler : finishHandlers) {
|
|
handler.finish(sourceName, featureCollectors, next);
|
|
}
|
|
}
|
|
|
|
public interface NaturalEarthProcessor {
|
|
|
|
void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features);
|
|
}
|
|
|
|
public interface LakeCenterlineProcessor {
|
|
|
|
void processLakeCenterline(SourceFeature feature, FeatureCollector features);
|
|
}
|
|
|
|
public interface OsmWaterPolygonProcessor {
|
|
|
|
void processOsmWater(SourceFeature feature, FeatureCollector features);
|
|
}
|
|
|
|
public interface OsmAllProcessor {
|
|
|
|
void processAllOsm(SourceFeature feature, FeatureCollector features);
|
|
}
|
|
|
|
public interface FinishHandler {
|
|
|
|
void finish(String sourceName, FeatureCollector.Factory featureCollectors,
|
|
Consumer<FeatureCollector.Feature> next);
|
|
}
|
|
|
|
public interface OsmRelationPreprocessor {
|
|
|
|
List<OpenStreetMapReader.RelationInfo> preprocessOsmRelation(ReaderRelation relation);
|
|
}
|
|
|
|
public interface FeaturePostProcessor {
|
|
|
|
List<VectorTileEncoder.Feature> postProcess(int zoom, List<VectorTileEncoder.Feature> items)
|
|
throws GeometryException;
|
|
}
|
|
|
|
@Override
|
|
public boolean caresAboutWikidataTranslation(ReaderElement elem) {
|
|
var tags = ReaderElementUtils.getProperties(elem);
|
|
return switch (elem.getType()) {
|
|
case ReaderElement.WAY -> osmPolygonMappings.matches(tags) || osmLineMappings.matches(tags);
|
|
case ReaderElement.NODE -> osmPointMappings.matches(tags);
|
|
case ReaderElement.RELATION -> osmPolygonMappings.matches(tags);
|
|
default -> false;
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public String name() {
|
|
return OpenMapTilesSchema.NAME;
|
|
}
|
|
|
|
@Override
|
|
public String description() {
|
|
return OpenMapTilesSchema.DESCRIPTION;
|
|
}
|
|
|
|
@Override
|
|
public String attribution() {
|
|
return OpenMapTilesSchema.ATTRIBUTION;
|
|
}
|
|
|
|
@Override
|
|
public String version() {
|
|
return OpenMapTilesSchema.VERSION;
|
|
}
|
|
}
|