Skip to content

Commit 2b5e0d1

Browse files
committed
Fixes #33 - Wrong place for POI of Q relation
1 parent dc44b04 commit 2b5e0d1

File tree

4 files changed

+104
-115
lines changed

4 files changed

+104
-115
lines changed

src/main/java/il/org/osm/israelhiking/MergedLinesHelper.java

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/main/java/il/org/osm/israelhiking/MinWayIdFinder.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,28 @@
33
import java.util.List;
44
import java.util.concurrent.CopyOnWriteArrayList;
55

6+
import org.locationtech.jts.operation.linemerge.LineMerger;
7+
8+
import com.onthegomap.planetiler.reader.SourceFeature;
9+
610
class MinWayIdFinder {
711
List<Long> ids = new CopyOnWriteArrayList<Long>();
8-
long minId;
9-
public MinWayIdFinder() {
10-
this.minId = Integer.MAX_VALUE;
11-
}
12+
long minId;
13+
LineMerger lineMerger;
14+
/**
15+
* The feature that reporesens the merged line. In our cause, the feature with the minimal id.
16+
*/
17+
SourceFeature representingFeature;
18+
19+
public MinWayIdFinder() {
20+
this.minId = Integer.MAX_VALUE;
21+
this.lineMerger = new LineMerger();
22+
}
1223

13-
public void addWayId(long id) {
14-
ids.add(id);
15-
if (id < minId) {
16-
minId = id;
17-
}
24+
public void addWayId(long id) {
25+
ids.add(id);
26+
if (id < minId) {
27+
minId = id;
1828
}
29+
}
1930
}

src/main/java/il/org/osm/israelhiking/PlanetSearchProfile.java

Lines changed: 80 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
import static com.onthegomap.planetiler.reader.osm.OsmElement.Type.WAY;
44

55
import java.util.Arrays;
6+
import java.util.Collections;
67
import java.util.HashMap;
78
import java.util.List;
89
import java.util.Map;
910
import java.util.Objects;
1011
import java.util.concurrent.ConcurrentHashMap;
11-
import java.util.concurrent.ConcurrentMap;
1212
import java.util.stream.Collectors;
1313

14-
import org.locationtech.jts.geom.Envelope;
1514
import org.locationtech.jts.geom.Geometry;
1615
import org.locationtech.jts.geom.LineString;
1716
import org.locationtech.jts.geom.Point;
@@ -29,11 +28,7 @@
2928

3029
import co.elastic.clients.elasticsearch.ElasticsearchClient;
3130

32-
import org.slf4j.Logger;
33-
import org.slf4j.LoggerFactory;
34-
3531
public class PlanetSearchProfile implements Profile {
36-
private static final Logger LOGGER = LoggerFactory.getLogger(PlanetSearchProfile.class);
3732
private PlanetilerConfig config;
3833
private ElasticsearchClient esClient;
3934
private final String pointsIndexName;
@@ -42,10 +37,8 @@ public class PlanetSearchProfile implements Profile {
4237

4338
public static final String POINTS_LAYER_NAME = "global_points";
4439

45-
private static final Map<String, MinWayIdFinder> Singles = new HashMap<>();
46-
private static final Map<String, MinWayIdFinder> Waterways = new HashMap<>();
47-
private static final ConcurrentMap<Long, MergedLinesHelper> RelationLineMergers = new ConcurrentHashMap<>();
48-
private static final ConcurrentMap<Long, MergedLinesHelper> WaysLineMergers = new ConcurrentHashMap<>();
40+
private static final Map<String, MinWayIdFinder> Singles = new ConcurrentHashMap<>();
41+
private static final Map<String, MinWayIdFinder> Waterways = new ConcurrentHashMap<>();
4942

5043
public PlanetSearchProfile(PlanetilerConfig config, ElasticsearchClient esClient, String pointsIndexName, String bboxIndexName, String[] supportedLnaguages) {
5144
this.config = config;
@@ -119,7 +112,8 @@ public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation)
119112
pointDocument.wikimedia_commons = relation.getString("wikimedia_commons");
120113
info.pointDocument = pointDocument;
121114
info.firstMemberId = members_ids.getFirst();
122-
info.memberIds = members_ids;
115+
info.secondMemberId = members_ids.size() > 1 ? members_ids.get(1) : -1;
116+
info.memberIds = Collections.synchronizedList(members_ids);
123117

124118
return List.of(info);
125119
}
@@ -128,23 +122,27 @@ public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation)
128122
public void preprocessOsmWay(OsmElement.Way way) {
129123
if (way.hasTag("mtb:name")) {
130124
String mtbName = way.getString("mtb:name");
131-
if (!Singles.containsKey(mtbName)) {
132-
var finder = new MinWayIdFinder();
133-
finder.addWayId(way.id());
134-
Singles.put(mtbName, finder);
135-
} else {
136-
Singles.get(mtbName).addWayId(way.id());
125+
synchronized(mtbName.intern()) {
126+
if (!Singles.containsKey(mtbName)) {
127+
var finder = new MinWayIdFinder();
128+
finder.addWayId(way.id());
129+
Singles.put(mtbName, finder);
130+
} else {
131+
Singles.get(mtbName).addWayId(way.id());
132+
}
133+
return;
137134
}
138-
return;
139135
}
140-
if (way.hasTag("waterway")) {
136+
if (way.hasTag("waterway") && way.hasTag("name")) {
141137
String waterwayName = way.getString("name");
142-
if (!Waterways.containsKey(waterwayName)) {
143-
var finder = new MinWayIdFinder();
144-
finder.addWayId(way.id());
145-
Waterways.put(waterwayName, finder);
146-
} else {
147-
Waterways.get(waterwayName).addWayId(way.id());
138+
synchronized(waterwayName.intern()) {
139+
if (!Waterways.containsKey(waterwayName)) {
140+
var finder = new MinWayIdFinder();
141+
finder.addWayId(way.id());
142+
Waterways.put(waterwayName, finder);
143+
} else {
144+
Waterways.get(waterwayName).addWayId(way.id());
145+
}
148146
}
149147
}
150148

@@ -208,34 +206,29 @@ private void processOsmRelationFeature(SourceFeature feature, FeatureCollector f
208206
if (relation.pointDocument.name.isEmpty()) {
209207
continue;
210208
}
211-
// Collect all relation way members
212-
if (!RelationLineMergers.containsKey(relation.id())) {
213-
RelationLineMergers.put(relation.id(), new MergedLinesHelper());
214-
}
215-
var mergedLines = RelationLineMergers.get(relation.id());
216-
synchronized(mergedLines) {
217-
mergedLines.lineMerger.add(feature.line());
218-
relation.memberIds.remove(feature.id());
209+
relation.memberIds.remove(feature.id());
219210

220-
if (relation.firstMemberId == feature.id()) {
221-
mergedLines.feature = feature;
222-
}
211+
if (relation.firstMemberId == feature.id()) {
212+
relation.firstMemberFeature = feature;
213+
}
214+
if (relation.secondMemberId == feature.id()) {
215+
relation.secondMemberFeature = feature;
216+
}
223217

224-
if (!relation.memberIds.isEmpty()) {
225-
continue;
226-
}
227-
// All relation members were reached. Add a POI element for line relation
228-
var point = getFirstPointOfLineRelation(mergedLines);
229-
var lngLatPoint = GeoUtils.worldToLatLonCoords(point).getCoordinate();
230-
relation.pointDocument.location = new double[]{lngLatPoint.getX(), lngLatPoint.getY()};
218+
if (!relation.memberIds.isEmpty()) {
219+
continue;
220+
}
221+
// All relation members were reached. Add a POI element for line relation
222+
var point = getFirstPointOfLineRelation(relation);
223+
var lngLatPoint = GeoUtils.worldToLatLonCoords(point).getCoordinate();
224+
relation.pointDocument.location = new double[]{lngLatPoint.getX(), lngLatPoint.getY()};
231225

232-
insertPointToElasticsearch(relation.pointDocument, "OSM_relation_" + relation.id());
226+
insertPointToElasticsearch(relation.pointDocument, "OSM_relation_" + relation.id());
233227

234-
var tileFeature = features.geometry(POINTS_LAYER_NAME, point)
235-
.setZoomRange(10, 14)
236-
.setId(relation.vectorTileFeatureId(config.featureSourceIdMultiplier()));
237-
setFeaturePropertiesFromPointDocument(tileFeature, relation.pointDocument);
238-
}
228+
var tileFeature = features.geometry(POINTS_LAYER_NAME, point)
229+
.setZoomRange(10, 14)
230+
.setId(relation.vectorTileFeatureId(config.featureSourceIdMultiplier()));
231+
setFeaturePropertiesFromPointDocument(tileFeature, relation.pointDocument);
239232
}
240233
}
241234

@@ -247,23 +240,19 @@ private boolean processMtbNameFeature(SourceFeature feature, FeatureCollector fe
247240
if (!Singles.containsKey(mtbName)) {
248241
return false;
249242
}
250-
var minId = Singles.get(mtbName).minId;
251-
if (!WaysLineMergers.containsKey(minId)) {
252-
WaysLineMergers.put(minId, new MergedLinesHelper());
253-
}
254-
var mergedLines = WaysLineMergers.get(minId);
255-
synchronized(mergedLines) {
256-
mergedLines.lineMerger.add(feature.worldGeometry());
257-
Singles.get(mtbName).ids.remove(feature.id());
243+
var single = Singles.get(mtbName);
244+
synchronized(single) {
245+
single.lineMerger.add(feature.worldGeometry());
246+
single.ids.remove(feature.id());
258247

259-
if (minId == feature.id()) {
260-
mergedLines.feature = feature;
248+
if (single.minId == feature.id()) {
249+
single.representingFeature = feature;
261250
}
262-
if (!Singles.get(mtbName).ids.isEmpty()) {
251+
if (!single.ids.isEmpty()) {
263252
return true;
264253
}
265-
var minIdFeature = mergedLines.feature;
266-
var point = GeoUtils.point(((Geometry)mergedLines.lineMerger.getMergedLineStrings().iterator().next()).getCoordinate());
254+
var minIdFeature = single.representingFeature;
255+
var point = GeoUtils.point(((Geometry)single.lineMerger.getMergedLineStrings().iterator().next()).getCoordinate());
267256

268257
var pointDocument = new PointDocument();
269258
for (String language : supportedLanguages) {
@@ -280,7 +269,7 @@ private boolean processMtbNameFeature(SourceFeature feature, FeatureCollector fe
280269
var lngLatPoint = GeoUtils.worldToLatLonCoords(point).getCoordinate();
281270
pointDocument.location = new double[]{lngLatPoint.getX(), lngLatPoint.getY()};
282271

283-
insertPointToElasticsearch(pointDocument, "OSM_way_" + minId);
272+
insertPointToElasticsearch(pointDocument, "OSM_way_" + single.minId);
284273
// This was the last way with the same mtb:name, so we can merge the lines and add the feature
285274
// Add a POI element for a SingleTrack
286275
var tileFeature = features.geometry(POINTS_LAYER_NAME, point)
@@ -296,6 +285,9 @@ private boolean processWaterwayFeature(SourceFeature feature, FeatureCollector f
296285
if (!feature.hasTag("waterway")) {
297286
return false;
298287
}
288+
if (!feature.hasTag("name")) {
289+
return false;
290+
}
299291
String name = feature.getString("name");
300292
if (!Waterways.containsKey(name)) {
301293
return false;
@@ -308,24 +300,20 @@ private boolean processWaterwayFeature(SourceFeature feature, FeatureCollector f
308300
}
309301
}
310302

311-
var minId = Waterways.get(name).minId;
312-
if (!WaysLineMergers.containsKey(minId)) {
313-
WaysLineMergers.put(minId, new MergedLinesHelper());
314-
}
315-
var mergedLines = WaysLineMergers.get(minId);
316-
synchronized(mergedLines) {
303+
var waterway = Waterways.get(name);
304+
synchronized(waterway) {
317305

318-
mergedLines.lineMerger.add(feature.worldGeometry());
319-
Waterways.get(name).ids.remove(feature.id());
306+
waterway.lineMerger.add(feature.worldGeometry());
307+
waterway.ids.remove(feature.id());
320308

321-
if (minId == feature.id()) {
322-
mergedLines.feature = feature;
309+
if (waterway.minId == feature.id()) {
310+
waterway.representingFeature = feature;
323311
}
324-
if (!Waterways.get(name).ids.isEmpty()) {
312+
if (!waterway.ids.isEmpty()) {
325313
return true;
326314
}
327-
var minIdFeature = mergedLines.feature;
328-
var point = GeoUtils.point(((Geometry)mergedLines.lineMerger.getMergedLineStrings().iterator().next()).getCoordinate());
315+
var minIdFeature = waterway.representingFeature;
316+
var point = GeoUtils.point(((Geometry)waterway.lineMerger.getMergedLineStrings().iterator().next()).getCoordinate());
329317

330318
var pointDocument = new PointDocument();
331319
for (String language : supportedLanguages) {
@@ -342,7 +330,7 @@ private boolean processWaterwayFeature(SourceFeature feature, FeatureCollector f
342330
var lngLatPoint = GeoUtils.worldToLatLonCoords(point).getCoordinate();
343331
pointDocument.location = new double[]{lngLatPoint.getX(), lngLatPoint.getY()};
344332

345-
insertPointToElasticsearch(pointDocument, "OSM_way_" + minId);
333+
insertPointToElasticsearch(pointDocument, "OSM_way_" + waterway.minId);
346334
if (!isInterestingPoint(pointDocument)) {
347335
// Skip adding features without any description or image to tiles
348336
return true;
@@ -530,7 +518,6 @@ private void insertBboxToElasticsearch(SourceFeature feature, String[] supported
530518
);
531519
} catch (Exception e) {
532520
// swallow
533-
LOGGER.error("Unable to insert: " + e.getMessage());
534521
}
535522
}
536523

@@ -540,26 +527,26 @@ private void insertBboxToElasticsearch(SourceFeature feature, String[] supported
540527
* @return the first point of the trail relation
541528
* @throws GeometryException
542529
*/
543-
private Point getFirstPointOfLineRelation(MergedLinesHelper mergedLines) throws GeometryException {
544-
var firstMergedLineString = (LineString) mergedLines.lineMerger.getMergedLineStrings().iterator().next();
545-
var firstMergedLineCoordinate = firstMergedLineString.getCoordinate();
546-
var lastMergedLineCoordinate = firstMergedLineString.getCoordinateN(firstMergedLineString.getNumPoints() - 1);
547-
548-
var firstMemberGeometry = (LineString) mergedLines.feature.line();
530+
private Point getFirstPointOfLineRelation(RelationInfo relation) throws GeometryException {
531+
if (relation.secondMemberFeature == null) {
532+
return GeoUtils.point(relation.firstMemberFeature.worldGeometry().getCoordinate());
533+
}
534+
535+
var firstMemberGeometry = (LineString) relation.firstMemberFeature.line();
549536
var firstMemberStartCoordinate = firstMemberGeometry.getCoordinate();
550537
var firstMemberEndCoordinate = firstMemberGeometry.getCoordinateN(firstMemberGeometry.getNumPoints() - 1);
538+
539+
var secondMemberGeometry = (LineString) relation.secondMemberFeature.line();
540+
var secondMemberStartCoordinate = secondMemberGeometry.getCoordinate();
541+
var secondMemberEndCoordinate = secondMemberGeometry.getCoordinateN(secondMemberGeometry.getNumPoints() - 1);
551542

552-
if (firstMergedLineCoordinate.equals(firstMemberStartCoordinate)) {
553-
// The direction of the related's first memeber and the merged lines is the same
554-
return GeoUtils.point(firstMergedLineCoordinate);
543+
if (firstMemberStartCoordinate.equals2D(secondMemberStartCoordinate) || firstMemberStartCoordinate.equals2D(secondMemberEndCoordinate)) {
544+
return GeoUtils.point(firstMemberEndCoordinate);
555545
}
556-
557-
if (lastMergedLineCoordinate.equals(firstMemberStartCoordinate) || lastMergedLineCoordinate.equals(firstMemberEndCoordinate)) {
558-
// The direction of the related's first memeber and the merged lines is the opposite
559-
return GeoUtils.point(lastMergedLineCoordinate);
546+
if (firstMemberEndCoordinate.equals2D(secondMemberStartCoordinate) || firstMemberEndCoordinate.equals2D(secondMemberEndCoordinate)) {
547+
return GeoUtils.point(firstMemberStartCoordinate);
560548
}
561-
// Otherwise, return the first point of the merged line
562-
return GeoUtils.point(firstMergedLineCoordinate);
549+
return GeoUtils.point(firstMemberStartCoordinate);
563550
}
564551

565552
private boolean isInterestingPoint(PointDocument pointDocument) {

src/main/java/il/org/osm/israelhiking/RelationInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.List;
44

5+
import com.onthegomap.planetiler.reader.SourceFeature;
56
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
67

78
// Minimal container for data we extract from OSM route relations. This is held in RAM so keep it small.
@@ -22,5 +23,8 @@ public long id() {
2223
}
2324

2425
Long firstMemberId;
26+
Long secondMemberId;
2527
List<Long> memberIds;
28+
SourceFeature firstMemberFeature;
29+
SourceFeature secondMemberFeature;
2630
}

0 commit comments

Comments
 (0)