11use crate :: hierarchy_builder:: ZonesTree ;
22use crate :: is_place;
3+ use crate :: zone_ext:: ZoneExt ;
4+ use anyhow:: { Context , Result } ;
35use cosmogony:: { Zone , ZoneIndex , ZoneType } ;
46use geo:: prelude:: BoundingRect ;
57use geo_types:: { Coordinate , MultiPolygon , Point , Rect } ;
68use geos:: { Geom , Geometry } ;
9+ use itertools:: Itertools ;
710use osmpbfreader:: { OsmId , OsmObj } ;
811use rayon:: iter:: { IntoParallelIterator , IntoParallelRefIterator , ParallelIterator } ;
912use std:: collections:: BTreeMap ;
1013
11- use crate :: zone_ext:: ZoneExt ;
12-
1314fn difference < ' a > ( g : & geos:: Geometry < ' a > , other : & Zone ) -> Option < geos:: Geometry < ' a > > {
1415 let zone_as_geos: Option < Geometry > = other. boundary . as_ref ( ) . and_then ( |b| {
1516 b. try_into ( )
@@ -30,12 +31,13 @@ fn difference<'a>(g: &geos::Geometry<'a>, other: &Zone) -> Option<geos::Geometry
3031 }
3132}
3233
33- pub fn compute_additional_cities (
34+ pub fn compute_additional_places (
3435 zones : & mut Vec < Zone > ,
3536 parsed_pbf : & BTreeMap < OsmId , OsmObj > ,
3637 zones_rtree : ZonesTree ,
3738) {
3839 let place_zones = read_places ( parsed_pbf) ;
40+
3941 info ! (
4042 "there are {} places, we'll try to make boundaries for them" ,
4143 place_zones. len( )
@@ -48,17 +50,20 @@ pub fn compute_additional_cities(
4850 get_parent ( place, zones, & zones_rtree) . map ( |parent| ( parent, place) )
4951 } )
5052 . filter ( |( parent, place) | {
51- parent
52- . zone_type
53- . as_ref ( )
54- . map ( |x| {
55- if * x == ZoneType :: Country {
53+ ( parent. zone_type )
54+ . map ( |parent_zone| {
55+ if parent_zone == ZoneType :: Country {
5656 info ! (
5757 "Ignoring place with id {} and country {} as parent" ,
5858 place. osm_id, parent. osm_id
5959 ) ;
6060 }
61- * x > ZoneType :: City && * x < ZoneType :: Country
61+
62+ // Ensuring zones are stricly increasing also ensures there will be no
63+ // duplicates, for example by adding an admin label which is inside its
64+ // boundary.
65+ parent_zone > place. zone_type . unwrap_or ( parent_zone)
66+ && parent_zone < ZoneType :: Country
6267 } )
6368 . unwrap_or ( false )
6469 } )
@@ -78,20 +83,19 @@ pub fn compute_additional_cities(
7883 candidate_parent_zones. len( )
7984 ) ;
8085
81- let new_cities: Vec < Vec < Zone > > = {
86+ let new_cities: Vec < Zone > = {
8287 candidate_parent_zones
8388 . into_par_iter ( )
8489 . filter ( |( _, places) | !places. is_empty ( ) )
8590 . map ( |( parent, places) | compute_voronoi ( parent, & places, zones, & zones_rtree) )
91+ . flatten ( )
8692 . collect ( )
8793 } ;
88- for cities in new_cities. into_iter ( ) {
89- publish_new_cities ( zones, cities) ;
90- }
94+
95+ publish_new_places ( zones, new_cities) ;
9196}
9297
9398fn get_parent < ' a > ( place : & Zone , zones : & ' a [ Zone ] , zones_rtree : & ZonesTree ) -> Option < & ' a Zone > {
94- use itertools:: Itertools ;
9599 zones_rtree
96100 . fetch_zone_bbox ( place)
97101 . into_iter ( )
@@ -119,96 +123,114 @@ fn read_places(parsed_pbf: &BTreeMap<OsmId, OsmObj>) -> Vec<Zone> {
119123 if !is_place ( obj) {
120124 return None ;
121125 }
122- if let OsmObj :: Node ( ref node) = obj {
123- let next_index = ZoneIndex { index } ;
124- if let Some ( mut zone) = Zone :: from_osm_node ( node, next_index) {
125- if zone. name . is_empty ( ) {
126- return None ;
127- }
128- zone. zone_type = Some ( ZoneType :: City ) ;
129- zone. center = Some ( Point :: < f64 > :: new ( node. lon ( ) , node. lat ( ) ) ) ;
130- zone. bbox = zone. center . as_ref ( ) . map ( |p| {
131- Rect :: new (
132- Coordinate {
133- x : p. 0 . x - std:: f64:: EPSILON ,
134- y : p. 0 . y - std:: f64:: EPSILON ,
135- } , // min
136- Coordinate {
137- x : p. 0 . x + std:: f64:: EPSILON ,
138- y : p. 0 . y + std:: f64:: EPSILON ,
139- } , // max
140- )
141- } ) ;
142- zone. is_generated = true ;
143- return Some ( zone) ;
144- }
126+
127+ let node = obj. node ( ) ?;
128+ let next_index = ZoneIndex { index } ;
129+ let mut zone = Zone :: from_osm_node ( node, next_index) ?;
130+
131+ if zone. name . is_empty ( ) {
132+ return None ;
145133 }
146- None
134+
135+ zone. center = Some ( Point :: < f64 > :: new ( node. lon ( ) , node. lat ( ) ) ) ;
136+ zone. bbox = zone. center . as_ref ( ) . map ( |p| {
137+ Rect :: new (
138+ Coordinate {
139+ x : p. 0 . x - std:: f64:: EPSILON ,
140+ y : p. 0 . y - std:: f64:: EPSILON ,
141+ } , // min
142+ Coordinate {
143+ x : p. 0 . x + std:: f64:: EPSILON ,
144+ y : p. 0 . y + std:: f64:: EPSILON ,
145+ } , // max
146+ )
147+ } ) ;
148+
149+ zone. is_generated = true ;
150+ Some ( zone)
147151 } )
148152 . collect ( )
149153}
150154
151- fn convert_to_geo ( geom : Geometry < ' _ > ) -> Option < MultiPolygon < f64 > > {
152- match match geom. try_into ( ) {
153- Ok ( c) => c,
154- Err ( e) => {
155- warn ! ( "convert_to_geo: conversion to geo failed: {}" , e) ;
156- return None ;
155+ fn convert_to_geo ( geom : Geometry < ' _ > ) -> Result < Option < MultiPolygon < f64 > > > {
156+ let is_empty_poly = |poly : & geo:: Polygon | poly. exterior ( ) . lines ( ) . next ( ) . is_none ( ) ;
157+
158+ let polys = match geom. try_into ( ) . context ( "failed to convert to geo" ) ? {
159+ geo:: Geometry :: Polygon ( x) => vec ! [ x] ,
160+ geo:: Geometry :: GeometryCollection ( geoms) => {
161+ // Convert each geometry into a multi-polygon
162+ let multi_polys: Vec < _ > = geoms
163+ . into_iter ( )
164+ . map ( MultiPolygon :: try_from)
165+ . try_collect ( ) ?;
166+
167+ // Flatten all multi polygons into a single one
168+ multi_polys
169+ . into_iter ( )
170+ . flat_map ( |m| m. into_iter ( ) )
171+ . collect ( )
157172 }
158- } {
159- geo:: Geometry :: Polygon ( x) => Some ( MultiPolygon ( vec ! [ x] ) ) ,
160173 y => {
161- if let Ok ( x) = y. try_into ( ) {
162- Some ( x)
163- } else {
164- None
165- }
174+ return Ok ( Some (
175+ y. try_into ( ) . context ( "failed to convert to multi-polygon" ) ?,
176+ ) )
166177 }
167- }
178+ } ;
179+
180+ let polys: Vec < _ > = polys
181+ . into_iter ( )
182+ . filter ( |poly| !is_empty_poly ( poly) )
183+ . collect ( ) ;
184+
185+ Ok ( {
186+ if polys. is_empty ( ) {
187+ None
188+ } else {
189+ Some ( MultiPolygon ( polys) )
190+ }
191+ } )
168192}
169193
170- fn subtract_existing_zones ( zone : & mut Zone , to_subtract : & [ & Zone ] ) -> Result < ( ) , String > {
194+ fn subtract_existing_zones ( zone : & mut Zone , to_subtract : & [ & Zone ] ) -> Result < ( ) > {
171195 if to_subtract. is_empty ( ) {
172196 return Ok ( ( ) ) ;
173197 }
198+
174199 if let Some ( ref boundary) = zone. boundary {
175200 let mut updates = 0 ;
176- let mut g_boundary = match geos:: Geometry :: try_from ( boundary) {
177- Ok ( b) => b,
178- Err ( e) => {
179- warn ! (
180- "subtract_existing_town: failed to convert to geos for zone {}: {}" ,
181- zone. osm_id, e
182- ) ;
183- return Err ( e. to_string ( ) ) ;
184- }
185- } ;
201+
202+ let mut g_boundary = geos:: Geometry :: try_from ( boundary) . map_err ( |err| {
203+ warn ! (
204+ "subtract_existing_town: failed to convert to geos for zone {}: {}" ,
205+ zone. osm_id, err
206+ ) ;
207+ err
208+ } ) ?;
209+
186210 for z in to_subtract {
187211 if let Some ( b) = difference ( & g_boundary, z) {
188212 updates += 1 ;
189213 g_boundary = b;
190214 }
191215 }
216+
192217 if updates > 0 {
193- match convert_to_geo ( g_boundary) {
194- Some ( g) => {
195- zone. bbox = g. bounding_rect ( ) ;
196- zone. boundary = Some ( g) ;
197- }
198- None => {
199- warn ! (
200- "subtract_existing_town: failed to convert back to geo for {}..." ,
201- zone. osm_id
202- ) ;
203- return Err ( "Failed to convert to Geo" . to_owned ( ) ) ;
204- }
218+ if let Some ( g) = convert_to_geo ( g_boundary) . map_err ( |err| {
219+ warn ! (
220+ "subtract_existing_town: failed to convert back to geo for {}..." ,
221+ zone. osm_id
222+ ) ;
223+ err
224+ } ) ? {
225+ zone. bbox = g. bounding_rect ( ) ;
226+ zone. boundary = Some ( g) ;
205227 }
206228 }
207229 }
208230 Ok ( ( ) )
209231}
210232
211- fn get_zones_to_subtract < ' a > (
233+ fn get_places_to_subtract < ' a > (
212234 zone : & Zone ,
213235 parent_id : & ZoneIndex ,
214236 zones : & ' a [ Zone ] ,
@@ -220,9 +242,7 @@ fn get_zones_to_subtract<'a>(
220242 . map ( |z_idx| & zones[ z_idx. index ] )
221243 . filter ( |z| {
222244 z. admin_type ( )
223- . map ( |zt| {
224- zt == ZoneType :: City || ( zt > ZoneType :: City && z. parent == Some ( * parent_id) )
225- } )
245+ . map ( |zt| Some ( zt) == zone. zone_type || z. parent == Some ( * parent_id) )
226246 . unwrap_or ( false )
227247 } )
228248 . filter ( |z| zone. intersects ( z) )
@@ -249,7 +269,7 @@ fn compute_voronoi(
249269 place. boundary = parent. boundary . clone ( ) ;
250270 place. bbox = parent. bbox ;
251271 place. parent = Some ( parent. id ) ;
252- let zones_to_subtract = get_zones_to_subtract ( parent, & parent. id , zones, zones_rtree) ;
272+ let zones_to_subtract = get_places_to_subtract ( parent, & parent. id , zones, zones_rtree) ;
253273 // If an error occurs, we can't just use the parent area so instead, we return nothing.
254274 if subtract_existing_zones ( & mut place, & zones_to_subtract) . is_ok ( ) {
255275 return vec ! [ place] ;
@@ -361,13 +381,17 @@ fn compute_voronoi(
361381 match geos_parent. intersection ( & voronoi) {
362382 Ok ( s) => {
363383 place. parent = Some ( parent. id ) ;
364- place. boundary = convert_to_geo ( s) ;
384+
385+ place. boundary = convert_to_geo ( s)
386+ . map_err ( |err| warn ! ( "failed to convert to geos: {err:?}" ) )
387+ . ok ( )
388+ . flatten ( ) ;
365389
366390 if let Some ( ref boundary) = place. boundary {
367391 place. bbox = boundary. bounding_rect ( ) ;
368392 }
369393 let zones_to_subtract =
370- get_zones_to_subtract ( & place, & parent. id , zones, zones_rtree) ;
394+ get_places_to_subtract ( & place, & parent. id , zones, zones_rtree) ;
371395 subtract_existing_zones ( & mut place, & zones_to_subtract) . ok ( ) ?;
372396 Some ( place)
373397 }
@@ -387,7 +411,7 @@ fn compute_voronoi(
387411 . collect ( )
388412}
389413
390- fn publish_new_cities ( zones : & mut Vec < Zone > , new_cities : Vec < Zone > ) {
414+ fn publish_new_places ( zones : & mut Vec < Zone > , new_cities : Vec < Zone > ) {
391415 for mut city in new_cities {
392416 city. id = ZoneIndex { index : zones. len ( ) } ;
393417 zones. push ( city) ;
0 commit comments