def __init__(self, mapdata): '''initialize a SubwayMap object Args: mapdata (SubwayMapData): container-class for stations and lines dataframes with implemented observer pattern. This is necessary for data binding of the view to the viewmodel. ''' Stream.__init__(self) #create an initial map stations, lines = mapdata.stationsdf, mapdata.linesdf self.pipe = Pipe(data=[]) self.subway_map = gv.Path(lines, vdims=['color']).opts( projection=crs.LambertConformal(), height=800, width=800, color='color') * gv.DynamicMap(self.callback, streams=[self.pipe]) self.pipe.send(stations) #bind changes in the stationsdf to pipe.send mapdata.bind_to_stationsdf(self.pipe.send) self.mapdata = mapdata
def __init__(self, stations): self.default_departure_station = 'Zürich HB' self.default_arrival_station = 'Zürich, Auzelg' self.default_departure_id = id_from_name( stations, self.default_departure_station) self.default_arrival_id = id_from_name(stations, self.default_arrival_station) zurich = stations.loc[self.default_departure_id] self.stations = stations self.station_names = list(self.stations['station_name']) self.default_arrival_time = datetime.datetime.fromisoformat( '2019-05-06 12:30:00') self.default_min_success_probability = 0 self.tiles = gv.tile_sources.CartoLight() self.unselected_hover = HoverTool(tooltips=[('', '@station_name')]) self.departure_hover = HoverTool(tooltips=[('Departure Station', '@station_name')]) self.arrival_hover = HoverTool(tooltips=[('Arrival Station', '@station_name')]) self.departure_station_widget = pn.widgets.AutocompleteInput( name='Departure Station', value=self.default_departure_station, options=self.station_names) self.arrival_station_widget = pn.widgets.AutocompleteInput( name='Arrival Station', value=self.default_arrival_station, options=self.station_names) self.arrival_time_widget = pn.widgets.DatetimeInput( name='Arrival Time', value=self.default_arrival_time) self.min_probability_widget = pn.widgets.FloatSlider( name='Minium Success Probability', start=0.0, end=1.0, value=self.default_min_success_probability) self.departure_tap_stream = hv.streams.SingleTap( x=stations.loc[self.default_departure_id, 'lat'], y=stations.loc[self.default_departure_id, 'lon']).rename(x='departure_lat', y='departure_lon') self.arrival_tap_stream = hv.streams.DoubleTap( x=stations.loc[self.default_arrival_id, 'lat'], y=stations.loc[self.default_arrival_id, 'lon']).rename(x='arrival_lat', y='arrival_lon') self.departure_tap_stream.add_subscriber( lambda departure_lat, departure_lon: set_to_closest_station( self.stations, self.departure_station_widget, departure_lat, departure_lon)) self.arrival_tap_stream.add_subscriber( lambda arrival_lat, arrival_lon: set_to_closest_station( self.stations, self.arrival_station_widget, arrival_lat, arrival_lon)) self.departure_station_widget.link( self.departure_tap_stream, callbacks={'value': self._update_stream}) self.arrival_station_widget.link( self.arrival_tap_stream, callbacks={'value': self._update_stream}) self.station_map = gv.DynamicMap( self._station_map(), streams=[self.departure_tap_stream, self.arrival_tap_stream]) self.widgets = pn.Column(self.departure_station_widget, self.arrival_station_widget, self.arrival_time_widget, self.min_probability_widget) self.interface = pn.Row(pn.Spacer(height=600), pn.Column(self.widgets, max_width=400), self.station_map)
def view(self): return (gv.DynamicMap(self.callback) * self.boxes).options(active_tools=['wheel_zoom'])
def get_timeseries(source, data, dataset, ymin, ymax, fmt): ''' get time series plots ''' #initialize timeseries_data if data.init is False: #find the maximum side length x, y = dataset['SCHISM_hgrid_node_x'].data, dataset[ 'SCHISM_hgrid_node_y'].data e1, e2, e3 = dataset['SCHISM_hgrid_face_nodes'].data.T s1 = abs((x[e1] - x[e2]) + 1j * (y[e1] - y[e2])).max() s2 = abs((x[e2] - x[e3]) + 1j * (y[e2] - y[e3])).max() s3 = abs((x[e3] - x[e1]) + 1j * (y[e3] - y[e1])).max() #save data data.sx, data.sy, data.x0, data.y0 = x, y, x.mean(), y.mean() data.mdist = np.max([s1, s2, s3]) data.time = dataset['time'].data data.xys = [] data.elev = [] data.curve = [] data.init = True def get_plot_point(x, y): if None not in [x, y]: add_remove_pts(x, y, data, dataset, fmt) if ((x is None) or (y is None)) and len(data.xys) == 0: xys = [(data.x0, data.y0)] hpoint = gv.Points(xys).opts(show_legend=False, visible=False) htext = gv.HoloMap({ i: gv.Text(*xy, '{}'.format(i + 1)).opts(show_legend=False, visible=False) for i, xy in enumerate(xys) }).overlay() else: xys = data.xys hpoint = gv.Points(xys).opts(color='r', size=3, show_legend=False) htext = gv.HoloMap({ i: gv.Text(*xy, '{}'.format(i + 1)).opts(show_legend=False, color='k', fontsize=3) for i, xy in enumerate(xys) }).overlay() return hpoint * htext def get_plot_curve(x, y): mdist, mdata = extract_timeseries(x, y, data.sx, data.sy, dataset) if mdist > data.mdist: mdata = mdata * np.nan hdynamic = hv.Curve((data.time, mdata)).opts(color='k', line_width=2, line_dash='dotted') hcurve = hv.HoloMap({ 'dynamic': hdynamic, **{(i + 1): k for i, k in enumerate(data.curve)} }).overlay() return hcurve hpoint = gv.DynamicMap(get_plot_point, streams=[DoubleTap(source=source, transient=True)]) hcurve = gv.DynamicMap(get_plot_curve, streams=[ PointerXY(x=data.x0, y=data.y0, source=source) ]).opts(height=400, legend_cols=len(data.xys) + 1, legend_position='top', ylim=(float(ymin), float(ymax)), responsive=True, align='end', active_tools=["pan", "wheel_zoom"]) return hpoint, hcurve
def map_view(self): return self.tiles * gv.DynamicMap(self.update_map_values)
def view(self): warnings.warn( 'WMTS class is deprecated and will be removed in a future release.' ) return gv.DynamicMap(self.element)
def explore( cyclonic=None, anticyclonic=None, hover_info=["segment", "track"], tiles=None, background_func=None, dynamic=False, date_widget="calendar", ): """explore NetworkObservations of cyclonic and/or anticyclonic :param cyclonic: cyclonic dataset, defaults to None :type cyclonic: :py:class:`~py_eddy_tracker.observations.network.NetworkObservations`, optional :param anticyclonic: anticyclonic dataset, defaults to None :type anticyclonic: :py:class:`~py_eddy_tracker.observations.network.NetworkObservations`, optional :param hover_info: list of data to show on hover, defaults to ["segment", "track"] :type hover_info: list, optional :param tiles: if specified, plot data over web tiles. like :py:class:`geoviews.source_tiles.EsriImagery`, defaults to None :type tiles: :py:class:`geoviews.element.geo.WMTS`, optional :param background_func: python function, takes as param a date and return a `xr.Dataset`, defaults to None :type background_func: callable, optional :param dynamic: if True, use :py:class:`~holoviews.operation.datashader.regrid` to delay plot of `background_func`, defaults to False :type dynamic: bool, optional :param date_widget: widget for date. if not calendar, it will be a radio, defaults to "calendar" :type date_widget: str, optional :return: bokeh plot .. note:: the param `background_func` should return a :py:class :`~xarray.Dataset` with variables "lon", "lat" and "Grid_0001" for the data. The name of data should be on attribute "long_name" of Grid_0001, and "units" if wanted example of function : .. code-block:: python def date2dataset(date): fichier = f"/tmp/global_{date.strftime('%Y%m%d')}.nc" with xr.open_dataset(fichier) as ds: ds = ds.rename({"longitude": "lon", "latitude": "lat", "adt": "Grid_0001"}) ds.Grid_0001.attrs["long_name"] = "adt ds['lon'] = np.where(ds.lon > 180, ds.lon - 360, ds.lon) ds = ds.sortby("lon").load() return ds """ if cyclonic is None and anticyclonic is None: raise NotImplementedError("please specify cyclonic or anticyclonic") # if cyclonic or anticyclonic is not declared, only use one datasets = [(d, color) for d, color in [(cyclonic, "deepskyblue"), (anticyclonic, "red")] if d is not None] # compute every date with data datasets_unique = [np.unique(d.time) for d, _ in datasets] unique = np.unique(np.concatenate(datasets_unique)) if date_widget == "calendar": now = datetime.date(1950, 1, 1) + datetime.timedelta(int(unique[0])) pn_date = pn.widgets.DatePicker( name="date", value=now, width=200, enabled_dates=(np.datetime64("1950-01-01") + unique.astype("timedelta64[D]")).tolist(), ) widget_date = pn_date else: pn_date = pn.widgets.DiscretePlayer(name="date", options=unique.tolist(), value=unique[0]) @pn.depends(pn_date_value=pn_date.param.value) def plot_date(pn_date_value): return pn.pane.HTML( f"<h1>date = {np.datetime64('1950-01-01')+np.timedelta64(pn_date_value)}</h1>" ) widget_date = pn.Row(pn_date, plot_date) @pn.depends(pn_date_value=pn_date.param.value) 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"]) if background_func is not None: @pn.depends(pn_date_value=pn_date.param.value) def update_fond(pn_date_value): if isinstance(pn_date_value, datetime.date): # si on a choisi le widget DatePicker date = pn_date_value else: date = datetime.datetime( 1950, 1, 1) + datetime.timedelta(days=int(pn_date_value)) ds = background_func(date) info = ds.Grid_0001.units if hasattr(ds.Grid_0001, "units") else "" return gv.Image( (ds.lon, ds.lat, ds.Grid_0001.T), kdims=["lon", "lat"], vdims=[ds.Grid_0001.long_name], ).opts(responsive=True, tools=["hover"], clabel=info) if dynamic: fond = regrid(hv.DynamicMap(update_fond)).opts(height=500, responsive=True) else: fond = hv.DynamicMap(update_fond).opts(responsive=True) if tiles is not None: visu = gv.Overlay([ tiles, fond, gv.DynamicMap(update_all), ]).collate() else: visu = gv.Overlay([ fond, gv.feature.land(scale="50m").opts( responsive=True, line_color="black", fill_color="darkgray"), # , data_aspect=0.7), gv.DynamicMap(update_all), ]).collate() return pn.Column(widget_date, visu, sizing_mode="stretch_both", min_height=600, min_width=400) else: if tiles is not None: visu = gv.Overlay([tiles, gv.DynamicMap(update_all)]).collate() else: visu = gv.Overlay([ gv.feature.land(scale="50m").opts( responsive=True, line_color="black", fill_color="darkgray", data_aspect=1, ), gv.DynamicMap(update_all), ]).collate() return pn.Column(widget_date, visu, sizing_mode="stretch_both", min_height=600, min_width=400)
def view(self): return (gv.DynamicMap(self.callback) * self.boxes)
def update_tiles(self): return self.tiles * gv.DynamicMap(self.update_poly)