Using Tap values from map

So I have a map that’s plotting a .csv dataset. Everything with the static_geo map is exactly how I like. It currently uses the np.nan values for Lat and Lon, and the line plot that is displayed below is how I would like it do display for the first time, everytime. However, I would like to click the map, use the Lat and Lon from a click to get a new line plot from the data elvs. Elvs has two dimensions, time and points. I get the closest point in the data by using the two sampled points and comparing those to the elvs dataset. I expect Elvs.sel(points=test_point) to provide with an array of numbers to plot on a line plot - it does that just need it to update with a click on the map.

data = gv.Points(df, [('X_lon', 'Longitude'), ('Y_lat', 'Latitude')],
                     [('AvgRate_ftPerYr', 'Weighted Combo Filtered'),
                      ('AvgRate_Implicit','Implicit Average'),
                      ('Median_ftPerYr','Median'),
                      ('AvgRate_First_Last_ftPerYr','Last - First'),])

data = gv.Dataset(gv.operation.project_points(data))
size_opts = dict(min_height=500, min_width=500, responsive=True)
geo_opts  = dict(size_opts, cmap='RdBu', global_extent=False, logz=False, colorbar=True)
elvs = reach_array.elevations

class ShoalingRateTest(param.Parameterized):
    average = param.ObjectSelector(default='AvgRate_ftPerYr', objects=['AvgRate_ftPerYr'
                                                                        ,'AvgRate_Implicit'
                                                                        ,'Median_ftPerYr'
                                                                        ,'AvgRate_First_Last_ftPerYr'])
    
    limit = max(df['AvgRate_ftPerYr'])
    @param.depends('average')
    def view(self,**kwargs):        
        geo_bg = gv.tile_sources.EsriImagery.options(alpha=0.8, bgcolor='black')
        
        geo_kw = dict(aggregator=ds.mean(self.average), x_sampling=5, y_sampling=5)
        geo_map = gv.Points(data, crs=ccrs.GOOGLE_MERCATOR).redim.range(AvgRate_ftPerYr=(-self.limit, self.limit),
                                                                                         AvgRate_Implicit=(-self.limit,self.limit),
                                                                                         Median_ftPerYr=(-self.limit,self.limit), 
                                                                                         AvgRate_First_Last_ftPerYr=(-self.limit,self.limit))
        
        static_geo = rasterize(geo_map, **geo_kw).options(alpha=1.0, tools=['hover','box_select', 'lasso_select'], active_tools=['box_select'], **geo_opts)
        tap = hv.streams.SingleTap(source=static_geo, x=np.nan, y=np.nan)
        
        test_point = np.argmin((tap.x-lons)**2+(tap.x-lats)**2)
        temp_ts = elvs.sel(points=test_point)       
        temp_line =  temp_ts.hvplot.line(x='time',y='elevations')        


        return (geo_bg*static_geo + temp_line)
        

shoal_test = ShoalingRateTest()
stock_dmap = shoal_test.view


pn.Row(pn.Column(shoal_test.param, parameters=['average']), stock_dmap)

It’s hard to get the syntax right without being able to run that code, plus it’s hard to follow the logic through the methods of that class, but I think you could use something like:

def drilldown(x,y):
   test_point = np.argmin((x-lons)**2+(y-lats)**2)
   temp_ts = elvs.sel(points=test_point)       
   return temp_ts.hvplot.line(x='time',y='elevations', dynamic=False)
   
stock_dmap = DynamicMap(drilldown, streams=[tap])

With DynamicMap being hv.DynamicMap?

Yes

So the result is the same as the original - no updating in the line plot. Is there any way to at least print to see if the lat and lons are being updated?

You can put a print in the drilldown. To go further we’d need a reproducible example here.

So I was able to get a reproducible example. The first click works, but after that the update no longer continues.

import panel as pn
import param
import os, numpy as np, pandas as pd, cartopy.crs as ccrs, bokeh
import holoviews as hv, geoviews as gv, datashader as ds

from colorcet import bmy
from holoviews.util import Dynamic
from holoviews.operation.datashader import rasterize, datashade
from holoviews import dim, opts
import xarray as xr
import numpy as np
from pyproj import Proj, transform
import scipy
from scipy import spatial
import hvplot.xarray
from holoviews import streams

hv.extension('bokeh', width=100)
pn.extension()


lats = np.linspace(28.4,28.5, num=1000)
lons = np.linspace(-93,-92.99, num=100)
elevations = np.random.rand(15, 100000)*10+15

lons = np.concatenate([lons]*1000)
lats = np.repeat(lats,100)

elvs = xr.DataArray(
            data = elevations,
            dims = ('time','points'),
            name='elevations')


d = {'X_lon':lons, 'Y_lat':lats, 'Avg_rates': np.random.rand(100000)*3}

df = pd.DataFrame(d)

data = gv.Points(df, [('X_lon', 'Longitude'), ('Y_lat', 'Latitude')],
                     [('Avg_rates', 'Weighted Combo Filtered')])

data = gv.Dataset(gv.operation.project_points(data))
size_opts = dict(min_height=500, min_width=500, responsive=True)
geo_opts  = dict(size_opts, cmap='RdBu', global_extent=False, logz=False, colorbar=True)

test_lon = -92.995
test_lat = 28.45


class ShoalingRateTest(param.Parameterized):
    average = param.ObjectSelector(default='Avg_rates', objects=['Avg_rates'])

    limit = max(df['Avg_rates'])
    @param.depends('average')
    def view(self,**kwargs):        
        geo_bg = gv.tile_sources.EsriImagery.options(alpha=0.8, bgcolor='black')

        geo_kw = dict(aggregator=ds.mean(self.average), x_sampling=5, y_sampling=5)
        geo_map = gv.Points(data, crs=ccrs.GOOGLE_MERCATOR).redim.range(Avg_rates=(-self.limit, self.limit))

        static_geo = rasterize(geo_map, **geo_kw).options(alpha=1.0, tools=['hover','box_select', 'lasso_select'], active_tools=['box_select'], **geo_opts)
        tap = hv.streams.SingleTap(source = static_geo, x=np.nan, y=np.nan)       

        def drilldown(x,y):
           test_point = np.argmin((y-lons)**2+(x-lats)**2)
           temp_ts = elvs.sel(points=test_point)       
           return temp_ts.hvplot.line(x='time',y='elevations', dynamic=False)

        tap_dmap = hv.DynamicMap(drilldown, streams=[tap])

        return (geo_bg*static_geo + tap_dmap)


shoal_test = ShoalingRateTest()
stock_dmap = shoal_test.view

pn.Row(pn.Column(shoal_test.param, parameters=['average']), stock_dmap)

Can you list your GeoViews and HoloViews versions? I get a KeyError: 'projection' from GeoViews if I try to run that code. Plus the np.argmin line doesn’t look right; maybe x and y are swapped?

Anyway, it’s not clear that you actually need GeoViews here; if you need it for other things you’re doing, then fine, but if all you need to do is project lon/lat to Web Mercator, you can use hv.util.transform.lon_lat_to_easting_northing(longitude, latitude), then just use hv and avoid projections altogether when plotting.

If I hack at it I can get something running and do see it updating on tap only once. Still, it’s really hard to follow the logic here, so you’d probably be better off just starting from a clean example of tapping on a raster plot and updating a line plot. @philippjfr, do you have an example of that?

Thanks,
Yes the argmin line is backwards,
holoviews 1.13.5
geoviews 1.8.2

Got it, so just rasterize the data using holoviews as well?

Changed to code to no longer use Geoviews and the map updates with each click. I’m struggling being able to map the dataframe data without geoviews, any examples you can share?

lats = np.linspace(28.4,28.5, num=1000)
lons = np.linspace(-93,-92.99, num=100)
elevations = np.random.rand(15, 100000)*10+15

lons = np.concatenate([lons]*1000)
lats = np.repeat(lats,100)

elvs = xr.DataArray(
        data = elevations,
        dims = ('time','points'),
        name='elevations')


d = {'X_lon':lons, 'Y_lat':lats, 'Avg_rates': np.random.rand(100000)*3}

df = pd.DataFrame(d)

data = hv.Points(df, [('X_lon', 'Longitude'), ('Y_lat', 'Latitude')],
                 [('Avg_rates', 'Weighted Combo Filtered')])

geo_opts  = dict(size_opts, cmap='RdBu', global_extent=False, logz=False, colorbar=True)

test_lon = -92.995
test_lat = 28.45

geo_bg = hv.element.tiles.EsriImagery().opts(width=600, bgcolor='black')

tap = hv.streams.Tap(source=geo_bg,x=-93.3401, y=29.9242)


def drilldown(x,y):
    test_point = np.argmin((x-lons)**2+(y-lats)**2)
    temp_ts = elvs.sel(points=test_point)
    return temp_ts.hvplot.line(x='time',y='elevations',width=500,dynamic=False)

tap_dmap = hv.DynamicMap(drilldown, streams=[tap])

channel_map = rasterize(data).opts(tools=['hover','box_select', 'lasso_select'],active_tools= 
 ['box_select'], **geo_opts)
    
geo_bg * rasterize(data).opts(tools=['hover','box_select', 'lasso_select'],active_tools=['box_select'], 
**geo_opts) + tap_dmap