class Cell(object): """ """ offstyle = {"fill_opacity": 0, "color": "white", "weight": 0.75} onstyle = {"fill_opacity": 0.4, "color": "lightgreen", "weight": 1} def __init__(self, feat): """Inits with id,lat,lon; makes request string, map point.""" self.feat = feat self.shape = shape(feat["geometry"]) self.prop = feat["properties"] self.feat["properties"]["style"] = { "fill_opacity": 0.1, "opacity": 0.1, "color": "white", "weight": 0.75 } self.id = self.prop["grid_id"] self.level = self.prop["grid_level"] self.layer = GeoJSON(data=self.feat, hover_style={ "weight": 1, "color": "white", "fillColor": "white", "fillOpacity": 0.3 }) self.layer.on_click(self.toggle) self.on = False def toggle(self, **kwargs): """Routine for when a cell is toggled on.""" self.on = False if self.on else True
def __init__(self, feat): """Inits with id,lat,lon; makes request string, map point.""" self.feat = feat self.shape = shape(feat["geometry"]) self.prop = feat["properties"] self.feat["properties"]["style"] = { "fill_opacity": 0.1, "opacity": 0.1, "color": "white", "weight": 0.75 } self.id = self.prop["grid_id"] self.level = self.prop["grid_level"] self.layer = GeoJSON(data=self.feat, hover_style={ "weight": 1, "color": "white", "fillColor": "white", "fillOpacity": 0.3 }) self.layer.on_click(self.toggle) self.on = False
def prepare_map(dc, m): dc.rectangle = {'shapeOptions': {'color': '#FF0000'}} dc.marker = { "shapeOptions": { "fillColor": "#fca45d", "color": "#fca45d", "fillOpacity": 1.0 } } dc.polyline = {} dc.polygon = {} dc.circlemarker = {} # Create a group of layers and add it to the Map group = LayerGroup() m.add_layer(group) # given Africa: N: 38.25, S: -36.25, E: 53.25, W: -19.25 africa = GeoJSON(data={ 'type': 'Feature', 'properties': { 'name': "Africa", 'style': { 'color': '#0000FF', 'clickable': True } }, 'geometry': { 'type': 'Polygon', 'coordinates': [[[-19, 38], [53, 38], [53, -36], [-19, -36]]] } }, hover_style={'fillColor': '03449e'}) group.add_layer(africa) # given Colombia: N: 13.75, S: -5.25, E: -62.75, W: -83.25 colombia = GeoJSON(data={ 'type': 'Feature', 'properties': { 'name': "Colombia", 'style': { 'color': '#0000FF', 'clickable': True } }, 'geometry': { 'type': 'Polygon', 'coordinates': [[[-83, 14], [-63, 14], [-63, -5], [-83, -5]]] } }, hover_style={'fillColor': '03449e'}) group.add_layer(colombia)
def map_shapefile(gdf, colormap=mpl.cm.YlOrRd, weight=2, default_zoom=13): def n_colors(n, colormap=colormap): data = np.linspace(0.0, 1.0, n) c = [mpl.colors.rgb2hex(d[0:3]) for d in colormap(data)] return c def data_to_colors(data, colormap=colormap): c = [ mpl.colors.rgb2hex(d[0:3]) for d in colormap(mpl.colors.Normalize()(data)) ] return c def click_handler(event=None, id=None, properties=None, type=None, coordinates=None): try: datasetID = properties['time'] print(datasetID) except: pass # Convert to WGS 84 and geojson format gdf_wgs84 = gdf.to_crs(epsg=4326) data = gdf_wgs84.__geo_interface__ # For each feature in dataset, append colour values n_features = len(data['features']) colors = n_colors(n_features) for feature, color in zip(data['features'], colors): feature['properties']['style'] = { 'color': color, 'weight': weight, 'fillColor': color, 'fillOpacity': 1.0 } # Get centroid to focus map on lon, lat = gdf_wgs84.unary_union.centroid.coords.xy # Plot map and add geojson layers m = Map(center=(lat[0], lon[0]), zoom=default_zoom, basemap=basemaps.Esri.WorldImagery, layout=dict(width='800px', height='600px')) feature_layer = GeoJSON(data=data) feature_layer.on_click(click_handler) m.add_layer(feature_layer) return m
def force(south, north, west, east): """Force a rectangle onto the map. Arguments: south: float north: float west: float east: float Returns: None """ # check for values if all([cardinal is not None for cardinal in [south, north, west, east]]): # construct coordinates coordinates = [[[west, south], [west, north], [east, north], [east, south], [west, south]]] # construct geo_json geo_json = {'type': 'Feature'} geo_json['properties'] = { 'style': chart.controls[-1].rectangle['shapeOptions'] } geo_json['geometry'] = {'type': 'Polygon', 'coordinates': coordinates} # add rectangle to chart chart.add_layer(GeoJSON(data=geo_json)) return None
def _load_shapes(self, widget, event, data): # get the data from the selected file gdf, column = self.load_shape.read_data() gdf = gdf.filter(items=[column, "geometry"]) # add them to the map for i, row in gdf.iterrows(): # transform the data into a feature feat = { "type": "Feature", "properties": { "style": {} }, "geometry": row.geometry.__geo_interface__, } self._add_geom(feat, row[column]) # display a tmp geometry before validation data = json.loads(gdf.to_json()) style = { **cp.aoi_style, "color": sc.info, "fillColor": sc.info, "opacity": 0.5, "weight": 2, } layer = GeoJSON(data=data, style=style, name="tmp") self.m.add_layer(layer) return
def _save_features(self): """save the features as layers on the map""" # remove any sub aoi layer layers_2_keep = [ "CartoDB.DarkMatter", "restoration layer", self.aoi_model.name ] [ self.m.remove_layer(l) for l in self.m.layers if l.name not in layers_2_keep ] # save the drawn features draw_features = self.draw_features # remove the shapes from the dc # as a side effect the draw_features member will be emptied self.m.dc.clear() # reset the draw_features # I'm sure the the AOI folder exists because the recipe was already saved there self.draw_features = draw_features features_file = (cp.result_dir / self.aoi_model.name / f"features_{self.question_model.recipe_name}.geojson") with features_file.open("w") as f: json.dump(draw_features, f) # set up the colors using the tab10 matplotlib colormap self.colors = [ to_hex(plt.cm.tab10(i)) for i in range(len(self.draw_features["features"])) ] # create a layer for each aoi for feat, color in zip(self.draw_features["features"], self.colors): name = feat["properties"]["name"] style = {**cp.aoi_style, "color": color, "fillColor": color} hover_style = {**style, "fillOpacity": 0.4, "weight": 2} layer = GeoJSON(data=feat, style=style, hover_style=hover_style, name=name) layer.on_hover(self._display_name) self.m.add_layer(layer) return self
def show_restrictions(b): polygons = './vehiclerestrictions_wgs.json' with open(polygons) as f: polygons_json = json.load(f) global geojson geojson = GeoJSON(data=polygons_json) m.add_layer(geojson)
def add_geojson(*args, **kwargs): # ugly workaround to call without data=aoi if 'data' not in kwargs: kwargs['data'] = args[0] args2 = [i for i in args[1:-1]] else: args2 = args r = GeoJSON(*args2, **kwargs) return m.add_layer(r)
def show_geojson(self, *args): data = json.loads( list( self.file_upload.value.values())[0]['content'].decode('ascii')) self.remove_marker() self.remove_geojson() self.geojson = GeoJSON( data=data, style={'color': 'green'}) #, 'opacity': 1})#, 'fillOpacity':0.1}) self.m.add_layer(self.geojson) self.marker_or_geojson = 'GeoJSON'
def _handle_draw(self, action, geo_json): self.clear() leaflet_map.remove_layer(leaflet_map.layers[1]) geometry_layer = GeoJSON(data=geo_json['geometry']) leaflet_map.add_layer(geometry_layer) roi_shape = shape(leaflet_map.layers[1].data) roi_area.value = roi_shape.wkt roi_validation.value = html_element( 'h3', att=dict(style=f'color:green'), value='Region of Interest defined.')
def filter_layer(df, name, start, end): if start is None and end is None: return df[['timestamp', 'geometry']].to_json() filtered_frame = df[df.date.between(start, end)][['timestamp', 'geometry']].to_json() geo_layer = GeoJSON( name=name, data=json.loads(filtered_frame), style={ 'opacity': 0.5, 'dashArray': '9', 'fillOpacity': 0.1, 'weight': 0.5 } ) return geo_layer
def show_map(selected_stats, year): control = WidgetControl(widget=districtbox, position='topright', min_width = 250, max_width=500) # load selected stats into choro_data_all choro_data_all, unit = choro_data_complete[selected_stats], units[selected_stats] # for geo plot extract chosen year and assign to choro_data choro_data = choro_data_all[choro_data_all['year']==year] choro_data = dict(choro_data.drop(columns=['year', 'name']).to_dict('split')['data']) # initialize bar chart with Frankfurt vs Offenbach update_figure('06412', selected_stats, choro_data_all, year) update_figure('06413', selected_stats, choro_data_all, year) # initialize districtbox loading_name, loading_values = id_to_name['06413'], choro_data['06413'] districtbox.value = f'<center><p><b>{loading_name}</b>:</p> {loading_values:g} {unit} {norm_unit}</center>' # set y-axis label fig.update_layout(yaxis_title=f'{stat_dict[selected_stats]} [{unit} {norm_unit}]', yaxis={'range':[0,max(choro_data_all[selected_stats])]}) # define chropleth layer for basic geo plotting layer = Choropleth(geo_data=geo_data,choro_data=choro_data,colormap=cm, style={'fillOpacity': 0.65, 'dashArray': '0, 0', 'weight':1}) # define GeoJSON layer for click and hover event interactions geo_json = GeoJSON(data=geo_data, style={'opacity': 0, 'dashArray': '9', 'fillOpacity': .0, 'weight': 1}, hover_style={'color': 'blue', 'dashArray': '0', 'fillOpacity': 0.7}) # on hover, the districtbox is updated to show properties of the hovered district def update_districtbox(feature, **kwargs): feature['value'] = choro_data[feature['id']] districtbox.value = f'<center><p><b>{id_to_name[feature["id"]]}</b>:</p> {feature["value"]:g} {unit} {norm_unit}</center>' # this function is called upon a click events and triggers figure update with the arguments passed from the map def update_fig_on_click(feature, **kwags): update_figure(feature['id'], selected_stats, choro_data_all, year) geo_json.on_hover(update_districtbox) geo_json.on_click(update_fig_on_click) # add layers and controls; set layout parameters m = Map(basemap=basemaps.OpenStreetMap.Mapnik, center=(50.5,9), zoom=8) m.add_layer(layer) m.add_layer(geo_json) m.add_control(control) m.layout.width = '40%' m.layout.height = '700px' # custom made legend using min/max normalization min_value, max_value = min(choro_data.values()), max(choro_data.values()) legend = LegendControl( {f"{min_value:g} {unit} {norm_unit}": cm(0), #hier f"{min_value+0.5*(max_value-min_value):g} {unit} {norm_unit}": cm(.5), f"{max_value:g} {unit} {norm_unit}": cm(1)}, name= f"{stat_dict[selected_stats]} ({year})", position="bottomleft") m.add_control(legend) return HBox([m, fig], layout=Layout(width='85%'))
def addS2Tiles(inMap): with open('./Shapefiles/Europe.geojson') as f: data = json.load(f) for feature in data['features']: feature['properties']['style'] = { 'color': 'grey', 'weight': 1, 'fillColor': None, 'fillOpacity': 0.1 } s2_tiles_layer = GeoJSON(data=data,name='S2_tiles') inMap.map.add_layer(s2_tiles_layer) return
def areaCommand(x): if x: for l in map.layers[1:]: map.remove_layer(l) cursor.execute("SELECT ST_AsGeoJSON(i.geom) FROM incidents i JOIN areacommand acmd ON ST_Intersects(acmd.geom, i.geom) WHERE acmd.name like'{}' and date >= NOW() - interval '10 day';".format(x)) c=cursor.fetchall() for x in c: layer=json.loads(x[0]) layergeojson=GeoJSON(data=layer) map.add_layer(layergeojson) return c else: pass
def rasters_on_map(rasters_list, out_dir, overlay_names_list, geojson_data=None): """ displays a raster on a ipyleaflet map :param rasters_list: rasters to display (rasterio image) :param out_dir: path to the output directory (preview writing) :param overlay_names_list: name of the overlays for the map """ # - get bounding box raster = rasters_list[0] epsg4326 = {'init': 'EPSG:4326'} bounds = transform_bounds(raster.crs, epsg4326, *raster.bounds) center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2] # - get centered map m = Map(center=(center[-1], center[0]), zoom=10) # - plot quicklook for raster, overlay_name in zip(rasters_list, overlay_names_list): bounds = transform_bounds(raster.crs, epsg4326, *raster.bounds) quicklook_url = os.path.join( out_dir, "PREVIEW_{}.JPG".format(datetime.datetime.now())) write_quicklook(raster, quicklook_url) quicklook = ImageOverlay(url=quicklook_url, bounds=((bounds[1], bounds[0]), (bounds[3], bounds[2])), name=overlay_name) m.add_layer(quicklook) m.add_control(LayersControl()) m.add_control(FullScreenControl()) # - add geojson data if geojson_data is not None: geo_json = GeoJSON(data=geojson_data, style={ 'color': 'green', 'opacity': 1, 'weight': 1.9, 'dashArray': '9', 'fillOpacity': 0.1 }) m.add_layer(geo_json) # - add draw control dc = DrawControl() m.add_control(dc) return m, dc
def get_ipy(wards, wardcentres, const, constcentres, x="ratio"): from ipyleaflet import Map, Marker, GeoJSON, basemaps, GeoData, LayersControl from pymapbox.utils import geojson m = Map() m.center = [52.1917, -1.7083] m.zoom = 10 m.scroll_wheel_zoom = True x, cats, colorset, wards = get_cats(x, wards) # create style columns wards["fillcolor"] = wards[x].map(dict(zip(cats, colorset))) wards = wards[["geometry", "wardname", "fillcolor"]] wardcentres = wardcentres[["wardname", "geometry"]] def wardstyle(row): return dict(fillColor=row["properties"]["fillcolor"], fillOpacity=1) layer = GeoJSON( name="constituencies", data=geojson(const), style=dict(fillColor="#00000000", weight=3), ) m.add_layer(layer) layer = GeoJSON( name="wards", data=geojson(wards), style_callback=wardstyle, style=dict(weight=1), ) m.add_layer(layer) m.add_control(LayersControl()) return m
def generate_route_map(pathToGeojson, zoomLevel=11): with open(pathToGeojson, "r") as f: data = json.load(f) ctrLon, ctrLat = np.mean(np.array( data['features'][0]['geometry']['coordinates']), axis=0) url = "http://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png" provider = TileLayer(url=url, opacity=1) center = [ctrLat, ctrLon] m = Map(default_tiles=provider, center=center, zoom=zoomLevel) m.layout = Layout(width='100%', height='800px') trueRouteCoords, resampledCoords, gpsRouteCoords, \ displacementLines, gpsMatchCoords = data['features'] g = GeoJSON(data=FeatureCollection([trueRouteCoords, gpsMatchCoords])) m.add_layer(g) for coords in resampledCoords['geometry']['coordinates']: cm = Circle(location=coords[::-1], radius=10, weight=1, color='#ff0000', opacity=1.0, fill_opacity=0.4, fill_color='#ff0000') m.add_layer(cm) for coords in gpsRouteCoords['geometry']['coordinates']: cm = Circle(location=coords[::-1], radius=10, weight=1, color='#0000ff', opacity=1.0, fill_opacity=0.4, fill_color='#0000ff') m.add_layer(cm) g = GeoJSON(data=displacementLines) m.add_layer(g) return m
def theDate(x): if x: for l in map.layers[1:]: map.remove_layer(l) nohyphen=str(x).replace("-","") d=datetime.datetime.strptime(nohyphen,'%Y%m%d').date() cursor.execute("SELECT ST_AsGeoJSON(geom) from incidents where date = '{}' ".format(str(d))) c=cursor.fetchall() for x in c: layer=json.loads(x[0]) layergeojson=GeoJSON(data=layer) map.add_layer(layergeojson) return len(c) else: pass
def get_ipygeojson(self): """return a ipygeojson layer ready to be displayed on the map""" if self.gdf is None: raise Exception("Impossible to load layer without he json data") # create a GeoJSON object ipygeojson = GeoJSON( data=self.gdf.__geo_interface__, style=cp.alert_style, hover_style={ **cp.alert_style, "weight": 5 }, name=cm.map.layer.alerts, ) return ipygeojson
def wrapper(*args, **kwargs): legend_file = BytesIO() value = func(*args, **kwargs) overwrite_parameters(other_options, kwargs) overwrite_parameters(plot_options, kwargs) if isinstance(value, xr.Dataset): if "varin" not in kwargs_dec: print("Error") geojson_str = return_geojson(value[kwargs_dec.get("varin")], plot_options, legend_file, **other_options) name = kwargs_dec.get("varin") else: name = value.name geojson_str = return_geojson(value, plot_options, legend_file, **other_options) geojson_data = json.loads(geojson_str) apply_style(geojson_data["features"]) #if "country_file" in kwargs_dec: # crop_country(geojson_data,country_file=kwargs_dec.get("country_file")) geojson_layer = GeoJSON(data=geojson_data, name=name) map_out = Map(zoom=6, center=(float(value.latitude.mean().values), float(value.longitude.mean().values))) map_out.add_layer(geojson_layer) if kwargs_dec.get("borders", False): if "country_file" in kwargs_dec: bord = Borders(fname=kwargs_dec.get("country_file"), color="black") map_out.add_layer(bord) if plot_options["save_colorbar"]: legend_file.seek(0) legend = widgets.Image(layout=widgets.Layout(height="430px")) legend.value = legend_file.read() widg_out = widgets.HBox([map_out, legend]) else: widg_out = map_out return widg_out
def _handle_roi_map_button_clicked(*args, **kwargs): try: geom = loads(roi_area.value) if type(geom) is not Polygon: _update_roi_status_for_error( 'User-provided Region of Interest is not of type Polygon.') return geojson_feature = geojson.Feature(geometry=geom, properties={}) draw_control.clear() leaflet_map.remove_layer(leaflet_map.layers[1]) geometry_layer = GeoJSON(data=geojson_feature['geometry']) leaflet_map.add_layer(geometry_layer) center_lon, center_lat = geom.centroid.coords.xy leaflet_map.center = [center_lat[0], center_lon[0]] @debug_view.capture(clear_output=False) def _adjust_zoom_level(event): if event['name'] == 'bounds' and leaflet_map.zoom < 18: southwest, northeast = leaflet_map.bounds map_bounds = Polygon([(southwest[1], southwest[0]), (southwest[1], northeast[0]), (northeast[1], northeast[0]), (northeast[1], southwest[0]), (southwest[1], southwest[0])]) if map_bounds.covers(geom): leaflet_map.zoom = leaflet_map.zoom + 1 elif leaflet_map.zoom > 1: leaflet_map.zoom = leaflet_map.zoom - 1 leaflet_map.unobserve(_adjust_zoom_level) leaflet_map.zoom = 1 leaflet_map.observe(_adjust_zoom_level) if geom.is_valid: roi_validation.value = html_element( 'h3', att=dict(style=f'color:green'), value='Region of Interest defined.') else: roi_validation.value = html_element( 'h3', att=dict(style=f'color:orange'), value='User-provided Region of Interest is invalid.') except WKTReadingError: _update_roi_status_for_error( 'User-provided Region of Interest cannot be read.')
def show_locations_on_map(locations): m = Map(center=(40.7205, -73.9060), zoom=11) for locs in locations: parse_loc = { "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": {}, "geometry": { "type": "Point", "coordinates": [locs[0], locs[1]] } }] } geo_json = GeoJSON(data=parse_loc) m.add_layer(geo_json) return m
def layer_from_element(element, style_function=None): """Return Leaflet layer from shape. Parameters ---------- element : telluric.vectors.GeoVector, telluric.features.GeoFeature, telluric.collections.BaseCollection Data to plot. """ # This import is here to avoid cyclic references from telluric.collections import BaseCollection if isinstance(element, BaseCollection): styled_element = element.map(lambda feat: style_element(feat, style_function)) else: styled_element = style_element(element, style_function) return GeoJSON(data=mapping(styled_element), name='GeoJSON')
def get_ipygeojson(self): """ Converts current geopandas object into ipyleaflet GeoJSON Return: (GeoJSON): the geojson layer of the aoi gdf """ if type(self.gdf) == type(None): raise Exception( "You must set the gdf before converting it into GeoJSON") data = json.loads(self.gdf.to_json()) self.ipygeojson = GeoJSON(data=data, style=AOI_STYLE, name='aoi', attribution='SEPA(c)') return self.ipygeojson
def rid(self, action, geo_json): """Rid the map of previous polygons, keeping only the one drawn. Arguments: self: self action: action geo_json: dict Returns: None """ # clear polygons and rectanges from draw control chart.controls[-1].clear_polygons() chart.controls[-1].clear_rectangles() # remove all previous layers chart.layers = chart.layers[:1] # add polygon to chart chart.add_layer(GeoJSON(data=geo_json)) return None
for footprint, color in zip(footprints, colors): # create the leaflet object feat = { 'geometry': footprint, "properties": { 'style': { 'color': color, 'fillColor': color, 'fillOpacity': 0.2, 'weight': 1 } }, 'type': u"Feature" } # convert to geojson gjson = GeoJSON(data=feat) # add it our map m.add_layer(gjson) # now we will draw our original AOI on top feat = { 'geometry': myAOI, "properties": { 'style': { 'color': "#FFFFFF", 'fillColor': "#FFFFFF", 'fillOpacity': 0.5, 'weight': 1 } }, 'type': u"Feature" }
def handle_draw(self, action, geo_json): nonlocal polygon_number # Execute behaviour based on what the user draws if geo_json['geometry']['type'] == 'Polygon': # Convert the drawn geometry to pixel coordinates geom_selectedarea_flat, geom_selectedarea = transform_from_wgs_poly( geo_json['geometry'], EPSGa=3577 # hard-coded to be same as case-study data ) geom_envelope = geom_selectedarea_flat.GetEnvelope() minX, maxX, minY, maxY = geom_envelope # Insert dc.load for dem and dlcdnsw x_range = (minX, maxX) y_range = (minY, maxY) inputcrs = "EPSG:3577" dem_res = (-5, 5) # dlcd_res is unused as the same res must be used when loading # multiple products (to support the cross count process). The # smallest cell size is used as this will provide the greatest # accuracy. dlcd_res = (-100, 100) # unused slope_cat = get_slope_raster(dc, x_range, y_range, inputcrs, dem_res, geom_selectedarea) dlcd = get_dlcd_raster(dc, x_range, y_range, inputcrs, dem_res, geom_selectedarea) stacked = xr.merge([dlcd, slope_cat]) cross_counts = unique_counts(stacked) slope_cat_count = slope_cat.to_dataframe( ).slope_category.value_counts().rename('counts').to_frame() slope_cat_count.index.name = 'index' slope_cat_table = pd.read_csv('slope_cat.csv') slope_coverage = pd.merge(slope_cat_count, slope_cat_table, how="left", left_on=['index'], right_on=['id']) # Compute the total number of pixels in the masked data set pix_dlcd = dlcd.count().compute().item() # Convert dlcd to pandas and get value counts for each class pd_dlcd = dlcd.to_dataframe() pd_dlcd_classcount = pd_dlcd.dlcd.value_counts().reset_index( name='counts') # Convert slope_cat to pandas and get value counts for each category # pd_slope_cat_count = slope_cat.to_dataframe().band1.value_counts() # Load DLCD land cover look-up table dlcd_lookup = pd.read_csv("dlcd.csv") # Join dlcd counts against landcover look up table pd_dlcd_coverage = pd.merge(pd_dlcd_classcount, dlcd_lookup, how="left", left_on=['index'], right_on=['id']) # Format the counts table to keep necessary items pd_dlcd_coverage['area(km^2)'] = pd_dlcd_coverage['counts'] * ( dem_res[1] / 1000.)**2 pd_dlcd_coverage['percentage_area'] = pd_dlcd_coverage[ 'counts'] / pd_dlcd_coverage['counts'].sum() * 100 pd_dlcd_output = pd_dlcd_coverage[[ 'Name', 'area(km^2)', 'percentage_area' ]] # manipulate cross counts into format suitable for presentation as # a table pd_cross_counts = cross_counts.to_dataframe() pd_cross_counts.sort_values(by='count', ascending=False, inplace=True) # join DLCD lookup table for DLCD class names pd_cross_counts = pd.merge(pd_cross_counts, dlcd_lookup, how='left', left_on=['dlcd'], right_on=['id']) pd_cross_counts = pd_cross_counts.rename(columns={ 'dlcd': 'dlcd_id', 'Name': 'DLCD' }) # join slope category definition table for slope class names pd_cross_counts = pd.merge(pd_cross_counts, slope_cat_table, how='left', left_on=['slope_category'], right_on=['id']) pd_cross_counts['slope'] = (pd_cross_counts[[ 'label', 'range' ]].apply(lambda x: '{} {}'.format(x[0], x[1]), axis=1)) # Format the counts table to keep necessary items pd_cross_counts['area(km^2)'] = (pd_cross_counts['count'] * (dem_res[1] / 1000.)**2) pd_cross_counts['percentage_area'] = ( pd_cross_counts['count'] / pd_cross_counts['count'].sum() * 100) pd_cross_counts_output = (pd_cross_counts[[ 'DLCD', 'slope', 'area(km^2)', 'percentage_area' ]]) # display(pd_cross_counts) colour = colour_list[polygon_number % len(colour_list)] # Add a layer to the map to make the most recently drawn polygon # the same colour as the line on the plot studyarea_map.add_layer( GeoJSON(data=geo_json, style={ 'color': colour, 'opacity': 1, 'weight': 4.5, 'fillOpacity': 0.0 })) # Make the DLDC Summary plot ax.clear() pd_dlcd_output.plot.bar(x='Name', y='percentage_area', rot=45, ax=ax, legend=False, color=colour) ax.set_xlabel("Land Cover Class") ax.set_ylabel("Percentage Coverage Of Polygon") # refresh display fig_display.clear_output( wait=True) # wait=True reduces flicker effect with fig_display: display(fig) info.clear_output(wait=True) # wait=True reduces flicker effect with info: # use to_string function to avoid truncation of results print(pd_cross_counts_output.to_string()) # Iterate the polygon number before drawing another polygon polygon_number = polygon_number + 1 else: info.clear_output(wait=True) with info: print("Plot status: this drawing tool is not currently " "supported. Please use the polygon tool.")
def run_miningrehab_app(ds): """ Plots an interactive map of the mining case-study area and allows the user to draw polygons. This returns plots of the fractional cover value of bare soil, green vegetation and brown vegetation in the polygon area. Last modified: January 2020 inputs ds - data set containing masked Fractional Cover data from Landsat 8 """ # Suppress warnings warnings.filterwarnings("ignore") # Update plotting functionality through rcParams mpl.rcParams.update({"figure.autolayout": True}) # Define the bounding box that will be overlayed on the interactive map # The bounds are hard-coded to match those from the loaded data geom_obj = { "type": "Feature", "properties": { "style": { "stroke": True, "color": "red", "weight": 4, "opacity": 0.8, "fill": True, "fillColor": False, "fillOpacity": 0, "showArea": True, "clickable": True, } }, "geometry": { "type": "Polygon", "coordinates": [[ [116.630731, -34.434517], [116.630731, -34.426512], [116.648123, -34.426512], [116.648123, -34.434517], [116.630731, -34.434517], ]], }, } # Create a map geometry from the geom_obj dictionary # center specifies where the background map view should focus on # zoom specifies how zoomed in the background map should be loadeddata_geometry = ogr.CreateGeometryFromJson(str(geom_obj["geometry"])) loadeddata_center = [ loadeddata_geometry.Centroid().GetY(), loadeddata_geometry.Centroid().GetX(), ] loadeddata_zoom = 15 # define the study area map studyarea_map = Map( layout=widgets.Layout(width="480px", height="600px"), center=loadeddata_center, zoom=loadeddata_zoom, basemap=basemaps.Esri.WorldImagery, ) # define the drawing controls studyarea_drawctrl = DrawControl( polygon={"shapeOptions": { "fillOpacity": 0 }}, marker={}, circle={}, circlemarker={}, polyline={}, ) # add drawing controls and data bound geometry to the map studyarea_map.add_control(studyarea_drawctrl) studyarea_map.add_layer(GeoJSON(data=geom_obj)) # Index to count drawn polygons polygon_number = 0 # Define widgets to interact with instruction = widgets.Output(layout={"border": "1px solid black"}) with instruction: print("Draw a polygon within the red box to view plots of " "the fractional cover values of bare, green and " "non-green cover for the area over time.") info = widgets.Output(layout={"border": "1px solid black"}) with info: print("Plot status:") fig_display = widgets.Output(layout=widgets.Layout( width="50%" # proportion of horizontal space taken by plot )) with fig_display: plt.ioff() fig, ax = plt.subplots(3, 1, figsize=(9, 9)) for axis in ax: axis.set_ylim([0, 1]) colour_list = plt.rcParams["axes.prop_cycle"].by_key()["color"] # Function to execute each time something is drawn on the map def handle_draw(self, action, geo_json): nonlocal polygon_number # info.clear_output(wait=True) # wait=True reduces flicker effect # with info: # print("Plot status: entered handle draw") # Execute behaviour based on what the user draws if geo_json["geometry"]["type"] == "Polygon": # Convert the drawn geometry to pixel coordinates geom_selectedarea = transform_geojson_wgs_to_epsg( geo_json, EPSG=3577 # hard-coded to be same as case-study data ) # Construct a mask to only select pixels within the drawn polygon mask = rasterio.features.geometry_mask( [geom_selectedarea for geoms in [geom_selectedarea]], out_shape=ds.geobox.shape, transform=ds.geobox.affine, all_touched=False, invert=True, ) masked_ds = ds.where(mask) masked_ds_mean = masked_ds.mean(dim=["x", "y"], skipna=True) colour = colour_list[polygon_number % len(colour_list)] # Add a layer to the map to make the most recently drawn polygon # the same colour as the line on the plot studyarea_map.add_layer( GeoJSON( data=geo_json, style={ "color": colour, "opacity": 1, "weight": 4.5, "fillOpacity": 0.0, }, )) # Add Fractional cover plots to app masked_ds_mean.BS.interpolate_na( dim="time", method="nearest").plot.line("-", ax=ax[0]) masked_ds_mean.PV.interpolate_na( dim="time", method="nearest").plot.line("-", ax=ax[1]) masked_ds_mean.NPV.interpolate_na( dim="time", method="nearest").plot.line("-", ax=ax[2]) # reset titles back to custom ax[0].set_ylabel("Bare cover") ax[1].set_ylabel("Green cover") ax[2].set_ylabel("Non-green cover") # refresh display fig_display.clear_output( wait=True) # wait=True reduces flicker effect with fig_display: display(fig) # Update plot info info.clear_output(wait=True) # wait=True reduces flicker effect with info: print("Plot status: polygon added to plot") # Iterate the polygon number before drawing another polygon polygon_number = polygon_number + 1 else: info.clear_output(wait=True) with info: print("Plot status: this drawing tool is not currently " "supported. Please use the polygon tool.") # call to say activate handle_draw function on draw studyarea_drawctrl.on_draw(handle_draw) with fig_display: # TODO: update with user friendly something display(widgets.HTML("")) # Construct UI: # +-----------------------+ # | instruction | # +-----------+-----------+ # | map | plot | # | | | # +-----------+-----------+ # | info | # +-----------------------+ ui = widgets.VBox( [instruction, widgets.HBox([studyarea_map, fig_display]), info]) display(ui)
def handle_draw(self, action, geo_json): nonlocal polygon_number # info.clear_output(wait=True) # wait=True reduces flicker effect # with info: # print("Plot status: entered handle draw") # Execute behaviour based on what the user draws if geo_json["geometry"]["type"] == "Polygon": # Convert the drawn geometry to pixel coordinates geom_selectedarea = transform_geojson_wgs_to_epsg( geo_json, EPSG=3577 # hard-coded to be same as case-study data ) # Construct a mask to only select pixels within the drawn polygon mask = rasterio.features.geometry_mask( [geom_selectedarea for geoms in [geom_selectedarea]], out_shape=ds.geobox.shape, transform=ds.geobox.affine, all_touched=False, invert=True, ) masked_ds = ds.where(mask) masked_ds_mean = masked_ds.mean(dim=["x", "y"], skipna=True) colour = colour_list[polygon_number % len(colour_list)] # Add a layer to the map to make the most recently drawn polygon # the same colour as the line on the plot studyarea_map.add_layer( GeoJSON( data=geo_json, style={ "color": colour, "opacity": 1, "weight": 4.5, "fillOpacity": 0.0, }, )) # Add Fractional cover plots to app masked_ds_mean.BS.interpolate_na( dim="time", method="nearest").plot.line("-", ax=ax[0]) masked_ds_mean.PV.interpolate_na( dim="time", method="nearest").plot.line("-", ax=ax[1]) masked_ds_mean.NPV.interpolate_na( dim="time", method="nearest").plot.line("-", ax=ax[2]) # reset titles back to custom ax[0].set_ylabel("Bare cover") ax[1].set_ylabel("Green cover") ax[2].set_ylabel("Non-green cover") # refresh display fig_display.clear_output( wait=True) # wait=True reduces flicker effect with fig_display: display(fig) # Update plot info info.clear_output(wait=True) # wait=True reduces flicker effect with info: print("Plot status: polygon added to plot") # Iterate the polygon number before drawing another polygon polygon_number = polygon_number + 1 else: info.clear_output(wait=True) with info: print("Plot status: this drawing tool is not currently " "supported. Please use the polygon tool.")