def load_files(self, *files: List[Path], verbose: bool = False): files = [Path(file) for file in files] if verbose: from tqdm import tqdm files = tqdm(files, desc="RoadEngine Load") for file in files: if not file.exists(): raise FileNotFoundError(f"Could not load {file.absolute()}!") gdf = gpd.read_file(file.absolute()) self.cache = self.cache.append(gdf, ignore_index=True, sort=False) tileset = set() geometry = gdf['geometry'] if verbose: geometry = tqdm(geometry, desc="RoadEngine Quadtree") for geom in geometry: if type(geom) != LineString: continue root = bounding_tile(*geom.bounds) tileset |= {root} self.cached_tiles |= set(simplify(*tileset)) if verbose: logger.info("RoadEngine is building an R-Tree...") self.cache.sindex if verbose: logger.info("RoadEngine R-Tree done!")
def __get_from_osmnx(self, tile: Tile) -> gpd.GeoDataFrame: bbox: LngLatBbox = bounds(tile) west, south, east, north = bbox west -= LAT_LNG_BUFFER east += LAT_LNG_BUFFER north += LAT_LNG_BUFFER south -= LAT_LNG_BUFFER try: graph = ox.graph_from_bbox(north, south, east, west, simplify=False, retain_all=True) gdfs = ox.graph_to_gdfs(graph) for gdf in gdfs: self.cache = self.cache.append(gdf, ignore_index=True, sort=False) except ox.core.EmptyOverpassResponse: pass except ValueError as e: if "not enough values" in str(e): print( f"[WARNING] Could not load tile {tile}! Assuming it is empty..." ) else: raise e self.cached_tiles |= {tile} self.cached_tiles = set(simplify(*self.cached_tiles)) return self.__get_from_cache(tile)
def quadkeys_to_poly(quadkeys): quadkeys = sorted(set(quadkeys)) tiles = [mercantile.quadkey_to_tile(qk) for qk in quadkeys] tiles = mercantile.simplify(tiles) quadkeys = [mercantile.quadkey(t) for t in tiles] polys = quadkeys_to_polys(quadkeys) poly = shapely.ops.unary_union(polys) return poly
def test_simplify_removal(): ''' Verify that tiles are being removed by simplify() ''' tiles = [ (1298, 3129, 13), (649, 1564, 12), (650, 1564, 12), ] simplified = mercantile.simplify(tiles) assert (1298, 3129, 13) not in simplified, 'Tile covered by a parent' assert (650, 1564, 12) in simplified, 'Highest-level tile' assert (649, 1564, 12) in simplified, 'Also highest-level tile'
def test_simplify(): children = mercantile.children(243, 166, 9, zoom=12) assert len(children) == 64 children = children[:-3] children.append(children[0]) simplified = mercantile.simplify(children) targets = [ (487, 332, 10), (486, 332, 10), (487, 333, 10), (973, 667, 11), (973, 666, 11), (972, 666, 11), (1944, 1334, 12), ] for target in targets: assert target in simplified
def missing_quadkeys(mosaic: Dict, shp_path: str, bounds: List[float] = None, simplify: bool = True) -> Dict: """Find quadkeys over land missing from mosaic Args: - mosaic: mosaic definition - shp_path: path to Natural Earth shapefile of land boundaries - bounds: force given bounds - simplify: reduce size of the tileset as much as possible by merging leaves into parents Returns: - GeoJSON FeatureCollection of missing tiles """ bounds = bounds or mosaic['bounds'] top_tile = mercantile.bounding_tile(*bounds) gdf = gpd.read_file(shp_path) quadkey_zoom = mosaic.get('quadkey_zoom', mosaic['minzoom']) # Remove null island # Keep the landmasses that are visible at given zoom gdf = gdf[gdf['max_zoom'] <= quadkey_zoom] land_tiles = find_child_land_tiles(top_tile, gdf, quadkey_zoom) quadkeys = {mercantile.quadkey(tile) for tile in land_tiles} mosaic_quadkeys = set(mosaic['tiles'].keys()) not_in_mosaic = quadkeys.difference(mosaic_quadkeys) not_in_mosaic = [mercantile.quadkey_to_tile(qk) for qk in not_in_mosaic] if simplify: not_in_mosaic = mercantile.simplify(not_in_mosaic) features = [mercantile.feature(tile) for tile in not_in_mosaic] return {'type': 'FeatureCollection', 'features': features}