11package il .org .osm .israelhiking ;
22
3+ import static com .onthegomap .planetiler .reader .osm .OsmElement .Type .RELATION ;
34import static com .onthegomap .planetiler .reader .osm .OsmElement .Type .WAY ;
45
56import java .util .Arrays ;
@@ -108,24 +109,43 @@ public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation)
108109 return null ;
109110 }
110111 // then store a RouteRelationInfo instance with tags we'll need later
111- var members_ids = relation .members ()
112+ var waysMemberIds = relation .members ()
112113 .stream ()
113114 .filter (member -> member .type () == WAY )
114115 .mapToLong (OsmElement .Relation .Member ::ref )
115116 .boxed ()
116117 .collect (Collectors .toList ());
117- if (members_ids .isEmpty ()) {
118+
119+ var relationMemberIds = relation .members ()
120+ .stream ()
121+ .filter (member -> member .type () == RELATION )
122+ .mapToLong (OsmElement .Relation .Member ::ref )
123+ .boxed ()
124+ .collect (Collectors .toList ());
125+
126+ if (waysMemberIds .isEmpty () && relationMemberIds .isEmpty ()) {
118127 return null ;
119128 }
120129 var info = new RelationInfo (relation .id ());
121130
122131 convertTagsToDocument (pointDocument , relation );
132+
133+ if (pointDocument .name .isEmpty ()) {
134+ return null ;
135+ }
123136 pointDocument .poiSource = "OSM" ;
124137 info .pointDocument = pointDocument ;
125- info .firstMemberId = members_ids .getFirst ();
126- info .secondMemberId = members_ids .size () > 1 ? members_ids .get (1 ) : -1 ;
127- info .memberIds = Collections .synchronizedList (members_ids );
128-
138+ if (waysMemberIds .size () > 0 ) {
139+ info .firstMemberId = waysMemberIds .getFirst ();
140+ info .secondMemberId = waysMemberIds .size () > 1 ? waysMemberIds .get (1 ) : -1 ;
141+ } else if (relationMemberIds .size () > 0 ) {
142+ info .firstMemberId = relationMemberIds .getFirst ();
143+ info .secondMemberId = relationMemberIds .size () > 1 ? relationMemberIds .get (1 ) : -1 ;
144+ }
145+
146+ info .waysMemberIds = Collections .synchronizedList (waysMemberIds );
147+ info .RelationMemberIds = Collections .synchronizedList (relationMemberIds );
148+ info .isSuperRelation = info .RelationMemberIds .size () > 0 ;
129149 return List .of (info );
130150 }
131151
@@ -216,28 +236,28 @@ private void processExternalFeautre(SourceFeature feature, FeatureCollector feat
216236 }
217237
218238 private void processOsmRelationFeature (SourceFeature feature , FeatureCollector features ) throws GeometryException {
219- // get all the RouteRelationInfo instances we returned from preprocessOsmRelation that
220- // this way belongs to
221- for (var routeInfo : feature .relationInfo (RelationInfo .class )) {
222- // (routeInfo.role() also has the "role" of this relation member if needed)
239+ // get all the RouteRelationInfo instances we returned from preprocessOsmRelation that this way belongs to, including super relations.
240+ for (var routeInfo : feature .relationInfo (RelationInfo .class , true )) {
223241 RelationInfo relation = routeInfo .relation ();
224- if (relation .pointDocument .name .isEmpty ()) {
225- continue ;
226- }
227- relation .memberIds .remove (feature .id ());
242+ relation .waysMemberIds .remove (feature .id ());
228243
229244 if (relation .firstMemberId == feature .id ()) {
230245 relation .firstMemberFeature = feature ;
231246 }
232247 if (relation .secondMemberId == feature .id ()) {
233248 relation .secondMemberFeature = feature ;
234249 }
250+ }
235251
236- if (!relation .memberIds .isEmpty ()) {
252+ handleSuperRelationMembersUpdate (feature );
253+
254+ for (var routeInfo : feature .relationInfo (RelationInfo .class , true )) {
255+ RelationInfo relation = routeInfo .relation ();
256+ if (!relation .waysMemberIds .isEmpty () || !relation .RelationMemberIds .isEmpty ()) {
237257 continue ;
238258 }
239259 // All relation members were reached. Add a POI element for line relation
240- var point = getFirstPointOfLineRelation (relation );
260+ var point = getFirstPointOfLineRelation (relation . firstMemberFeature , relation . secondMemberFeature );
241261 var lngLatPoint = GeoUtils .worldToLatLonCoords (point ).getCoordinate ();
242262 relation .pointDocument .location = new double []{lngLatPoint .getX (), lngLatPoint .getY ()};
243263
@@ -558,16 +578,15 @@ private void insertBboxToElasticsearch(SourceFeature feature, String[] supported
558578 * @return the first point of the trail relation
559579 * @throws GeometryException
560580 */
561- private Point getFirstPointOfLineRelation (RelationInfo relation ) throws GeometryException {
562- if (relation . secondMemberFeature == null ) {
563- return GeoUtils .point (relation . firstMemberFeature .worldGeometry ().getCoordinate ());
581+ private Point getFirstPointOfLineRelation (SourceFeature firstMemberFeature , SourceFeature secondMemberFeature ) throws GeometryException {
582+ if (secondMemberFeature == null ) {
583+ return GeoUtils .point (firstMemberFeature .worldGeometry ().getCoordinate ());
564584 }
565585
566- var firstMemberGeometry = (LineString ) relation . firstMemberFeature .line ();
586+ var firstMemberGeometry = (LineString ) firstMemberFeature .line ();
567587 var firstMemberStartCoordinate = firstMemberGeometry .getCoordinate ();
568588 var firstMemberEndCoordinate = firstMemberGeometry .getCoordinateN (firstMemberGeometry .getNumPoints () - 1 );
569-
570- var secondMemberGeometry = (LineString ) relation .secondMemberFeature .line ();
589+ var secondMemberGeometry = (LineString ) secondMemberFeature .line ();
571590 var secondMemberStartCoordinate = secondMemberGeometry .getCoordinate ();
572591 var secondMemberEndCoordinate = secondMemberGeometry .getCoordinateN (secondMemberGeometry .getNumPoints () - 1 );
573592
@@ -580,6 +599,40 @@ private Point getFirstPointOfLineRelation(RelationInfo relation) throws Geometry
580599 return GeoUtils .point (firstMemberStartCoordinate );
581600 }
582601
602+ /**
603+ * This method removes relation members that are part of super relations and have completed the ways processing.
604+ * This is done by checking for each new way that is being processed if it completes a relation,
605+ * and remove that relation from the list of parent relations, this way at some point all the ways and relations are empty
606+ * and it means we can continue processing them to add them to the database and tiles.
607+ * It also keeps track of the first and second member features in case they are needed to determine the first point of the relation.
608+ * @param feature
609+ */
610+ private void handleSuperRelationMembersUpdate (SourceFeature feature ) {
611+ var removedElement = false ;
612+ do {
613+ removedElement = false ;
614+ for (var routeInfo : feature .relationInfo (RelationInfo .class , true )) {
615+ RelationInfo relation = routeInfo .relation ();
616+ if (!relation .waysMemberIds .isEmpty () || !relation .RelationMemberIds .isEmpty ()) {
617+ continue ;
618+ }
619+ for (var superRouteInfo : feature .relationInfo (RelationInfo .class , true )) {
620+ RelationInfo superRelation = superRouteInfo .relation ();
621+ if (!superRelation .isSuperRelation ) {
622+ continue ;
623+ }
624+ if (superRelation .RelationMemberIds .remove (relation .id ())) {
625+ removedElement = true ;
626+ if (superRelation .firstMemberId == relation .id ()) {
627+ superRelation .firstMemberFeature = relation .firstMemberFeature ;
628+ superRelation .secondMemberFeature = relation .secondMemberFeature ;
629+ }
630+ }
631+ }
632+ }
633+ } while (removedElement );
634+ }
635+
583636 private boolean isInterestingPoint (PointDocument pointDocument ) {
584637 return !pointDocument .description .isEmpty () ||
585638 pointDocument .image != null ;
0 commit comments