def build_curv_tol_weight_map(tile, weight_array): if tile.apt_curv_tol != tile.curvature_tol and tile.apt_curv_tol > 0: UI.vprint( 1, "-> Modifying curv_tol weight map according to runway locations.") try: f = open(FNAMES.apt_file(tile), 'rb') dico_airports = pickle.load(f) f.close() except: UI.vprint( 1, " WARNING: File", FNAMES.apt_file(tile), "is missing (erased after Step 1?), cannot check airport info for upgraded zoomlevel." ) dico_airports = {} for airport in dico_airports: (xmin, ymin, xmax, ymax) = dico_airports[airport]['boundary'].bounds x_shift = 1000 * tile.apt_curv_ext * GEO.m_to_lon(tile.lat) y_shift = 1000 * tile.apt_curv_ext * GEO.m_to_lat colmin = max(round((xmin - x_shift) * 1000), 0) colmax = min(round((xmax + x_shift) * 1000), 1000) rowmax = min(round(((1 - ymin) + y_shift) * 1000), 1000) rowmin = max(round(((1 - ymax) - y_shift) * 1000), 0) weight_array[rowmin:rowmax + 1, colmin:colmax + 1] = tile.curvature_tol / tile.apt_curv_tol if tile.coast_curv_tol != tile.curvature_tol: UI.vprint( 1, "-> Modifying curv_tol weight map according to coastline location." ) sea_layer = OSM.OSM_layer() queries = ['way["natural"="coastline"]'] tags_of_interest = [] if not OSM.OSM_queries_to_OSM_layer(queries, sea_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='coastline'): return 0 for nodeid in sea_layer.dicosmn: (lonp, latp) = [float(x) for x in sea_layer.dicosmn[nodeid]] if lonp < tile.lon or lonp > tile.lon + 1 or latp < tile.lat or latp > tile.lat + 1: continue x_shift = 1000 * tile.coast_curv_ext * GEO.m_to_lon(tile.lat) y_shift = tile.coast_curv_ext / (111.12) colmin = max(round((lonp - tile.lon - x_shift) * 1000), 0) colmax = min(round((lonp - tile.lon + x_shift) * 1000), 1000) rowmax = min(round((tile.lat + 1 - latp + y_shift) * 1000), 1000) rowmin = max(round((tile.lat + 1 - latp - y_shift) * 1000), 0) weight_array[rowmin:rowmax + 1, colmin:colmax + 1] = numpy.maximum( weight_array[rowmin:rowmax + 1, colmin:colmax + 1], tile.curvature_tol / tile.coast_curv_tol) del (sea_layer) # It could be of interest to write the weight file as a png for user editing #from PIL import Image #Image.fromarray((weight_array!=1).astype(numpy.uint8)*255).save('weight.png') return
def include_sea(vector_map, tile): UI.vprint(0, "-> Dealing with coastline") sea_layer = OSM.OSM_layer() custom_coastline = FNAMES.custom_coastline(tile.lat, tile.lon) if os.path.isfile(custom_coastline): sea_layer.update_dicosm(custom_coastline, target_tags=None) else: queries = ['way["natural"="coastline"]'] tags_of_interest = [] if not OSM.OSM_queries_to_OSM_layer(queries, sea_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='coastline'): return 0 coastline = OSM.OSM_to_MultiLineString(sea_layer, tile.lat, tile.lon, None) if not coastline.is_empty: # 1) encoding the coastline UI.vprint(1, " * Encoding coastline.") vector_map.encode_MultiLineString(VECT.cut_to_tile( coastline, strictly_inside=True), tile.dem.alt_vec, 'SEA', check=True, refine=False) UI.vprint(3, "...done.") # 2) finding seeds (transform multilinestring coastline to polygon coastline # linemerge being expensive we first set aside what is already known to be closed loops UI.vprint(1, " * Reconstructing its topology.") loops = geometry.MultiLineString( [line for line in coastline.geoms if line.is_ring]) remainder = VECT.ensure_MultiLineString( VECT.cut_to_tile(geometry.MultiLineString( [line for line in coastline.geoms if not line.is_ring]), strictly_inside=True)) UI.vprint(3, "Linemerge...") if not remainder.is_empty: remainder = VECT.ensure_MultiLineString(ops.linemerge(remainder)) UI.vprint(3, "...done.") coastline = geometry.MultiLineString([line for line in remainder] + [line for line in loops]) sea_area = VECT.ensure_MultiPolygon( VECT.coastline_to_MultiPolygon(coastline, tile.lat, tile.lon)) if sea_area.geoms: UI.vprint(1, " Found ", len(sea_area.geoms), "contiguous patch(es).") for polygon in sea_area.geoms: seed = numpy.array(polygon.representative_point()) if 'SEA' in vector_map.seeds: vector_map.seeds['SEA'].append(seed) else: vector_map.seeds['SEA'] = [seed]
def include_water(vector_map, tile): UI.vprint(0, "-> Dealing with inland water") water_layer = OSM.OSM_layer() custom_water = FNAMES.custom_water(tile.lat, tile.lon) if os.path.isfile(custom_water): water_layer.update_dicosm(custom_water, target_tags=None) else: queries = [ 'rel["natural"="water"]', 'rel["waterway"="riverbank"]', 'way["natural"="water"]', 'way["waterway"="riverbank"]', 'way["waterway"="dock"]' ] tags_of_interest = ["name"] if not OSM.OSM_queries_to_OSM_layer(queries, water_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='water'): return 0 UI.vprint(1, " * Building water multipolygon.") water_area = OSM.OSM_to_MultiPolygon(water_layer, tile.lat, tile.lon) if not water_area.is_empty: UI.vprint(1, " Cleaning it.") try: (idx_water, dico_water) = VECT.MultiPolygon_to_Indexed_Polygons( water_area, merge_overlappings=tile.clean_bad_geometries, limit=VECT.max_pols_for_merge) except: return 0 UI.vprint( 2, " Number of water Multipolygons : " + str(len(dico_water))) UI.vprint(1, " Encoding it.") vector_map.encode_MultiPolygon(dico_water, tile.dem.alt_vec, 'WATER', area_limit=tile.min_area / 10000, simplify=0.00001, check=True) return 1
def include_airports(vector_map, tile, patches_area): # patches_area if not None is the extent to substract from runway_area # we enlarge it (local copy) slightly for security patches_area = patches_area.buffer(0.00002) UI.vprint(0, "-> Dealing with airports") airport_layer = OSM.OSM_layer() queries = [('rel["aeroway"="runway"]', 'rel["aeroway"="taxiway"]', 'rel["aeroway"="apron"]', 'way["aeroway"="runway"]', 'way["aeroway"="taxiway"]', 'way["aeroway"="apron"]')] tags_of_interest = ["all"] if not OSM.OSM_queries_to_OSM_layer(queries, airport_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='airports'): return 0 # Runway and taxiway center lines (they will be incorporated to ensure triangles # are not too badly aligned with these lines (improves removal of bumpiness) runway_network = OSM.OSM_to_MultiLineString(airport_layer, tile.lat, tile.lon, []) # Buffer these for later smoothing runway_area = VECT.improved_buffer(runway_network, 0.0003, 0.0001, 0.00001) if not runway_area: return 0 runway_area = runway_area.difference(patches_area).buffer(0).simplify( 0.00001) runway_network = runway_network.difference(patches_area) # Now we encode in vector_map vector_map.encode_MultiLineString(runway_network, tile.dem.alt_vec, 'DUMMY', check=True, refine=20) vector_map.encode_MultiPolygon(runway_area, tile.dem.alt_vec, 'SMOOTHED_ALT', check=True, refine=50) return 1
def include_airports(vector_map, tile): UI.vprint(0, "-> Dealing with airports") airport_layer = OSM.OSM_layer() queries = [('node["aeroway"]', 'way["aeroway"]', 'rel["aeroway"]')] tags_of_interest = ["all"] if not OSM.OSM_queries_to_OSM_layer(queries, airport_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='airports'): return 0 dico_airports = {} APT.discover_airport_names(airport_layer, dico_airports) APT.attach_surfaces_to_airports(airport_layer, dico_airports) APT.sort_and_reconstruct_runways(tile, airport_layer, dico_airports) APT.discard_unwanted_airports(tile, dico_airports) APT.build_hangar_areas(tile, airport_layer, dico_airports) APT.build_apron_areas(tile, airport_layer, dico_airports) APT.build_taxiway_areas(tile, airport_layer, dico_airports) APT.update_airport_boundaries(tile, dico_airports) APT.list_airports_and_runways(dico_airports) UI.vprint(1, " Loading elevation data and smoothing it over airports.") tile.dem = DEM.DEM(tile.lat, tile.lon, tile.custom_dem, tile.fill_nodata or "to zero", info_only=False) APT.smooth_raster_over_airports(tile, dico_airports) (patches_area, patches_list) = include_patches(vector_map, tile) runway_taxiway_apron_area = APT.encode_runways_taxiways_and_aprons( tile, airport_layer, dico_airports, vector_map, patches_list) treated_area = ops.cascaded_union( [patches_area, runway_taxiway_apron_area]) APT.encode_hangars(tile, dico_airports, vector_map, patches_list) APT.flatten_helipads(airport_layer, vector_map, tile, treated_area) #APT.encode_aprons(tile,dico_airports,vector_map) apt_array = APT.build_airport_array(tile, dico_airports) return (apt_array, treated_area)
else: query += char else: query = None if nargs in (7, 8): epsg_code = sys.argv[3] if nargs == 8: grid_size_or_bbox = eval(sys.argv[4]) else: grid_size = 0.02 if epsg_code == '4326' else 2000 pixel_size = float(sys.argv[nargs - 3]) buffer_width = float(sys.argv[nargs - 2]) / pixel_size mask_width = int(int(sys.argv[nargs - 1]) / pixel_size) pixel_size = pixel_size / 111120 if epsg_code == '4326' else pixel_size # assuming meters if not degrees vector_map = VECT.Vector_Map() osm_layer = OSM.OSM_layer() if not os.path.exists(cached_file_name): print("OSM query...") if not OSM.OSM_query_to_OSM_layer( query, '', osm_layer, cached_file_name=cached_file_name): print("OSM query failed. Exiting.") del (vector_map) time.sleep(1) sys.exit(0) else: print("Recycling OSM file...") osm_layer.update_dicosm(cached_file_name, None) print("Transform to multipolygon...") multipolygon_area = OSM.OSM_to_MultiPolygon(osm_layer, 0, 0) del (osm_layer) if not multipolygon_area.area:
def include_roads(vector_map, tile): if not tile.road_level: return UI.vprint(0, "-> Dealing with roads") tags_of_interest = ["bridge", "tunnel"] #Need to evaluate if including bridges is better or worse tags_for_exclusion = set(["bridge", "tunnel"]) #tags_for_exclusion=set(["tunnel"]) road_layer = OSM.OSM_layer() queries = [ 'way["highway"="motorway"]', 'way["highway"="trunk"]', 'way["highway"="primary"]', 'way["highway"="secondary"]', 'way["railway"="rail"]', 'way["railway"="narrow_gauge"]' ] if not OSM.OSM_queries_to_OSM_layer(queries, road_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='big_roads'): return 0 UI.vprint(1, " * Checking which large roads need levelling.") (road_network_banked, road_network_flat) = OSM.OSM_to_MultiLineString( road_layer, tile.lat, tile.lon, tags_for_exclusion, lambda way: tile.dem.way_is_too_much_banked( way, tile.road_banking_limit)) if UI.red_flag: return 0 if tile.road_level >= 2: road_layer = OSM.OSM_layer() queries=[\ 'way["highway"="tertiary"]'] if tile.road_level >= 3: queries += [ 'way["highway"="unclassified"]', 'way["highway"="residential"]' ] if tile.road_level >= 4: queries += ['way["highway"="service"]'] if tile.road_level >= 5: queries += ['way["highway"="track"]'] if not OSM.OSM_queries_to_OSM_layer(queries, road_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='small_roads'): return 0 UI.vprint(1, " * Checking which smaller roads need levelling.") timer = time.time() (road_network_banked_2,road_network_flat_2)=OSM.OSM_to_MultiLineString(road_layer,\ tile.lat,tile.lon,tags_for_exclusion,lambda way: tile.dem.way_is_too_much_banked(way,tile.road_banking_limit),limit_segs=tile.max_levelled_segs) UI.vprint(3, "Time for check :", time.time() - timer) road_network_banked = geometry.MultiLineString( list(road_network_banked) + list(road_network_banked_2)).simplify(0.000005) if not road_network_banked.is_empty: UI.vprint(1, " * Buffering banked road network as multipolygon.") timer = time.time() road_area = VECT.improved_buffer(road_network_banked, 0.00004, 0.00002, 0.000005) UI.vprint(3, "Time for improved buffering:", time.time() - timer) if UI.red_flag: return 0 UI.vprint(1, " Encoding it.") vector_map.encode_MultiPolygon(road_area, tile.dem.alt_vec_road, 'INTERP_ALT', check=True, refine=False) if UI.red_flag: return 0 if not road_network_flat.is_empty: road_network_flat = road_network_flat.simplify( 0.00001) #.difference(road_area) UI.vprint( 1, " * Encoding the remaining primary road network as linestrings." ) vector_map.encode_MultiLineString(road_network_flat, tile.dem.alt_vec_road, 'DUMMY', check=True) return 1
def include_patches(vector_map, tile): UI.vprint(0, "-> Dealing with patches") patch_dir = FNAMES.patch_dir(tile.lat, tile.lon) if not os.path.exists(patch_dir): return geometry.Polygon() patch_layer = OSM.OSM_layer() for pfile_name in os.listdir(patch_dir): if pfile_name[-10:] != '.patch.osm': continue UI.vprint(1, " " + pfile_name) try: patch_layer.update_dicosm(os.path.join(patch_dir, pfile_name), target_tags=None) except: UI.vprint(1, " Error in treating", pfile_name, " , skipped.") dw = patch_layer.dicosmw dn = patch_layer.dicosmn df = patch_layer.dicosmfirst dt = patch_layer.dicosmtags patches_area = geometry.Polygon() def tanh_profile(alpha, x): return (numpy.tanh( (x - 0.5) * alpha) / numpy.tanh(0.5 * alpha) + 1) / 2 def spline_profile(x): return 3 * x**2 - 2 * x**3 def plane_profile(x): return x # reorganize them so that untagged dummy ways are treated last (due to altitude being first done kept for all) #waylist=list(set(dw).intersection(df['w']).intersection(dt['w']))+list(set(dw).intersection(df['w']).difference(dt['w'])) waylist = list(set(df['w']).intersection(dt['w'])) + list( set(df['w']).difference(dt['w'])) for wayid in waylist: way = numpy.array([dn[nodeid] for nodeid in dw[wayid]], dtype=numpy.float) way = way - numpy.array([[tile.lon, tile.lat]]) alti_way_orig = tile.dem.alt_vec(way) cplx_patch = False if wayid in dt['w']: wtags = dt['w'][wayid] if 'cst_alt_abs' in wtags: alti_way = numpy.ones( (len(way), 1)) * float(wtags['cst_alt_abs']) elif 'cst_alt_rel' in wtags: alti_way = tile.dem.alt_vec_mean(way) + float( wtags['cst_alt_rel']) elif 'var_alt_rel' in wtags: alti_way = alti_way_orig + float(wtags['var_alt_rel']) elif 'altitude' in wtags: # deprecated : for backward compatibility only try: alti_way = numpy.ones( (len(way), 1)) * float(wtags['altitude']) except: alti_way = tile.dem.alt_vec_mean(way) elif 'altitude_high' in wtags: cplx_patch = True if len(way) != 5 or (way[0] != way[-1]).all(): UI.vprint( 1, " Wrong number of nodes or non closed way for a altitude_high/altitude_low polygon, skipped." ) continue short_high = way[-2:] short_low = way[1:3] try: altitude_high = float(wtags['altitude_high']) altitude_low = float(wtags['altitude_low']) except: altitude_high = tile.dem.alt_vec(short_high).mean() altitude_low = tile.dem.alt_vec(short_low).mean() try: cell_size = float(wtags['cell_size']) except: cell_size = 10 try: rnw_profile = wtags['profile'] except: rnw_profile = 'plane' try: alpha = float(wtags['steepness']) except: alpha = 2 if 'tanh' in rnw_profile: rnw_profile = lambda x: tanh_profile(alpha, x) elif rnw_profile == 'spline': rnw_profile = spline_profile else: rnw_profile = plane_profile rnw_vect = (short_high[0] + short_high[1] - short_low[0] - short_low[1]) / 2 rnw_length = sqrt(rnw_vect[0]**2 * cos(tile.lat * pi / 180)**2 + rnw_vect[1]**2) * 111120 cuts_long = int(rnw_length / cell_size) if cuts_long: cuts_long += 1 way=numpy.array([way[0]+i/cuts_long*(way[1]-way[0]) for i in range(cuts_long)]+\ [way[1]]+[way[2]+i/cuts_long*(way[3]-way[2]) for i in range(cuts_long)]+[way[3],way[4]]) alti_way = numpy.array([ altitude_high - rnw_profile(i / cuts_long) * (altitude_high - altitude_low) for i in range(cuts_long + 1) ]) alti_way = numpy.hstack( [alti_way, alti_way[::-1], alti_way[0]]) else: alti_way = alti_way_orig else: alti_way = alti_way_orig if not cplx_patch: for i in range(len(way)): nodeid = dw[wayid][i] if nodeid in dt: ntags = dt['n'][nodeid] if 'alt_abs' in ntags: alti_way[i] = float(ntags['alt_abs']) elif 'alt_rel' in ntags: alti_way[i] = alti_way_orig[i] + float( ntags['alt_rel']) alti_way = alti_way.reshape((len(alti_way), 1)) if (way[0] == way[-1]).all(): try: pol = geometry.Polygon(way) if pol.is_valid and pol.area: patches_area = patches_area.union(pol) vector_map.insert_way(numpy.hstack([way, alti_way]), 'INTERP_ALT', check=True) seed = numpy.array(pol.representative_point()) if 'INTERP_ALT' in vector_map.seeds: vector_map.seeds['INTERP_ALT'].append(seed) else: vector_map.seeds['INTERP_ALT'] = [seed] else: UI.vprint(2, "Skipping invalid patch polygon.") except: UI.vprint(2, "Skipping invalid patch polygon.") else: vector_map.insert_way(numpy.hstack([way, alti_way]), 'DUMMY', check=True) return patches_area
def zone_list_to_ortho_dico(tile): # tile.zone_list is a list of 3-uples of the form ([(lat0,lat0),...(latN,lonN),zoomlevel,provider_code) # where higher lines have priority over lower ones. masks_im=Image.new("L",(4096,4096),'black') masks_draw=ImageDraw.Draw(masks_im) airport_array=numpy.zeros((4096,4096),dtype=numpy.bool) if tile.cover_airports_with_highres: UI.vprint(1,"-> Checking airport locations for upgraded zoomlevel.") airport_layer=OSM.OSM_layer() queries=[('rel["aeroway"="runway"]','rel["aeroway"="taxiway"]','rel["aeroway"="apron"]', 'way["aeroway"="runway"]','way["aeroway"="taxiway"]','way["aeroway"="apron"]')] tags_of_interest=["all"] if not OSM.OSM_queries_to_OSM_layer(queries,airport_layer,tile.lat,tile.lon,tags_of_interest,cached_suffix='airports'): return 0 runway_network=OSM.OSM_to_MultiLineString(airport_layer,tile.lat,tile.lon) runway_area=VECT.improved_buffer(runway_network,0.0003,0.0001,0.00001) runway_area=VECT.ensure_MultiPolygon(runway_area) for polygon in runway_area.geoms: (xmin,ymin,xmax,ymax)=polygon.bounds # extension xmin-=1000*tile.cover_extent*GEO.m_to_lon(tile.lat) xmax+=1000*tile.cover_extent*GEO.m_to_lon(tile.lat) ymax+=1000*tile.cover_extent*GEO.m_to_lat ymin-=1000*tile.cover_extent*GEO.m_to_lat # round off to texture boundaries at tile.cover_zl zoomlevel (til_x_left,til_y_top)=GEO.wgs84_to_orthogrid(ymax+tile.lat,xmin+tile.lon,tile.cover_zl) (ymax,xmin)=GEO.gtile_to_wgs84(til_x_left,til_y_top,tile.cover_zl) ymax-=tile.lat; xmin-=tile.lon (til_x_left,til_y_top)=GEO.wgs84_to_orthogrid(ymin+tile.lat,xmax+tile.lon,tile.cover_zl) (ymin,xmax)=GEO.gtile_to_wgs84(til_x_left+16,til_y_top+16,tile.cover_zl) ymin-=tile.lat; xmax-=tile.lon # mark to airport_array colmin=round(xmin*4095) colmax=round(xmax*4095) rowmax=round((1-ymin)*4095) rowmin=round((1-ymax)*4095) airport_array[rowmin:rowmax+1,colmin:colmax+1]=1 del(airport_layer) del(runway_network) del(runway_area) dico_tmp={} dico_customzl={} i=1 base_zone=((tile.lat,tile.lon,tile.lat,tile.lon+1,tile.lat+1,tile.lon+1,tile.lat+1,tile.lon,tile.lat,tile.lon),tile.default_zl,tile.default_website) for region in [base_zone]+tile.zone_list[::-1]: dico_tmp[i]=(region[1],region[2]) pol=[(round((x-tile.lon)*4095),round((tile.lat+1-y)*4095)) for (x,y) in zip(region[0][1::2],region[0][::2])] masks_draw.polygon(pol,fill=i) i+=1 til_x_min,til_y_min=GEO.wgs84_to_orthogrid(tile.lat+1,tile.lon,tile.mesh_zl) til_x_max,til_y_max=GEO.wgs84_to_orthogrid(tile.lat,tile.lon+1,tile.mesh_zl) for til_x in range(til_x_min,til_x_max+1,16): for til_y in range(til_y_min,til_y_max+1,16): (latp,lonp)=GEO.gtile_to_wgs84(til_x+8,til_y+8,tile.mesh_zl) lonp=max(min(lonp,tile.lon+1),tile.lon) latp=max(min(latp,tile.lat+1),tile.lat) x=round((lonp-tile.lon)*4095) y=round((tile.lat+1-latp)*4095) (zoomlevel,provider_code)=dico_tmp[masks_im.getpixel((x,y))] if airport_array[y,x]: zoomlevel=max(zoomlevel,tile.cover_zl) til_x_text=16*(int(til_x/2**(tile.mesh_zl-zoomlevel))//16) til_y_text=16*(int(til_y/2**(tile.mesh_zl-zoomlevel))//16) dico_customzl[(til_x,til_y)]=(til_x_text,til_y_text,zoomlevel,provider_code) return dico_customzl
def include_patches(vector_map, tile): def tanh_profile(alpha, x): return (numpy.tanh( (x - 0.5) * alpha) / numpy.tanh(0.5 * alpha) + 1) / 2 def spline_profile(x): return 3 * x**2 - 2 * x**3 def plane_profile(x): return x patches_list = [] patches_area = geometry.Polygon() patch_dir = FNAMES.patch_dir(tile.lat, tile.lon) if not os.path.exists(patch_dir): return (patches_area, patches_list) for pfile_name in os.listdir(patch_dir): if pfile_name[-10:] != '.patch.osm': continue UI.vprint(1, " Patching", pfile_name) patch_layer = OSM.OSM_layer() try: patch_layer.update_dicosm(os.path.join(patch_dir, pfile_name), input_tags=None, target_tags=None) except: UI.vprint(1, " Error in treating", pfile_name, ", skipped.") patches_list.append(pfile_name[:-10]) dw = patch_layer.dicosmw dn = patch_layer.dicosmn df = patch_layer.dicosmfirst dt = patch_layer.dicosmtags patches_area = geometry.Polygon() # reorganize them so that untagged dummy ways are treated last (due to altitude being first done kept for all) #waylist=list(set(dw).intersection(df['w']).intersection(dt['w']))+list(set(dw).intersection(df['w']).difference(dt['w'])) #HACK waylist = tuple(df['w'].intersection(dt['w'])) + tuple( df['w'].difference(dt['w'])) for wayid in waylist: way = numpy.array([dn[nodeid] for nodeid in dw[wayid]], dtype=numpy.float) way = way - numpy.array([[tile.lon, tile.lat]]) alti_way_orig = tile.dem.alt_vec(way) cplx_way = False if wayid in dt['w']: wtags = dt['w'][wayid] if 'cst_alt_abs' in wtags: alti_way = numpy.ones( (len(way), 1)) * float(wtags['cst_alt_abs']) elif 'cst_alt_rel' in wtags: alti_way = numpy.ones( (len(way), 1)) * (numpy.mean(tile.dem.alt_vec(way)) + float(wtags['cst_alt_rel'])) elif 'var_alt_rel' in wtags: alti_way = alti_way_orig + float(wtags['var_alt_rel']) elif 'altitude' in wtags: # deprecated : for backward compatibility only try: alti_way = numpy.ones( (len(way), 1)) * float(wtags['altitude']) except: alti_way = numpy.ones( (len(way), 1)) * numpy.mean(tile.dem.alt_vec(way)) elif 'altitude_high' in wtags: cplx_way = True if len(way) != 5 or (way[0] != way[-1]).all(): UI.vprint( 1, " Wrong number of nodes or non closed way for a altitude_high/altitude_low polygon, skipped." ) continue short_high = way[-2:] short_low = way[1:3] try: altitude_high = float(wtags['altitude_high']) altitude_low = float(wtags['altitude_low']) except: altitude_high = tile.dem.alt_vec(short_high).mean() altitude_low = tile.dem.alt_vec(short_low).mean() try: cell_size = float(wtags['cell_size']) except: cell_size = 10 try: rnw_profile = wtags['profile'] except: rnw_profile = 'plane' try: alpha = float(wtags['steepness']) except: alpha = 2 if 'tanh' in rnw_profile: rnw_profile = lambda x: tanh_profile(alpha, x) elif rnw_profile == 'spline': rnw_profile = spline_profile else: rnw_profile = plane_profile rnw_vect = (short_high[0] + short_high[1] - short_low[0] - short_low[1]) / 2 rnw_length = sqrt(rnw_vect[0]**2 * cos(tile.lat * pi / 180)**2 + rnw_vect[1]**2) * 111120 cuts_long = int(rnw_length / cell_size) if cuts_long: cuts_long += 1 way=numpy.array([way[0]+i/cuts_long*(way[1]-way[0]) for i in range(cuts_long)]+\ [way[1]]+[way[2]+i/cuts_long*(way[3]-way[2]) for i in range(cuts_long)]+[way[3],way[4]]) alti_way = numpy.array([ altitude_high - rnw_profile(i / cuts_long) * (altitude_high - altitude_low) for i in range(cuts_long + 1) ]) alti_way = numpy.hstack( [alti_way, alti_way[::-1], alti_way[0]]) else: alti_way = alti_way_orig else: alti_way = alti_way_orig if not cplx_way: for i in range(len(way)): nodeid = dw[wayid][i] if nodeid in dt['n']: ntags = dt['n'][nodeid] if 'alt_abs' in ntags: alti_way[i] = float(ntags['alt_abs']) elif 'alt_rel' in ntags: alti_way[i] = alti_way_orig[i] + float( ntags['alt_rel']) alti_way = alti_way.reshape((len(alti_way), 1)) if (way[0] == way[-1]).all(): try: pol = geometry.Polygon(way) if pol.is_valid and pol.area: patches_area = patches_area.union(pol) vector_map.insert_way(numpy.hstack([way, alti_way]), 'INTERP_ALT', check=True) seed = numpy.array(pol.representative_point()) if 'INTERP_ALT' in vector_map.seeds: vector_map.seeds['INTERP_ALT'].append(seed) else: vector_map.seeds['INTERP_ALT'] = [seed] if cplx_way and cuts_long: for i in range(1, cuts_long): id0 = vector_map.dico_nodes[tuple(way[i])] id1 = vector_map.dico_nodes[tuple(way[-2 - i])] vector_map.insert_edge( id0, id1, vector_map.dico_attributes['DUMMY']) else: UI.vprint(2, " Skipping invalid patch polygon.") except: UI.vprint(2, " Skipping invalid patch polygon.") else: vector_map.insert_way(numpy.hstack([way, alti_way]), 'DUMMY', check=True) for pdir_name in os.listdir(patch_dir): if not os.path.isdir(os.path.join(patch_dir, pdir_name)): continue UI.vprint(1, " Including OBJ8 objects from", pdir_name) patches_list.append(pdir_name) for pfile_name in os.listdir(os.path.join(patch_dir, pdir_name)): pfile_namelong = os.path.join(patch_dir, pdir_name, pfile_name) try: pfile = open(pfile_namelong, "r") except: continue firstline = pfile.readline() if not 'ANCHOR' in firstline: UI.vprint( 1, " Object ", pfile_name, " is missing and ANCHOR in first line, skipping it.") continue pfile.close() try: (lon_anchor, lat_anchor, alt_anchor, heading_anchor) = [float(x) for x in firstline.split()[1:]] except: try: (lon_anchor, lat_anchor, heading_anchor) = [ float(x) for x in firstline.split()[1:] ] alt_anchor = tile.dem.alt( (lon_anchor - tile.lon, lat_anchor - tile.lat)) except: UI.vprint(1, " Anchor wrongly encode for : ", pfile_name, " skipping that one.") continue patches_area = patches_area.union( keep_obj8(lat_anchor, lon_anchor, alt_anchor, heading_anchor, pfile_namelong, vector_map, tile)) return (patches_area, patches_list)
def include_water(vector_map, tile): large_lake_threshold = tile.max_area * 1e6 / (GEO.lat_to_m * GEO.lon_to_m(tile.lat + 0.5)) def filter_large_lakes(pol, osmid, dicosmtags): if pol.area < large_lake_threshold: return False area = int(pol.area * GEO.lat_to_m * GEO.lon_to_m(tile.lat + 0.5) / 1e6) if (osmid in dicosmtags) and ('name' in dicosmtags[osmid]): if (dicosmtags[osmid]['name'] in good_imagery_list): UI.vprint(1, " * ", dicosmtags[osmid]['name'], "kept will complete imagery although it is", area, "km^2.") return False else: UI.vprint( 1, " * ", dicosmtags[osmid]['name'], "will be masked like the sea due to its large area of", area, "km^2.") return True else: pt = pol.exterior.coords[ 0] if 'Multi' not in pol.geom_type else pol[0].exterior.coords[ 0] UI.vprint(1, " * ", "Some large OSM water patch close to lat=", '{:.2f}'.format(pt[1] + tile.lon), "lon=", '{:.2f}'.format(pt[0] + tile.lat), "will be masked due to its large area of", area, "km^2.") return True UI.vprint(0, "-> Dealing with inland water") water_layer = OSM.OSM_layer() custom_water = FNAMES.custom_water(tile.lat, tile.lon) custom_water_dir = FNAMES.custom_water_dir(tile.lat, tile.lon) if os.path.isfile(custom_water): UI.vprint(1, " * User defined custom water data detected.") water_layer.update_dicosm(custom_water, input_tags=None, target_tags=None) elif os.path.isdir(custom_water_dir): UI.vprint( 1, " * User defined custom water data detected (multiple files).") for osm_file in os.listdir(custom_water_dir): UI.vprint(2, " ", osm_file) water_layer.update_dicosm(os.path.join(custom_water_dir, osm_file), input_tags=None, target_tags=None) water_layer.write_to_file(custom_water) else: queries = [ 'rel["natural"="water"]', 'rel["waterway"="riverbank"]', 'way["natural"="water"]', 'way["waterway"="riverbank"]', 'way["waterway"="dock"]' ] tags_of_interest = ["name"] if not OSM.OSM_queries_to_OSM_layer(queries, water_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='water'): return 0 UI.vprint(1, " * Building water multipolygon.") (water_area, sea_equiv_area) = OSM.OSM_to_MultiPolygon(water_layer, tile.lat, tile.lon, filter_large_lakes) if not water_area.is_empty: UI.vprint(1, " Cleaning it.") try: (idx_water, dico_water) = VECT.MultiPolygon_to_Indexed_Polygons( water_area, merge_overlappings=tile.clean_bad_geometries) except: return 0 UI.vprint( 2, " Number of water Multipolygons : " + str(len(dico_water))) UI.vprint(1, " Encoding it.") vector_map.encode_MultiPolygon(dico_water, tile.dem.alt_vec, 'WATER', area_limit=tile.min_area / 10000, simplify=tile.water_simplification * GEO.m_to_lat, check=True) if not sea_equiv_area.is_empty: UI.vprint( 1, " Separate treatment for larger pieces requiring masks.") try: (idx_water, dico_water) = VECT.MultiPolygon_to_Indexed_Polygons( sea_equiv_area, merge_overlappings=tile.clean_bad_geometries) except: return 0 UI.vprint( 2, " Number of water Multipolygons : " + str(len(dico_water))) UI.vprint(1, " Encoding them.") vector_map.encode_MultiPolygon(dico_water, tile.dem.alt_vec, 'SEA_EQUIV', area_limit=tile.min_area / 10000, simplify=tile.water_simplification * GEO.m_to_lat, check=True) return 1
def include_roads(vector_map, tile, apt_array, apt_area): def road_is_too_much_banked(way, filtered_segs): (col, row) = numpy.minimum(numpy.maximum(numpy.round(way[0] * 1000), 0), 1000) if apt_array[int(1000 - row), int(col)]: return True (col, row) = numpy.minimum(numpy.maximum(numpy.round(way[-1] * 1000), 0), 1000) if apt_array[int(1000 - row), int(col)]: return True if filtered_segs >= tile.max_levelled_segs: return False return (numpy.abs( tile.dem.alt_vec(way) - tile.dem.alt_vec(VECT.shift_way(way, tile.lane_width))) >= tile.road_banking_limit).any() def alt_vec_shift(way): return tile.dem.alt_vec(VECT.shift_way(way, tile.lane_width)) if not tile.road_level: return UI.vprint(0, "-> Dealing with roads") tags_of_interest = ["bridge", "tunnel"] #Need to evaluate if including bridges is better or worse tags_for_exclusion = set(["bridge", "tunnel"]) #tags_for_exclusion=set(["tunnel"]) road_layer = OSM.OSM_layer() queries = [ 'way["highway"="motorway"]', 'way["highway"="trunk"]', 'way["highway"="primary"]', 'way["highway"="secondary"]', 'way["railway"="rail"]', 'way["railway"="narrow_gauge"]' ] if not OSM.OSM_queries_to_OSM_layer(queries, road_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='big_roads'): return 0 UI.vprint(1, " * Checking which large roads need levelling.") (road_network_banked, road_network_flat) = OSM.OSM_to_MultiLineString( road_layer, tile.lat, tile.lon, tags_for_exclusion, road_is_too_much_banked) if UI.red_flag: return 0 if tile.road_level >= 2: road_layer = OSM.OSM_layer() queries=[\ 'way["highway"="tertiary"]'] if tile.road_level >= 3: queries += [ 'way["highway"="unclassified"]', 'way["highway"="residential"]' ] if tile.road_level >= 4: queries += ['way["highway"="service"]'] if tile.road_level >= 5: queries += ['way["highway"="track"]'] if not OSM.OSM_queries_to_OSM_layer(queries, road_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='small_roads'): return 0 UI.vprint(1, " * Checking which smaller roads need levelling.") timer = time.time() (road_network_banked_2,road_network_flat_2)=OSM.OSM_to_MultiLineString(road_layer,\ tile.lat,tile.lon,tags_for_exclusion,road_is_too_much_banked) UI.vprint(3, "Time for check :", time.time() - timer) road_network_banked = geometry.MultiLineString( list(road_network_banked) + list(road_network_banked_2)) if not road_network_banked.is_empty: UI.vprint(1, " * Buffering banked road network as multipolygon.") timer = time.time() road_area = VECT.improved_buffer(road_network_banked.difference( VECT.improved_buffer(apt_area, tile.lane_width + 2, 0, 0)), tile.lane_width, 2, 0.5, show_progress=True) UI.vprint(3, "Time for improved buffering:", time.time() - timer) if UI.red_flag: return 0 UI.vprint(1, " Encoding it.") vector_map.encode_MultiPolygon(road_area, alt_vec_shift, 'INTERP_ALT', check=True, refine=100) if UI.red_flag: return 0 if not road_network_flat.is_empty: road_network_flat = road_network_flat.difference( VECT.improved_buffer(apt_area, 15, 0, 0)).simplify(0.00001) UI.vprint( 1, " * Encoding the remaining primary road network as linestrings." ) vector_map.encode_MultiLineString(road_network_flat, tile.dem.alt_vec, 'DUMMY', check=True) return 1
def build_curv_tol_weight_map(tile, weight_array): if tile.apt_curv_tol != tile.curvature_tol: UI.vprint( 1, "-> Modifying curv_tol weight map according to runway locations.") airport_layer = OSM.OSM_layer() queries = [('rel["aeroway"="runway"]', 'rel["aeroway"="taxiway"]', 'rel["aeroway"="apron"]', 'way["aeroway"="runway"]', 'way["aeroway"="taxiway"]', 'way["aeroway"="apron"]')] tags_of_interest = ["all"] if not OSM.OSM_queries_to_OSM_layer(queries, airport_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='airports'): return 0 runway_network = OSM.OSM_to_MultiLineString(airport_layer, tile.lat, tile.lon) runway_area = VECT.improved_buffer(runway_network, 0.0003, 0.0001, 0.00001) if not runway_area: return 0 runway_area = VECT.ensure_MultiPolygon(runway_area) for polygon in runway_area.geoms if ( 'Multi' in runway_area.geom_type or 'Collection' in runway_area.geom_type) else [runway_area]: (xmin, ymin, xmax, ymax) = polygon.bounds x_shift = 1000 * tile.apt_curv_ext * GEO.m_to_lon(tile.lat) y_shift = 1000 * tile.apt_curv_ext * GEO.m_to_lat colmin = round((xmin - x_shift) * 1000) colmax = round((xmax + x_shift) * 1000) rowmax = round(((1 - ymin) + y_shift) * 1000) rowmin = round(((1 - ymax) - y_shift) * 1000) weight_array[ rowmin:rowmax + 1, colmin:colmax + 1] = tile.curvature_tol / tile.apt_curv_tol if tile.apt_curv_tol > 0 else 1 del (airport_layer) del (runway_network) del (runway_area) if tile.coast_curv_tol != tile.curvature_tol: UI.vprint( 1, "-> Modifying curv_tol weight map according to coastline location." ) sea_layer = OSM.OSM_layer() queries = ['way["natural"="coastline"]'] tags_of_interest = [] if not OSM.OSM_queries_to_OSM_layer(queries, sea_layer, tile.lat, tile.lon, tags_of_interest, cached_suffix='coastline'): return 0 for nodeid in sea_layer.dicosmn: (lonp, latp) = [float(x) for x in sea_layer.dicosmn[nodeid]] x_shift = 1000 * tile.coast_curv_ext * GEO.m_to_lon(tile.lat) y_shift = tile.coast_curv_ext / (111.12) colmin = round((lonp - tile.lon - x_shift) * 1000) colmax = round((lonp - tile.lon + x_shift) * 1000) rowmax = round((tile.lat + 1 - latp + y_shift) * 1000) rowmin = round((tile.lat + 1 - latp - y_shift) * 1000) weight_array[ rowmin:rowmax + 1, colmin:colmax + 1] = tile.curvature_tol / tile.coast_curv_tol if tile.coast_curv_tol > 0 else 1 del (sea_layer) # It could be of interest to write the weight file as a png for user editing #Image.fromarray((weight_array!=1).astype(numpy.uint8)*255).save('weight.png') return