def summarize_by_huc12(geometries): """Calculate current and projected urbanization for each decade from 2020 to 2100. Parameters ---------- geometries : Series of pygeos geometries, indexed by HUC12 id """ index = [] results = [] for huc12, geometry in Bar( "Calculating Urbanization counts for HUC12", max=len(geometries) ).iter(geometries.iteritems()): zone_results = extract_by_geometry( [to_dict(geometry)], bounds=pg.total_bounds(geometry) ) if zone_results is None: continue index.append(huc12) results.append(zone_results) cols = ["shape_mask", "urban"] + URBAN_YEARS df = pd.DataFrame(results, index=index)[cols] df = df.reset_index().rename(columns={"index": "id"}).round() df.columns = [str(c) for c in df.columns] df.to_feather(results_filename)
def summarize_raster_by_geometry(geometries, extract_func, outfilename, progress_label="", bounds=None, **kwargs): """Summarize values of input dataset by geometry and writes results to a feather file, with one column for shape_mask and one for each raster value. Parameters ---------- geometries : Series of pygeos geometries, indexed by HUC12 / marine block extract_func : function that extracts results for each geometry outfilename : str progress_label : str """ if bounds is not None: # select only those areas that overlap input area tree = pg.STRtree(geometries) ix = tree.query(pg.box(*bounds)) geometries = geometries.iloc[ix].copy() if not len(geometries): return index = [] results = [] for ix, geometry in Bar(progress_label, max=len(geometries)).iter(geometries.iteritems()): zone_results = extract_func([to_dict(geometry)], bounds=pg.total_bounds(geometry), **kwargs) if zone_results is None: continue index.append(ix) results.append(zone_results) if not len(results): return df = pd.DataFrame(results, index=index) results = df[["shape_mask"]].copy() results.index.name = "id" avg_cols = [c for c in df.columns if c.endswith("_avg")] # each column is an array of counts for each for col in df.columns.difference(["shape_mask"] + avg_cols): s = df[col].apply(pd.Series).fillna(0) s.columns = [f"{col}_{c}" for c in s.columns] results = results.join(s) if len(avg_cols) > 0: results = results.join(df[avg_cols]).round() results.reset_index().to_feather(outfilename)
def __init__(self, geometry, crs, name): """Initialize a custom area from a pygeos geometry. Parameters ---------- geometry : pygeos Geometry crs : pyproj CRS object name : string name of custom area """ self.geometry = to_crs(geometry, crs, DATA_CRS) self.bounds = pg.total_bounds(self.geometry) # wrap geometry as a dict for rasterio self.shapes = np.asarray([to_dict(self.geometry[0])]) self.name = name
def summarize_by_huc12(geometries): """Summarize by HUC12 Parameters ---------- geometries : Series of pygeos geometries, indexed by HUC12 id """ # find the indexes of the geometries that overlap with SLR bounds; these are the only # ones that need to be analyzed for SLR impacts slr_bounds = gp.read_feather(slr_bounds_filename).geometry tree = pg.STRtree(geometries) ix = tree.query(slr_bounds.geometry.values.data[0], predicate="intersects") geometries = geometries.iloc[ix].copy() if not len(geometries): return results = [] index = [] for huc12, geometry in Bar( "Calculating SLR counts for HUC12", max=len(geometries) ).iter(geometries.iteritems()): zone_results = extract_by_geometry( [to_dict(geometry)], bounds=pg.total_bounds(geometry) ) if zone_results is None: continue index.append(huc12) results.append(zone_results) df = pd.DataFrame(results, index=index) # reorder columns df = df[["shape_mask"] + list(df.columns.difference(["shape_mask"]))] # extract only areas that actually had SLR pixels df = df[df[df.columns[1:]].sum(axis=1) > 0] df.columns = [str(c) for c in df.columns] df = df.reset_index().rename(columns={"index": "id"}).round() df.to_feather(results_filename)
def summarize_blueprint_by_geometry(geometries, outfilename, marine=False): counts = [] means = [] index = [] for ix, geometry in Bar( "Calculating overlap with Blueprint, Corridors, and Indicators", max=len(geometries), ).iter(geometries.iteritems()): zone_results = extract_by_geometry( [to_dict(geometry)], bounds=pg.total_bounds(geometry), marine=marine, zonal_means=True, ) if zone_results is None: continue index.append(ix) counts.append(zone_results["counts"]) means.append(zone_results["means"]) count_df = pd.DataFrame(counts, index=index) mean_df = pd.DataFrame(means, index=index).round() mean_df.columns = [f"{c}_avg" for c in mean_df.columns] results = count_df[["shape_mask"]].copy() results.index.name = "id" ### Export the Blueprint, corridors, and indicators # each column is an array of counts for each for col in count_df.columns.difference(["shape_mask"]): s = count_df[col].apply(pd.Series).fillna(0) s.columns = [f"{col}_{c}" for c in s.columns] results = results.join(s) results = results.join(mean_df) results.reset_index().to_feather(outfilename)
async def get_aoi_map_image(geometry, center, zoom, width, height): """Create a rendered map image of the area of interest. Parameters ---------- geometry : pygeos Geometry object (singular) center : [longitude, latitude] zoom : float width : int map width height : int map height Returns ------- Image object """ style = deepcopy(STYLE) style["sources"]["aoi"]["data"] = to_dict(geometry) params = { "style": style, "center": center, "zoom": zoom, "width": width, "height": height, } try: map = await render_mbgl_map(params) except Exception as ex: return None, f"Error generating aoi image ({type(ex)}): {ex}" return map, None
async def get_locator_map_image(longitude, latitude, bounds, geometry=None): """ Create a rendered locator map image. If the bounds cover a large area, `geometry` will be rendered if available, otherwise a box covering the bounds will be rendered. Otherwise, a representative point will be displayed on the map. Parameters ---------- latitude : float latitude of area of interest marker longitude : float longitude of area of interest marker bounds : list-like of xmin, ymin, xmax, ymax bounds of geometry to locate on map geometry : pygeos.Geometry, optional (default: None) If present, will be used to render the area of interest if the bounds are very large. Returns ------- Image object """ style = deepcopy(LOCATOR_STYLE) xmin, ymin, xmax, ymax = bounds # If boundary is a large extent (more than 0.5 degree on edge) # and has big enough area then render geometry or a box instead of a point # NOTE: some multipart features cover large extent and small area, so these # drop out if we do not use area threshold. if xmax - ymax >= 0.5 or ymax - ymin >= 0.5: if geometry and pg.area(geometry) > 0.1: geometry = to_dict(geometry) else: geometry = ({ "type": "Polygon", "coordinates": [[ [xmin, ymin], [xmin, ymax], [xmax, ymax], [xmax, ymin], [xmin, ymin], ]], }, ) style["sources"]["feature"] = {"type": "geojson", "data": geometry} else: style["sources"]["marker"] = { "type": "geojson", "data": { "type": "Point", "coordinates": [longitude, latitude] }, } params = { "style": style, "center": CENTER, "zoom": ZOOM, "width": WIDTH, "height": HEIGHT, } try: map = await render_mbgl_map(params) except Exception as ex: return None, f"Error generating locator image ({type(ex)}): {ex}" return map, None
inputs.reset_index().rename(columns={ "index": "value" }).set_index("inputs"), on="inputs", ) write_dataframe(df, bnd_dir / "input_areas.fgb") df.to_feather(out_dir / "boundaries/input_areas.feather") # Rasterize to match the blueprint df = pd.DataFrame(df[["geometry", "value"]].copy()) df.geometry = df.geometry.values.data # convert to pairs of GeoJSON , value shapes = df.apply(lambda row: (to_dict(row.geometry), row.value), axis=1) print("Rasterizing inputs...") with rasterio.open(blueprint_filename) as src: data = rasterize(shapes.values, src.shape, transform=src.transform, dtype="uint8", fill=255) profile = src.profile outfilename = out_dir / "input_areas.tif" with rasterio.open(outfilename, "w", **profile) as out: out.write(data, 1)
huc12["acres"] = (pg.area(huc12.geometry.values.data) * M2_ACRES).round().astype("uint") # for those that touch the edge of the region, drop any that are not >= 50% in # raster input area. We are not able to use polygon intersection because it # takes too long. tree = pg.STRtree(huc12.geometry.values.data) ix = tree.query(bnd, predicate="contains") edge_df = huc12.loc[~huc12.id.isin(huc12.iloc[ix].id)].copy() geometries = pd.Series(edge_df.geometry.values.data, index=edge_df.id) drop_ids = [] for id, geometry in Bar( "Calculating HUC12 overlap with input area", max=len(geometries) ).iter(geometries.iteritems()): percent_overlap = calculate_percent_overlap( input_area_mask, [to_dict(geometry)], bounds=pg.total_bounds(geometry) ) if percent_overlap < 50: drop_ids.append(id) print(f"Dropping {len(drop_ids)} HUC12s that do not sufficiently overlap input areas") huc12 = huc12.loc[~huc12.id.isin(drop_ids)].copy() # extract geographic bounds huc12_wgs84 = huc12.to_crs(GEO_CRS) huc12 = huc12.join(huc12_wgs84.bounds) # Save in EPSG:5070 for analysis huc12.to_feather(analysis_dir / "huc12.feather") write_dataframe(huc12, bnd_dir / "huc12.gpkg")