pull/1/head
Mike Barry 2021-05-31 06:21:53 -04:00
rodzic df1d6afa54
commit fd3267ac85
5 zmienionych plików z 177 dodań i 34 usunięć

Wyświetl plik

@ -2,6 +2,9 @@ package com.onthegomap.flatmap;
import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.geo.GeometryException;
import com.onthegomap.flatmap.read.OpenStreetMapReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Lineal;
@ -11,11 +14,14 @@ public abstract class SourceFeature {
private final Map<String, Object> properties;
private final String source;
private final String sourceLayer;
private final List<OpenStreetMapReader.RelationInfo> relationInfos;
protected SourceFeature(Map<String, Object> properties, String source, String sourceLayer) {
protected SourceFeature(Map<String, Object> properties, String source, String sourceLayer,
List<OpenStreetMapReader.RelationInfo> relationInfos) {
this.properties = properties;
this.source = source;
this.sourceLayer = sourceLayer;
this.relationInfos = relationInfos;
}
public abstract Geometry latLonGeometry() throws GeometryException;
@ -147,4 +153,19 @@ public abstract class SourceFeature {
public String getSourceLayer() {
return sourceLayer;
}
public <T extends OpenStreetMapReader.RelationInfo> List<T> relationInfo(Class<T> relationInfoClass) {
List<T> result = null;
if (relationInfos != null) {
for (OpenStreetMapReader.RelationInfo info : relationInfos) {
if (relationInfoClass.isInstance(info)) {
if (result == null) {
result = new ArrayList<>();
}
result.add(relationInfoClass.cast(info));
}
}
}
return result == null ? List.of() : result;
}
}

Wyświetl plik

@ -209,7 +209,18 @@ public class OpenStreetMapReader implements Closeable, MemoryEstimator.HasEstima
}
boolean closed = nodes.size() > 1 && nodes.get(0) == nodes.get(nodes.size() - 1);
String area = way.getTag("area");
return new WaySourceFeature(way, closed, area, nodeCache);
LongArrayList relationIds = wayToRelations.get(way.getId());
List<RelationInfo> rels = null;
if (!relationIds.isEmpty()) {
rels = new ArrayList<>(relationIds.size());
for (int r = 0; r < relationIds.size(); r++) {
RelationInfo rel = relationInfo.get(relationIds.get(r));
if (rel != null) {
rels.add(rel);
}
}
}
return new WaySourceFeature(way, closed, area, nodeCache, rels);
}
SourceFeature processNodePass2(ReaderNode node) {
@ -237,10 +248,10 @@ public class OpenStreetMapReader implements Closeable, MemoryEstimator.HasEstima
nodeDb.close();
}
public static class RelationInfo implements MemoryEstimator.HasEstimate {
public interface RelationInfo extends MemoryEstimator.HasEstimate {
@Override
public long estimateMemoryUsageBytes() {
default long estimateMemoryUsageBytes() {
return 0;
}
}
@ -252,8 +263,9 @@ public class OpenStreetMapReader implements Closeable, MemoryEstimator.HasEstima
final boolean point;
final long osmId;
public ProxyFeature(ReaderElement elem, boolean point, boolean line, boolean polygon) {
super(ReaderElementUtils.getProperties(elem), name, null);
public ProxyFeature(ReaderElement elem, boolean point, boolean line, boolean polygon,
List<RelationInfo> relationInfo) {
super(ReaderElementUtils.getProperties(elem), name, null, relationInfo);
this.point = point;
this.line = line;
this.polygon = polygon;
@ -298,7 +310,7 @@ public class OpenStreetMapReader implements Closeable, MemoryEstimator.HasEstima
private final double lat;
NodeSourceFeature(ReaderNode node) {
super(node, true, false, false);
super(node, true, false, false, null);
this.lon = node.getLon();
this.lat = node.getLat();
}
@ -327,10 +339,12 @@ public class OpenStreetMapReader implements Closeable, MemoryEstimator.HasEstima
private final NodeLocationProvider nodeCache;
private final LongArrayList nodeIds;
public WaySourceFeature(ReaderWay way, boolean closed, String area, NodeLocationProvider nodeCache) {
public WaySourceFeature(ReaderWay way, boolean closed, String area, NodeLocationProvider nodeCache,
List<RelationInfo> relationInfo) {
super(way, false,
(!closed || !"yes".equals(area)) && way.getNodes().size() >= 2,
(closed && !"no".equals(area)) && way.getNodes().size() >= 4
(closed && !"no".equals(area)) && way.getNodes().size() >= 4,
relationInfo
);
this.nodeIds = way.getNodes();
this.nodeCache = nodeCache;
@ -373,7 +387,7 @@ public class OpenStreetMapReader implements Closeable, MemoryEstimator.HasEstima
private final NodeLocationProvider nodeCache;
public MultipolygonSourceFeature(ReaderRelation relation, NodeLocationProvider nodeCache) {
super(relation, false, false, true);
super(relation, false, false, true, null);
this.relation = relation;
this.nodeCache = nodeCache;
}

Wyświetl plik

@ -19,7 +19,7 @@ public class ReaderFeature extends SourceFeature {
}
public ReaderFeature(Geometry latLonGeometry, Map<String, Object> properties, String source, String sourceLayer) {
super(properties, source, sourceLayer);
super(properties, source, sourceLayer, null);
this.latLonGeometry = latLonGeometry;
this.properties = properties;
}

Wyświetl plik

@ -61,6 +61,11 @@ public class FlatMapTest {
private static final int Z4_TILES = 1 << 4;
private final Stats stats = new Stats.InMemory();
private static <T extends ReaderElement> T with(T elem, Consumer<T> fn) {
fn.accept(elem);
return elem;
}
private void processReaderFeatures(FeatureGroup featureGroup, Profile profile, CommonParams config,
List<? extends SourceFeature> features) {
new Reader(profile, stats, "test") {
@ -97,15 +102,10 @@ public class FlatMapTest {
}
}
private interface Runner {
void run(FeatureGroup featureGroup, Profile profile, CommonParams config) throws Exception;
}
private FlatMapResults run(
Map<String, String> args,
Runner runner,
BiConsumer<SourceFeature, FeatureCollector> profileFunction
Profile profile
) throws Exception {
CommonParams config = CommonParams.from(Arguments.of(args));
var translations = Translations.defaultProvider(List.of());
@ -113,7 +113,6 @@ public class FlatMapTest {
LongLongMap nodeLocations = LongLongMap.newInMemorySortedTable();
FeatureSort featureDb = FeatureSort.newInMemory();
FeatureGroup featureGroup = new FeatureGroup(featureDb, profile1, stats);
var profile = TestProfile.processSourceFeatures(profileFunction);
runner.run(featureGroup, profile, config);
featureGroup.sorter().sort();
try (Mbtiles db = Mbtiles.newInMemoryDatabase()) {
@ -134,7 +133,7 @@ public class FlatMapTest {
return run(
args,
(featureGroup, profile, config) -> processReaderFeatures(featureGroup, profile, config, features),
profileFunction
TestProfile.processSourceFeatures(profileFunction)
);
}
@ -146,7 +145,20 @@ public class FlatMapTest {
return run(
args,
(featureGroup, profile, config) -> processOsmFeatures(featureGroup, profile, config, features),
profileFunction
TestProfile.processSourceFeatures(profileFunction)
);
}
private FlatMapResults runWithOsmElements(
Map<String, String> args,
List<ReaderElement> features,
Function<ReaderRelation, List<OpenStreetMapReader.RelationInfo>> preprocessOsmRelation,
BiConsumer<SourceFeature, FeatureCollector> profileFunction
) throws Exception {
return run(
args,
(featureGroup, profile, config) -> processOsmFeatures(featureGroup, profile, config, features),
new TestProfile(profileFunction, preprocessOsmRelation, (a, b, c) -> null)
);
}
@ -654,11 +666,6 @@ public class FlatMapTest {
), results.tiles);
}
private static <T extends ReaderElement> T with(T elem, Consumer<T> fn) {
fn.accept(elem);
return elem;
}
@Test
public void testOsmLine() throws Exception {
var results = runWithOsmElements(
@ -741,15 +748,6 @@ public class FlatMapTest {
)), sortListValues(results.tiles));
}
private <K extends Comparable<? super K>, V extends List<?>> Map<K, ?> sortListValues(Map<K, V> input) {
Map<K, List<?>> result = new TreeMap<>();
for (var entry : input.entrySet()) {
List<?> sorted = entry.getValue().stream().sorted(Comparator.comparing(Object::toString)).toList();
result.put(entry.getKey(), sorted);
}
return result;
}
@Test
public void testOsmMultipolygon() throws Exception {
var results = runWithOsmElements(
@ -812,11 +810,80 @@ public class FlatMapTest {
), results.tiles);
}
@Test
public void testOsmLineInRelation() throws Exception {
record TestRelationInfo(String name) implements OpenStreetMapReader.RelationInfo {}
var results = runWithOsmElements(
Map.of("threads", "1"),
List.of(
new ReaderNode(1, 0, 0),
new ReaderNode(2, GeoUtils.getWorldLat(0.375), 0),
new ReaderNode(3, GeoUtils.getWorldLat(0.25), 0),
new ReaderNode(4, GeoUtils.getWorldLat(0.125), 0),
with(new ReaderWay(5), way -> {
way.setTag("attr", "value1");
way.getNodes().add(1, 2);
}),
with(new ReaderWay(6), way -> {
way.setTag("attr", "value2");
way.getNodes().add(3, 4);
}),
with(new ReaderRelation(6), rel -> {
rel.setTag("name", "relation name");
rel.add(new ReaderRelation.Member(ReaderRelation.WAY, 6, "role"));
})
),
(relation) -> {
if (relation.hasTag("name", "relation name")) {
return List.of(new TestRelationInfo(relation.getTag("name")));
}
return null;
}, (in, features) -> {
List<TestRelationInfo> relationInfos = in.relationInfo(TestRelationInfo.class);
if (in.canBeLine()) {
features.line("layer")
.setZoomRange(0, 0)
.setAttr("relname", relationInfos.stream()
.findFirst().map(TestRelationInfo::name)
.orElse(null))
.inheritFromSource("attr");
}
}
);
assertSubmap(sortListValues(Map.of(
TileCoord.ofXYZ(0, 0, 0), List.of(
feature(newLineString(128, 128, 128, 0.375 * 256), Map.of(
"attr", "value1"
)),
feature(newLineString(128, 0.25 * 256, 128, 0.125 * 256), Map.of(
"attr", "value2",
"relname", "relation name"
))
)
)), sortListValues(results.tiles));
}
private <K extends Comparable<? super K>, V extends List<?>> Map<K, ?> sortListValues(Map<K, V> input) {
Map<K, List<?>> result = new TreeMap<>();
for (var entry : input.entrySet()) {
List<?> sorted = entry.getValue().stream().sorted(Comparator.comparing(Object::toString)).toList();
result.put(entry.getKey(), sorted);
}
return result;
}
private Map.Entry<TileCoord, List<TestUtils.ComparableFeature>> newTileEntry(int x, int y, int z,
List<TestUtils.ComparableFeature> features) {
return Map.entry(TileCoord.ofXYZ(x, y, z), features);
}
private interface Runner {
void run(FeatureGroup featureGroup, Profile profile, CommonParams config) throws Exception;
}
private interface LayerPostprocessFunction {
List<VectorTileEncoder.Feature> process(String layer, int zoom, List<VectorTileEncoder.Feature> items);

Wyświetl plik

@ -643,6 +643,47 @@ public class OpenStreetMapReaderTest {
assertThrows(GeometryException.class, feature::validatedPolygon);
}
@Test
public void testWayInRelation() {
record OtherRelInfo() implements OpenStreetMapReader.RelationInfo {}
record TestRelInfo(String name) implements OpenStreetMapReader.RelationInfo {
@Override
public long estimateMemoryUsageBytes() {
return 10 + name.length();
}
}
OpenStreetMapReader reader = new OpenStreetMapReader(
osmSource,
longLongMap,
new Profile.NullProfile() {
@Override
public List<OpenStreetMapReader.RelationInfo> preprocessOsmRelation(ReaderRelation relation) {
return List.of(new TestRelInfo("name"));
}
},
stats
);
var nodeCache = reader.newNodeGeometryCache();
var node1 = new ReaderNode(1, 0, 0);
var node2 = node(2, 0.75, 0.75);
var way = new ReaderWay(3);
way.getNodes().add(node1.getId(), node2.getId());
way.setTag("key", "value");
var relation = new ReaderRelation(4);
relation.add(new ReaderRelation.Member(ReaderRelation.Member.WAY, 3, ""));
reader.processPass1(node1);
reader.processPass1(node2);
reader.processPass1(way);
reader.processPass1(relation);
SourceFeature feature = reader.processWayPass2(nodeCache, way);
assertEquals(List.of(), feature.relationInfo(OtherRelInfo.class));
assertEquals(List.of(new TestRelInfo("name")), feature.relationInfo(TestRelInfo.class));
}
private OpenStreetMapReader newOsmReader() {
return new OpenStreetMapReader(
osmSource,