0

Here is my Jupyer Notebook source code.

But a hard-coded reproducible example is below. (You will need access to the UK.geojson file from my Github or the true source: http://geoportal1-ons.opendata.arcgis.com/datasets/687f346f5023410ba86615655ff33ca9_1.geojson)

I have a geopandas DataFrame to plot the UK map and then plot certain coordinates (park run locations) on top of it.

I want to be able to interactively 'pick' the coordinates and then I will code it to show me information about that specific run location.

I can't figure out why the fig.canvas.mpl_connect('pick_event', onpick) function I'm calling does not work.

Please can someone help me figure this out?

import pandas as pd
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt


##### Create hardcoded dataset ######
df = pd.DataFrame.from_dict({0: {'parkrun_name': 'Beeston',
  'location_name': 'Weirfields Recreation Ground',
  'longitude': -1.201737,
  'latitude': 52.913592,
  'Run Date': '12/11/2022',
  'Run Number': 370.0,
  'Pos': 107.0,
  'Time': '26:05',
  'Age Grade': '49.46%',
  'PB?': np.nan},
 1: {'parkrun_name': 'Colwick',
  'location_name': 'Colwick Country Park, Nottingham',
  'longitude': -1.09786,
  'latitude': 52.945171,
  'Run Date': '22/10/2022',
  'Run Number': 511.0,
  'Pos': 127.0,
  'Time': '29:44',
  'Age Grade': '43.39%',
  'PB?': np.nan},
 2: {'parkrun_name': 'Exmouth',
  'location_name': 'Exmouth',
  'longitude': -3.412392,
  'latitude': 50.614697,
  'Run Date': '24/12/2022',
  'Run Number': 189.0,
  'Pos': 197.0,
  'Time': '25:44',
  'Age Grade': '50.13%',
  'PB?': 'PB'},
}, orient='index')

###### Read in the UK Map File ######
uk = gpd.read_file("uk.geojson")

###### Convert df to geodf ######
gdf = gpd.GeoDataFrame(
    df, geometry=gpd.points_from_xy(df.longitude, df.latitude)
)
gdf.crs = "EPSG:4326"
gdf = gdf.to_crs(uk.crs)
###### Plot interactive map ######
%matplotlib widget

fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)
uk.plot(ax=ax, alpha=0.8)

def onclick(event):
    ax = plt.gca()
    # ax.set_title(f"You selected {event.x}, {event.y}")
    
    line = event.artist
    xdata, ydata = line.get_data()
    ind = event.ind
    
    tx = f"{np.array([xdata[ind], ydata[ind]]).T}"
    
    ax.set_title(f"{tx}")
    
gdf[gdf['Run Date'].isna()].plot(ax=ax, color='black', marker='.', markersize=8, alpha=0.2, picker=20)
gdf[gdf['Run Date'].notna()].plot(ax=ax, color='#AAFF00', marker='.', markersize=50, picker=20)

ax.set_xlim(-7,2)
ax.set_ylim(49,60)

cid = fig.canvas.mpl_connect('pick_event', onclick)
4
  • 1
    You should embed the absolute minimum code (with a hard-coded dataset) into the question. If you decide to delete that gist one day, the question will be useless Commented Jan 17, 2023 at 5:17
  • @PaulH thanks, I've done this now. Although I can't hardcode the UK map file, so I've linked to the true source of it. Commented Jan 17, 2023 at 9:16
  • Function looks fine (although you don't need the gca call). Maybe backend issues? Does the code work when run as a script (i.e. not in a notebook)? I would also give %matplotlib notebook a try (instead of the widget backend). Commented Jan 17, 2023 at 12:05
  • you can't hard code the UK map, but you don't need the UK map. Hard code a a few squares in cartesian coordinates. Commented Jan 17, 2023 at 15:19

1 Answer 1

0

I'm the dev of EOmaps, a package intended to simplify making matplotlib/cartopy maps interactive.

It also comes with a feature to make geodataframes pickable... which might be exactly what you're looking for :-)

Here's a example on how it works at the moment:
(check the docs on geodataframes and callbacks for more info)

from eomaps import Maps

m = Maps()
m.add_feature.preset.coastline()

# fetch a geodataframe from NaturalEarth (with polygon geometries)
countries = m.add_feature.cultural.admin_0_countries.get_gdf(scale=110)
# fetch a geodataframe from NaturalEarth (with point geometries)
populated_places = m.add_feature.cultural.populated_places.get_gdf(scale=110)

# add the geo-dataframes to the map and make them "pickable"
m.add_gdf(countries, column="NAME", alpha=0.5,
          picker_name="countries", pick_method="contains")

m.add_gdf(populated_places, column="NAME", 
          picker_name="populated_places", pick_method="centroids")

# attach a pre-defined callback to show an annotation if you pick a geometry
m.cb.pick__countries.attach.annotate(
    text=lambda val, **kwargs: f"clicked in country:\n    {val}"
    )
m.cb.pick__populated_places.attach.annotate(
    text=lambda val, **kwargs: f"closest city to click-position:\n    {val}",
    xytext=(20,-20)
    )

# define a custom callback
def custom_cb(ID, val, pos, picker_name, **kwargs):
    print(f"### {picker_name} picked ID '{ID}' with value: '{val}' at position '{pos}'")

# execute the custom callback if you pick a geometry
m.cb.pick__populated_places.attach(custom_cb)
m.cb.pick__countries.attach(custom_cb)

m.ax.set_extent((-180.0, 180.0, -90.0, 90.0))

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.