def getGeoElementFromData(self, data,vdims=None): arrayGeomType = data.geom_type.unique() for geomType in arrayGeomType: data = data[data['geometry'].apply(lambda x: x.geom_type == geomType)] if geomType in POINT: geomNdOverlay = gv.Points(data, vdims=vdims,crs=ccrs.GOOGLE_MERCATOR, group=POINT[0]) elif geomType in LINESTRING: data['geometry'] = data.simplify(SOFT_SIMPLIFY, True) geomNdOverlay = gv.Path(data, vdims=vdims,crs=ccrs.GOOGLE_MERCATOR, group=LINESTRING[0]) elif geomType in MULTILINESTRING: data['geometry'] = data.simplify(SOFT_SIMPLIFY, True) geomNdOverlay = gv.Path(data, vdims=vdims, crs=ccrs.GOOGLE_MERCATOR, group=MULTILINESTRING[0]) elif geomType in POLYGONE: data['geometry'] = data.simplify(STRONG_SIMPLIFY, True) geomNdOverlay = gv.Polygons(data, vdims=vdims, crs=ccrs.GOOGLE_MERCATOR, group=POLYGONE[0]) elif geomType in MULTIPOLYGONE: data['geometry'] = data.simplify(STRONG_SIMPLIFY, True) geomNdOverlay = gv.Polygons(data, vdims=vdims, crs=ccrs.GOOGLE_MERCATOR, group=MULTIPOLYGONE[0]) elif geomType in GEOMETRYCOLLECTION: data = data.explode() geomNdOverlay = GeomUtil.getGeoElementFromData(self,data=data) else: return None return geomNdOverlay.opts(tools=['hover', 'tap'])
def generate(self, bounds_polygon: sg.Polygon, raster_shape: Tuple[int, int], from_cache: bool = False, **kwargs) -> \ Tuple[Geometry, np.ndarray, gpd.GeoDataFrame]: import colorcet import datashader as ds from holoviews.operation.datashader import rasterize import geoviews as gv from copy import deepcopy bounds = bounds_polygon.bounds polys_df = self.query_osm_polygons(bounds_polygon) bounded_census_wards = self._census_wards.cx[bounds[1]:bounds[3], bounds[0]:bounds[2]] # Find landuse polygons intersecting/within census wards and merge left census_df = gpd.overlay(polys_df, bounded_census_wards, how='intersection') # Estimate the population of landuse polygons from the density of the census ward they are within # EPSG:4326 is *not* an equal area projection so would give gibberish areas # Project geometries to an equidistant/equal areq projection census_df['population'] = census_df['density'] * census_df[ 'geometry'].to_crs('EPSG:3395').area census_df['ln_density'] = np.log(census_df['density']) # Construct the GeoViews Polygons gv_polys = gv.Polygons(census_df, kdims=['Longitude', 'Latitude'], vdims=['population', 'ln_density', 'density']) \ .opts(color='ln_density', cmap=colorcet.CET_L18, alpha=0.8, colorbar=True, colorbar_opts={'title': 'Log Population Density [ln(people/km^2)]'}, show_legend=False, line_color='ln_density') if self.buffer_dist > 0: buffered_df = deepcopy(census_df) buffered_df.geometry = buffered_df.to_crs('EPSG:27700') \ .buffer(self.buffer_dist).to_crs('EPSG:4326') buffered_polys = gv.Polygons(buffered_df, kdims=['Longitude', 'Latitude'], vdims=['name', 'density']) raster = rasterize(buffered_polys, aggregator=ds.max('density'), width=raster_shape[0], height=raster_shape[1], x_range=(bounds[1], bounds[3]), y_range=(bounds[0], bounds[2]), dynamic=False) else: raster = rasterize(gv_polys, aggregator=ds.max('density'), width=raster_shape[0], height=raster_shape[1], x_range=(bounds[1], bounds[3]), y_range=(bounds[0], bounds[2]), dynamic=False) raster_grid = np.copy( list(raster.data.data_vars.items())[0][1].data.astype(np.float)) return gv_polys, raster_grid, gpd.GeoDataFrame(census_df)
def createOverlay(self,**kwargs): traceP = kwargs.get("traceParam") data = traceP.data label = kwargs.get("label") self.param.variable_taille_point.objects = traceP.listeEntier self.param.variable_couleur.objects = data.columns if not self.variable_taille_point: self.silently = True self.variable_taille_point = traceP.listeEntier[0] if not self.variable_couleur: self.silently = True self.variable_couleur = traceP.listeEntier[0] vdims = traceP.listeEntier kdims = list(data.columns)[5:-1] size = self.variable_taille_point arrayGeomType = data.geom_type.unique() for geomType in arrayGeomType: data = data[data['geometry'].apply(lambda x: x.geom_type == geomType)] if geomType in POINT: geomNdOverlay = gv.Points(data, vdims=vdims, crs=crs.GOOGLE_MERCATOR, label=label, group=POINT[0], id=traceP.trace.id).options(size=dim(size).norm()*45) ## Convertir les autres géometry en barycentre elif geomType in POLYGONE: data['geometry'] = data.simplify(STRONG_SIMPLIFY,True) geomNdOverlay = gv.Polygons(data,vdims=vdims, crs=crs.GOOGLE_MERCATOR,label=label, group=POLYGONE[0]) elif geomType in LINESTRING: data['geometry'] = data.simplify(SOFT_SIMPLIFY, True) geomNdOverlay = gv.Path(data, crs=crs.GOOGLE_MERCATOR,label=label, group=LINESTRING[0]) elif geomType in MULTILINESTRING: data['geometry'] = data.simplify(SOFT_SIMPLIFY, True) geomNdOverlay = gv.Path(data, crs=crs.GOOGLE_MERCATOR, label=label, group=MULTILINESTRING[0]) elif geomType in MULTIPOLYGONE: data['geometry'] = data.simplify(STRONG_SIMPLIFY,True) geomNdOverlay = gv.Polygons(data, vdims=vdims, crs=crs.GOOGLE_MERCATOR,label=label, group=MULTIPOLYGONE[0]) else: geomNdOverlay = None overlay = hv.Overlay([geomNdOverlay.opts(tools=['hover', 'tap'],color=self.variable_couleur, cmap='Category20')]) return overlay
def geodframe_to_geoviews(obj, geo_type, crs): """Converts the geometry in a GeoPandas GeoDataFrame into a list formatted for Geoviews only accepts one data type per object Parameters ---------- obj - geopandas geodataframe with geometry column of shapely objects geo_type - string - type of geometry to extract from gdf 'point' or 'polygon'. Only one geometry type can be extracted from a gdf crs - cartopy.crs projection """ if geo_type is 'point': data = [item.coords for item in obj.geometry] element = gv.Points(data, crs=crs) for col in obj.columns: if col is not 'geometry': element = element.add_dimension(col, 0, obj[col], True) elif geo_type is 'polygon': data = [item.coords for item in obj.geometry.boundary] element = gv.Polygons(data, crs=crs) for col in obj.columns: if col is not 'geometry': element = element.add_dimension(col, 0, obj[col], True) else: raise IOError('Shapely geometry type {} not supported'.format( obj.geometryType())) return element
def compare_sp_and_osm_for_tile(rgb_fn, gdf_sp, gdf_osm): """ Creates an interactive gui to explore each tile resulting rgb_fn: a uncropped rgb file or cropped tile gdf_sp and gdf_osm: ideally covers equal or larger bounds than the bounds of rgb_fn """ ds = rio.open(rgb_fn) img = reshape_as_image(ds.read()) bounds = ds.bounds polys_sp = filter_gdf_to_polys_within_bounds(gdf_sp, bounds) polys_osm = filter_gdf_to_polys_within_bounds(gdf_osm, bounds) overlay = (gv.Image(img, bounds=bounds) * gv.Polygons(polys_sp, group='sp', datatype=['list']) * gv.Polygons(polys_osm, group='osm')) return overlay
def warning(color="orange"): warning = gv.Polygons([]).opts(line_color=color, line_width=3, fill_color=color, fill_alpha=0.2) pen = FreehandDraw(source=warning) return pen, warning
class Mapview(param.Parameterized): opts = dict(width=1200, height=750, xaxis=None, yaxis=None, show_grid=False) tiles = gv.tile_sources.CartoEco().apply.opts(**opts) extents = param.Parameter(default=(-168, -60, 168, 83), precedence=-1) tiles.extents = extents.default box_polygons = gv.Polygons([]).opts(fill_alpha=0.1) box_colours = ["red", "blue", "green", "orange", "purple"] box_stream = hv.streams.BoxEdit(source=box_polygons, num_objects=5, styles={"fill_color": box_colours}) template_df = pd.DataFrame({ "x_meters": [], "y_meters": [] }, columns=["x_meters", "y_meters"]) dfstream = hv.streams.Buffer(template_df, index=False, length=10000) points = hv.DynamicMap(hv.Points, streams=[dfstream]).opts(size=5, color="green", fill_alpha=0.3, line_alpha=0.4) def show_map(self): return self.tiles * self.box_polygons * self.points
def grid(self, tiles=False, **kwargs): x = kwargs.get("x", self._obj.SCHISM_hgrid_node_x[:].values) y = kwargs.get("y", self._obj.SCHISM_hgrid_node_y[:].values) tes = kwargs.get("tri3", self._obj.SCHISM_hgrid_face_nodes.values) width = kwargs.get("width", 800) height = kwargs.get("height", 600) opts.defaults(opts.WMTS(width=width, height=height)) tile = gv.WMTS("https://b.tile.openstreetmap.org/{Z}/{X}/{Y}.png") nodes = pd.DataFrame({"longitude": x, "latitude": y}) points = gv.operation.project_points(gv.Points(nodes)) if tes.shape[1] == 3: elems = pd.DataFrame(tes, columns=["a", "b", "c"]) trimesh = gv.TriMesh((elems, points)).edgepaths if tiles: return tile * datashade( trimesh, precompute=True, cmap=["black"]) else: return datashade(trimesh, precompute=True, cmap=["green"]).opts(width=width, height=height) else: # there are quads elems = pd.DataFrame(tes, columns=["a", "b", "c", "d"]) quads = elems.loc[~elems.d.isna()].copy() quads = quads.reset_index(drop=True) ap = nodes.loc[quads.a, ["longitude", "latitude"]] bp = nodes.loc[quads.b, ["longitude", "latitude"]] cp = nodes.loc[quads.c, ["longitude", "latitude"]] dp = nodes.loc[quads.d, ["longitude", "latitude"]] quads["ap"] = ap.values.tolist() quads["bp"] = bp.values.tolist() quads["cp"] = cp.values.tolist() quads["dp"] = dp.values.tolist() q = gv.Polygons([ quads.loc[i, ["ap", "bp", "cp", "dp"]].tolist() for i in range(quads.shape[0]) ]).options(fill_alpha=0, line_color="black") triangles = elems.loc[elems.d.isna()] trimesh = gv.TriMesh((triangles, points)).edgepaths g1 = datashade(trimesh, precompute=True, cmap=["black"]) if tiles: return tile * g1 * q else: return g1 * q
def generate(self, bounds_polygon: sg.Polygon, raster_shape: Tuple[int, int], from_cache: bool = False, **kwargs) -> \ Tuple[Geometry, np.ndarray, gpd.GeoDataFrame]: import geoviews as gv from holoviews.operation.datashader import rasterize bounds = bounds_polygon.bounds bounded_df = self.dataframe.cx[bounds[1]:bounds[3], bounds[0]:bounds[2]] polys = gv.Polygons(bounded_df).opts(style={ 'alpha': 0.8, 'color': self._colour }) raster = rasterize(polys, width=raster_shape[0], height=raster_shape[1], x_range=(bounds[1], bounds[3]), y_range=(bounds[0], bounds[2]), dynamic=False) raster_grid = np.copy( list(raster.data.data_vars.items())[0][1].data.astype(np.float)) if self.blocking: raster_grid[raster_grid != 0] = -1 return polys, raster_grid, gpd.GeoDataFrame(self.dataframe)
def plot(self): commune_plot = gv.Polygons( ifrag_cont_df_merged[ifrag_cont_df_merged.nom_com == self.localisation], vdims=vdims, ) return self.tiles * commune_plot.opts( color=indice, width=600, height=600, fill_alpha=0.5)
def __init__(self, poly_data=[], **params): super(SelectRegionPanel, self).__init__(**params) self.boxes = gv.Polygons(poly_data).options( fill_alpha=0.5, color='grey', line_color='white', line_width=2, width=self.width, height=self.height ) if not self.boxes: self.boxes = self.boxes.options(global_extent=True) self.box_stream = BoxEdit(source=self.boxes, num_objects=1)
def _plot_shapes_one(self, data_gpd: GeoDataFrame, timestamp: dt.datetime, crs: CRS): """Plots shapes for one timestamp from geopandas GeoDataFrame :param data_gpd: data to plot :param timestamp: timestamp to plot data for :param crs: in which crs is the data to plot """ out = data_gpd.loc[data_gpd[self.config.timestamp_column] == timestamp] return gv.Polygons(out, crs=ccrs.epsg(int(crs.value)))
def grid(self, tiles=False, **kwargs): x = kwargs.get('x',self._obj.SCHISM_hgrid_node_x[:].values) y = kwargs.get('y',self._obj.SCHISM_hgrid_node_y[:].values) tes = kwargs.get('tri3',self._obj.SCHISM_hgrid_face_nodes.values) width = kwargs.get('width', 800) height = kwargs.get('height', 600) opts.defaults(opts.WMTS(width=width, height=height)) tile = gv.WMTS('https://b.tile.openstreetmap.org/{Z}/{X}/{Y}.png') nodes = pd.DataFrame({'longitude':x,'latitude':y}) points = gv.operation.project_points(gv.Points(nodes)) if tes.shape[1] == 3 : elems = pd.DataFrame(tes, columns=['a', 'b', 'c']) trimesh=gv.TriMesh((elems, points)).edgepaths if tiles: return tile * datashade(trimesh, precompute=True, cmap=['black']) else: return datashade(trimesh, precompute=True, cmap=['green']).opts(width=width,height=height) else: # there are quads elems = pd.DataFrame(tes, columns=['a', 'b', 'c', 'd']) quads = elems.loc[~elems.d.isna()].copy() quads = quads.reset_index(drop=True) ap = nodes.loc[quads.a,['longitude','latitude']] bp = nodes.loc[quads.b,['longitude','latitude']] cp = nodes.loc[quads.c,['longitude','latitude']] dp = nodes.loc[quads.d,['longitude','latitude']] quads['ap'] = ap.values.tolist() quads['bp'] = bp.values.tolist() quads['cp'] = cp.values.tolist() quads['dp'] = dp.values.tolist() q = gv.Polygons([quads.loc[i,['ap','bp','cp','dp']].tolist() for i in range(quads.shape[0])]).options(fill_alpha=0, line_color='black') triangles = elems.loc[elems.d.isna()] trimesh=gv.TriMesh((triangles, points)).edgepaths g1 = datashade(trimesh, precompute=True, cmap=['black']) if tiles : return tile * g1 * q else: return g1 * q
def _plot_vector_timeless(self, eopatch: EOPatch): """Plot vector timeless data""" crs = eopatch.bbox.crs data_gpd = eopatch[self.feature] if crs is CRS.WGS84: crs = CRS.POP_WEB data_gpd = data_gpd.to_crs(crs.pyproj_crs()) return gv.Polygons(data_gpd, crs=ccrs.epsg(crs.epsg), vdims=self.config.vdims)
def plot_shapes_one(self, data_gpd, timestamp, crs): """ Plots shapes for one timestamp from geopandas GeoDataFrame :param data_gpd: data to plot :type data_gpd: geopandas.GeoDataFrame :param timestamp: timestamp to plot data for :type timestamp: datetime :param crs: in which crs is the data to plot :type crs: sentinelhub.crs :return: visualization :rtype: geoviews """ out = data_gpd.loc[data_gpd[self.timestamp_column] == timestamp] return gv.Polygons(out, crs=ccrs.epsg(int(crs.value)))
def plot_vector_timeless(self, feature_name): """ Plot FeatureType.VECTOR_TIMELESS data :param feature_name: name of the eopatch featrue :type feature_name: str :return: visalization :rtype: geoviews """ crs = self.eopatch.bbox.crs data_gpd = self.eopatch[FeatureType.VECTOR_TIMELESS][feature_name] if crs is CRS.WGS84: crs = CRS.POP_WEB data_gpd = data_gpd.to_crs(crs.pyproj_crs()) return gv.Polygons(data_gpd, crs=ccrs.epsg(crs.epsg), vdims=self.vdims)
def bbox_selector(metadata_csv=None, metadata_csv_lon_column='Longitude', metadata_csv_lat_column='Latitude', bounds=(-124.5, 40, -108, 50), basemap_url=None): ''' Draw bounding box (hold shift) on basemap and return vertices. Select bounding box to delete. ''' OpenTopoMap = 'https://tile.opentopomap.org/{Z}/{X}/{Y}.png' OpenStreetMap = 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png' GoogleHybrid = 'https://mt1.google.com/vt/lyrs=y&x={X}&y={Y}&z={Z}' GoogleSatellite = 'https://mt1.google.com/vt/lyrs=s&x={X}&y={Y}&z={Z}' GoogleRoad = 'https://mt1.google.com/vt/lyrs=m&x={X}&y={Y}&z={Z}' GoogleTerrain = 'http://mt0.google.com/vt/lyrs=p&hl=en&x={X}&y={Y}&z={Z}' GoogleTerrainOnly = 'http://mt0.google.com/vt/lyrs=t&hl=en&x={X}&y={Y}&z={Z}' ESRIImagery = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.jpg' Wikimedia = 'https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}@2x.png' print('Toggle Box Edit Tool and hold shift to draw bounding box.') if not basemap_url: url = GoogleSatellite basemap = gv.WMTS(url, extents=bounds).opts(aspect=1, frame_height=500) if metadata_csv: df = pd.read_csv(metadata_csv) df = df.loc[(df[metadata_csv_lat_column] < bounds[3]) & (df[metadata_csv_lat_column] > bounds[1]) & (df[metadata_csv_lon_column] > bounds[0]) & (df[metadata_csv_lon_column] < bounds[2])].reset_index( drop=True) coords = list( zip(df[metadata_csv_lon_column].values, df[metadata_csv_lat_column].values)) points = gv.Points(coords).opts(size=10, frame_height=500) else: points = gv.Points({}).opts(size=10, frame_height=500) box_poly = gv.Polygons([{}]).opts( opts.Polygons(fill_alpha=0, line_color='yellow', line_width=3, selection_fill_color='red')) box_stream = BoxEdit(source=box_poly) return basemap * points * box_poly, box_stream
def update_poly(self): poly = polygons.copy() poly = poly[poly.nom_com == self.localisation] poly.loc[:, "value"] = np.random.rand(len(poly)) * self.some_value vdims = ["value", "value2"] poly.loc[:, "value2"] = np.random.rand(len(poly)) * self.some_value # Hovertool # https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#hovertool # gv.Polygons(d1, vdims=[('pop_est','Population'), ('name', 'Country')]).options( # tools=['hover'], width=800, height=500, projection=crs.Robinson() # ) tooltips = [("value", "@value"), ("value2", "-")] if self.some_value > 50: tooltips = [("value", "@value"), ("value2", "@value2")] TOOLTIPS_HTML = """ <div> """ for val in ["value", "value2"]: TOOLTIPS_HTML += """<div> <span style="font-size: 17px; font-weight: bold;">{parameter} :</span> <span style="red"> @{parameter}</span> </div>""".format(parameter=val) TOOLTIPS_HTML += """ </div> """ hover_custom = HoverTool(tooltips=TOOLTIPS_HTML) maps = gv.Polygons(poly, vdims=vdims).opts( # tools=[hover_custom], tools=[hover_custom], color="value", colorbar=True, toolbar="above", fill_alpha=0.5, width=800, height=800, ) return maps
def save_shapefile(cdsdata, path, template): """ Accepts bokeh ColumnDataSource data and saves it as a shapefile, using an existing template to determine the required schema. """ collection = fiona.open(template) arrays = [ np.column_stack([xs, ys]) for xs, ys in zip(cdsdata['xs'], cdsdata['ys']) ] polys = gv.Polygons(arrays, crs=ccrs.GOOGLE_MERCATOR) projected = gv.operation.project_path(polys, projection=ccrs.PlateCarree()) data = [list(map(tuple, arr)) for arr in projected.split(datatype='array')] shape_data = list(collection.items())[0][1] shape_data['geometry']['coordinates'] = data with fiona.open(path, 'w', collection.driver, collection.schema, collection.crs) as c: c.write(shape_data)
def plot_vector_timeless(self, feature_name): """ Plot FeatureType.VECTOR_TIMELESS data :param feature_name: name of the eopatch featrue :type feature_name: str :return: visalization :rtype: geoviews """ crs = self.eopatch.bbox.crs if crs is CRS.WGS84: crs = CRS.POP_WEB data_gpd = self.eopatch[ FeatureType.VECTOR_TIMELESS][feature_name].to_crs( {'init': 'epsg:{}'.format(crs.value)}) else: data_gpd = self.eopatch[FeatureType.VECTOR_TIMELESS][feature_name] return gv.Polygons(data_gpd, crs=ccrs.epsg(int(crs.value)), vdims=self.vdims)
def generate(self, bounds_polygon: sg.Polygon, raster_shape: Tuple[int, int], from_cache: bool = False, hour: int = 8, resolution: float = 20, **kwargs) -> \ Tuple[Geometry, np.ndarray, gpd.GeoDataFrame]: from holoviews.operation.datashader import rasterize import geoviews as gv import datashader as ds import colorcet relative_variation = self.relative_variations_flat[hour] roads_gdf = self._interpolate_traffic_counts(bounds_polygon) roads_gdf['population_per_hour'] = roads_gdf[ 'population_per_hour'] * relative_variation roads_gdf['population'] = roads_gdf['population_per_hour'] / 3600 roads_gdf['density'] = roads_gdf['population'] / ( roads_gdf.geometry.area * 1e-6) # km^2 ln_mask = roads_gdf['density'] > 0 roads_gdf.loc[ln_mask, 'ln_density'] = np.log(roads_gdf.loc[ln_mask, 'density']) roads_gdf['ln_density'].fillna(0, inplace=True) roads_gdf = roads_gdf.set_crs('EPSG:27700').to_crs('EPSG:4326') points = gv.Polygons( roads_gdf, kdims=['Longitude', 'Latitude'], vdims=['population_per_hour', 'ln_density', 'density']).opts( # colorbar=True, cmap=colorcet.CET_L18, color='ln_density', line_color='ln_density') bounds = bounds_polygon.bounds raster = rasterize(points, aggregator=ds.mean('density'), width=raster_shape[0], height=raster_shape[1], x_range=(bounds[1], bounds[3]), y_range=(bounds[0], bounds[2]), dynamic=False) raster_grid = np.copy( list(raster.data.data_vars.items())[0][1].data.astype(np.float)) return points, raster_grid, gpd.GeoDataFrame(roads_gdf)
def generate(self, bounds_polygon: sg.Polygon, raster_shape: Tuple[int, int], from_cache: bool = False, **kwargs) -> \ Tuple[Geometry, np.ndarray, gpd.GeoDataFrame]: import geoviews as gv from holoviews.operation.datashader import rasterize bounds = bounds_polygon.bounds polys_df = self.query_osm_polygons(bounds_polygon) if polys_df.empty: return None if self.buffer_dist > 0: polys_df.geometry = polys_df.to_crs('EPSG:27700').buffer(self.buffer_dist).to_crs('EPSG:4326') polys = gv.Polygons(polys_df).opts(alpha=0.8, color=self._colour, line_color=self._colour) raster = rasterize(polys, width=raster_shape[0], height=raster_shape[1], x_range=(bounds[1], bounds[3]), y_range=(bounds[0], bounds[2]), dynamic=False) raster_grid = np.copy(list(raster.data.data_vars.items())[0][1].data.astype(np.float)) if self.blocking: raster_grid[raster_grid != 0] = -1 else: raster_grid = None return polys, raster_grid, polys_df
def update_all(pn_date_value): if isinstance(pn_date_value, datetime.date): # si on a choisi le widget DatePicker pn_date_value = (pn_date_value - datetime.date(1950, 1, 1)).days hv_polygons = [] hv_exterieur = [] for (d, color), d_unique in zip(datasets, datasets_unique): mask = d.time == pn_date_value sub_lon = ((d.contour_lon_s[mask] + 180) % 360) - 180 sub_lat = d.contour_lat_s[mask] sub_data = np.moveaxis(np.array([sub_lon, sub_lat]), [0], [2]) polygons = [Polygon(poly) for poly in sub_data] dct_data = {name: d[name][mask] for name in hover_info} dct_data["geometry"] = polygons gpd = geopandas.GeoDataFrame(dct_data) opts_common = dict(responsive=True) hv_polygons.append( gv.Polygons(gpd, vdims=hover_info).opts(line_color=color, **opts_common)) sub_lon = ((d.contour_lon_e[mask] + 180) % 360) - 180 sub_lat = d.contour_lat_e[mask] _lon = flatten_line_matrix(sub_lon) _lat = flatten_line_matrix(sub_lat) hv_exterieur.append( gv.Path([np.array([_lon, _lat]).T]).opts(color=color, alpha=0.70, line_dash="dashed", **opts_common)) return gv.Overlay([*hv_polygons, *hv_exterieur]).opts(responsive=True, tools=["hover"])
def add_obstacles(bounds, obstacles, raster_shape): import geoviews as gv from holoviews.operation.datashader import rasterize import geopandas as gpd if type(obstacles) is gpd.GeoDataFrame: df = obstacles else: df = gpd.GeoDataFrame({'geometry': obstacles}).set_crs('EPSG:4326') bounded_df = df.cx[bounds[1]:bounds[3], bounds[0]:bounds[2]] bounded_df['z'] = np.inf polys = gv.Polygons(bounded_df, vdims=['z']) raster = rasterize(polys, width=raster_shape[1], height=raster_shape[0], x_range=(bounds[1], bounds[3]), y_range=(bounds[0], bounds[2]), dynamic=False) raster_grid = np.flipud( np.copy( list(raster.data.data_vars.items())[0][1].data.astype(np.float))) return remove_raster_nans(raster_grid)
def get_data(path): """ :return: """ redwood_np = gp.GeoDataFrame.from_file('data/RedwoodNP.geojson') gedi_files = [ g for g in os.listdir(path) if g.startswith('GEDI02_A') and g.endswith('.h5') ] for item in gedi_files: path_to_file = os.path.join(path, item) gedi_l2a = h5py.File(path_to_file, 'r') beam_names = [g for g in gedi_l2a.keys() if g.startswith('BEAM')] latslons = open_sds(gedi_l2a, beam_names[0]) vdims = [] for f in latslons: if f not in ['geometry']: vdims.append(f) gv.Polygons(redwood_np).opts( tools=['hover'], line_color='red', color=None) * point_visual( latslons, vdims=vdims)
def facet_map_interactive( df, cols_to_plot, target, predicted=None, dissolve_on=None, alpha=0.5, autobin=False, n_bins=7, geoid="nis", weight="exp_yr", shp_file=None, figsize=(12, 12), ncols=2, cmap=None, normalize=True, tiles_src=None, ): """ Prepare the geodata to map. Take the dataframe df, which should have a geoid column, and join it to the zipcode mapper and the geometries. The result is illustrated on a (facet) chart. An aggregation step by geoid is performed in order to have a value per unique geoid. If weight is provided, then weighted average, at the geoid level, of the target is computed. The uncertainty (on the weighted average) is computed accordingly to the distribution (gaussian, Poisson or Gamma) and can be illustrated in another panel and the sample weights as well. If dissolve it set to True, the geometries are "dissolved" to go at the upper level, e.g. dissolve counties to go at the state level (a state being larger and containing several counties). :param df: dataframe the data set, predictors + dependent variable :param cols_to_plot: list of str of None the columns to plot with the target (e.g. predicted values or any other columns than target) :param target: str the target name (main column to plot, e.g. observed values/truth) :param predicted: str or pandas data frame or None, default: None if dataframe, should be the same length of df. It could be predicted values, not joined to df :param dissolve_on: str the column name you want to dissolve on. Dissolve means going to an upper geographical level, e.g. from commune to district or to state. (e.g. moving from counties to states) :param alpha: float between 0 and 1, default=0.5 the transparency of the choropleth :param autobin: Bool, default=False autobin (discretized) the illustrated values. If True, the values are binned using percentiles before to be plotted :param n_bins: int, default: 7 number of bin for auto-binning (discretizing the values) :param geoid: str, default: 'INS' the name of the geoid columns, INS refers to the Belgian french name :param weight: None or str column (if any) of sample weights :param shp_file: geopandas frame the shapefile. E.G: load_be_shp() :param figsize: 2-uple, default: (12, 12) figure size, (width, heihgt) :param ncols: int, default: 2 the number of columns in the facet plot, if there are several columns to plot :param cmap: matplotlib cmap or None Please, use ONLY scientific color maps. No jet, no rainbow! If you have any doubt, keep the default e.g. http://www.fabiocrameri.ch/colourmaps.php :param normalize: bool should the other illustrated columns normalized to the target? If True, the vmin and vmax will be the same for all the panels and computed w.r.t the target colum, :param tiles_src: str or None the tile source, should be a string, compatible with one of the geoview tile sources :return: matplotlib figure """ if not isinstance(ncols, int): raise ValueError("'ncols' should be an integer") # load the data to illustrate geo_df = prepare_geo_data( df=df, cols_to_plot=cols_to_plot, target=target, predicted=predicted, dissolve_on=dissolve_on, n_bins=n_bins, geoid=geoid, weight=weight, shp_file=shp_file, autobin=False, ) # if more than 1 column to illustrate, fillna and set # the number of rows in the panel plot if cols_to_plot is not None: geo_df[cols_to_plot] = geo_df[cols_to_plot].fillna(0) else: cols_to_plot = [] if (tiles_src is not None) and (tiles_src not in list( hv.element.tiles.tile_sources.keys())): raise ValueError("`tiles_src` should be a string and one " "of {}".format( list(hv.element.tiles.tile_sources.keys()))) elif (tiles_src is not None) and (tiles_src in list( hv.element.tiles.tile_sources.keys())): tiles = hv.element.tiles.tile_sources[tiles_src]() else: tiles = hv.element.tiles.tile_sources["CartoLight"]() # define colour_map if (cmap is None) and (autobin is False): colour_map = cmr.tropical # Thermal_20.mpl_colormap elif (cmap is None) and (autobin is True): colour_map = cmr.tropical else: colour_map = cmap hv_plot_list = [] # loop over the columns to illustrate for i, col in enumerate([target] + cols_to_plot): plot_opts = dict( tools=["hover"], width=550, height=450, color_index=col, cmap=colour_map, colorbar=True, toolbar="above", xaxis=None, yaxis=None, alpha=alpha, title=col, clipping_colors={"NaN": "white"}, ) if autobin: if normalize: ser, bins = pd.qcut(geo_df[target].fillna(0), q=n_bins, retbins=True, labels=None, precision=2) geo_df[col] = (pd.cut( geo_df[col].fillna(0), bins=bins, labels=np.unique(ser), include_lowest=True, ).apply(lambda x: x.mid).astype(float)) # pd.cut(geo_df[col], q=n_bins, duplicates='drop'). # apply(lambda x: x.mid).astype(float) else: geo_df[col] = (pd.qcut( geo_df[col], q=n_bins, duplicates="drop", precision=2).apply(lambda x: x.mid).astype(float)) # .apply(lambda x: x.mid).astype(float) hv_plot_list.append(tiles * gv.Polygons( geo_df, vdims=[hv.Dimension(col) ], crs=ccrs.GOOGLE_MERCATOR).opts(**plot_opts)) else: if normalize: vmax = np.nanpercentile(geo_df[target].fillna(0), 99) vmin = np.nanpercentile(geo_df[target].fillna(0), 1) else: vmax = np.nanpercentile(geo_df[col].fillna(0), 99) vmin = np.nanpercentile(geo_df[col].fillna(0), 1) hv_plot_list.append(tiles * gv.Polygons( geo_df, vdims=[hv.Dimension(col, range=(vmin, vmax))], crs=ccrs.GOOGLE_MERCATOR, ).opts(**plot_opts)) # Display the figure hvl = (hv.Layout(hv_plot_list).opts( opts.Tiles(width=figsize[0], height=figsize[1])).cols(2)) return hvl
import quest import geoviews as gv import holoviews as hv import panel as pp hv.extension('bokeh') quest_service = 'svc://wmts:seamless_imagery' tile_service_options = quest.api.get_download_options( quest_service, fmt='param')[quest_service] boxes = gv.Polygons([]).options(fill_alpha=0.4, line_width=2) box_stream = hv.streams.BoxEdit(source=boxes, num_objects=1) import param class MyParam(param.Parameterized): alpha = param.Number(0.5, bounds=(0, 1), doc="alpha") myparam = MyParam() def change_basemap(**kw): print("change_basemap: " + str(kw)) url = kw['url'] alpha = kw["alpha"] tiles = gv.WMTS(url).options(global_extent=True, height=400, width=700,
def dmp_callback(*args, **kw): global stream_extract_options global elevation_filled global tiles global points global box_poly global streams global fill_dataset global st_dataset if box_stream.element: xs, ys = box_stream.element.array().T bbox = [xs[0], ys[1], xs[2], ys[0]] else: bbox = [ -90.88870324076336, 32.245105881134, -90.78133198716839, 32.37688211930573 ] print('BOUNDS:', bbox) tiles.extents = tuple(bbox) if box_stream._triggering: print("Box stream is triggering") elements = tiles * box_poly * points elif btn_download_stream._triggering: # import pdb # pdb.set_trace() print("btn_download_stream is triggering") elevation_raster = quest.api.get_seamless_data( service_uri=elevation_service.service, bbox=bbox, collection_name='examples', use_cache=False, # in standalone Bokeh app this should be set False as_open_dataset=False, ) fill_dataset = quest.tools.wbt_fill_depressions( dataset=elevation_raster, )['datasets'][0] fill = quest.api.open_dataset(fill_dataset, with_nodata=True, isel_band=0) elevation_filled = gv.Image(fill, ['x', 'y']).options(alpha=0.8) stream_extract_options = quest.tools.wbt_extract_streams_workflow stream_extract_options.params()['dataset'].precedence = -1 stream_extract_options.dataset = fill_dataset stream_extract_options.set_threshold_bounds() print("Bounds" + str(stream_extract_options.params()["stream_threshold"].bounds)) start, end = stream_extract_options.params()["stream_threshold"].bounds slider_extract.start = start slider_extract.end = end slider_extract.value = stream_extract_options.stream_threshold elements = tiles * elevation_filled * box_poly * points elif btn_run_stream._triggering: print("btn_run_stream is triggering") stream_extract_options.stream_threshold = slider_extract.value st_dataset = stream_extract_options()['datasets'][0] st = quest.api.open_dataset(st_dataset, with_nodata=True, isel_band=0) streams = gv.Image(st, ['x', 'y']).options(cmap='Greens') elements = tiles * box_poly * elevation_filled * streams * points elif btn_delin_stream._triggering: print("btn_delin_stream is triggering") if point_stream.element: original_outlets = [ (x, y) for x, y in zip(*point_stream.element.array().T) ] else: original_outlets = (-90.883981967599979, 32.291221825861946) result = quest.tools.wbt_watershed_delineation_workflow( elevation_dataset=fill_dataset, streams_dataset=st_dataset, snap_distance=0.1, outlets=original_outlets, ) watersheds, snapped_outlets, catalog_entry = result['catalog_entries'] outline = gv.Polygons(watersheds).options(alpha=0.5, color="yellow") original_points = gv.Points(original_outlets).options(color='blue', size=12) snapped_points = gv.Points(snapped_outlets).options(color='green', size=12) elements = tiles * box_poly * original_points * outline * snapped_points * streams * points else: print("someone is triggering") elements = tiles * box_poly * points return elements
# Subject to Bokeh Bug: Tool Bar disappears https://github.com/ioam/holoviews/issues/2784 import quest import geoviews as gv import holoviews as hv from holoviews.streams import (Params, PolyEdit, BoxEdit, PointDraw) # from holoviews.operation.datashader import regrid import panel as pp # import param hv.extension('bokeh') # pp.extension() tiles = gv.tile_sources.StamenTerrain().options(width=950, height=600) tiles.extents = (-125, 25, -65, 50) box_poly = gv.Polygons([]).options(fill_alpha=.2) box_stream = BoxEdit(source=box_poly, num_objects=1, name="boxedit") points = gv.Points([]) point_stream = PointDraw(source=points) elevation_service = quest.util.ServiceSelector( parameter='elevation', default='svc://usgs-ned:1-arc-second') # Explicitly create a panel widget btn_download = pp.widgets.Button(name='Download & Fill', button_type='danger') btn_download_stream = Params(btn_download, ['clicks'], rename={'clicks': 'download_clicks'}) btn_run = pp.widgets.Button(name='Extract', button_type='danger') btn_run_stream = Params(btn_run, ['clicks'], rename={'clicks': 'run_clicks'}) btn_delin = pp.widgets.Button(name='Delinate', button_type='danger')
def update_map_values(self): try: # Selection par localisation # http://holoviews.org/user_guide/Plotting_with_Bokeh.html # https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#custom-tooltip map_info = ["tout_axes", "nom_com"] vdims = ( map_info + [k + "_SCORE" for k in self.selected_indices_level_0] + list(AXES_INDICES.keys()) ) TOOLTIPS_HTML = """<span style="font-size: 22px; font-weight: bold;"> @nom_com</span> <div> """ for k, indicators in AXES_INDICES.items(): display_name = indicators["nom"] vdim_name = k if vdim_name in vdims: TOOLTIPS_HTML += """<div> <span style="font-size: 18px; font-weight: bold;"> {display_name} :</span> <span style="red"> @{vdim_name}</span> </div>""".format( display_name=display_name, vdim_name=vdim_name ) else: TOOLTIPS_HTML += """<div> <span style="font-size: 18px; font-weight: bold;"> {display_name} :</span> <span style="red"> N/A </span> </div>""".format( display_name=display_name, vdim_name=vdim_name ) for indic in indicators: if indic not in ["desc", "nom"]: display_name = CATEGORIES_INDICES[indic] vdim_name = indic + "_SCORE" if vdim_name in vdims: TOOLTIPS_HTML += """<div> <span style="font-size: 12px; "> {display_name} :</span> <span style="red">@{vdim_name}</span> </div>""".format( display_name=display_name, vdim_name=vdim_name ) else: TOOLTIPS_HTML += """<div> <span style="font-size: 12px;"> {display_name} :</span> <span style="red">N/A</span> </div>""".format( display_name=display_name ) TOOLTIPS_HTML += """ </div> """ hover_custom = HoverTool(tooltips=TOOLTIPS_HTML) self.maps = gv.Polygons(self.df_score, vdims=vdims) return self.maps.opts( tools=[hover_custom], color="tout_axes", colorbar=True, toolbar="above", # xaxis=None, # yaxis=None, fill_alpha=0.5, ) except Exception as e: print(e) pass