33import static com .onthegomap .planetiler .reader .osm .OsmElement .Type .WAY ;
44
55import java .util .Arrays ;
6+ import java .util .Collections ;
67import java .util .HashMap ;
78import java .util .List ;
89import java .util .Map ;
910import java .util .Objects ;
1011import java .util .concurrent .ConcurrentHashMap ;
11- import java .util .concurrent .ConcurrentMap ;
1212import java .util .stream .Collectors ;
1313
14- import org .locationtech .jts .geom .Envelope ;
1514import org .locationtech .jts .geom .Geometry ;
1615import org .locationtech .jts .geom .LineString ;
1716import org .locationtech .jts .geom .Point ;
2928
3029import co .elastic .clients .elasticsearch .ElasticsearchClient ;
3130
32- import org .slf4j .Logger ;
33- import org .slf4j .LoggerFactory ;
34-
3531public 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 ) {
0 commit comments