1

I dont have a working code - but a snipet of my code can be as follows. I'm trying to use geopandas with mathplotlib, and trying to plot a map with links and points.

shape_file = os.path.join(os.getcwd(), "Healthboard")
    healthboard = gp.read_file(os.path.join(shape_file, "healthboard.shp"))
    healthboard = healthboard.to_crs({'init': 'epsg:4326'}) # re-projection   

    geo_df1 = geo_df1[geo_df1['HealthBoardArea2019Code'] == string1]
    geo = geo_df[geo_df['Healthboard '] == string2]

    new_shape_file = os.path.join(os.getcwd(), "Council_Shapefile")

    council_to_healtboard = pd.read_csv("council_to_healthboard.csv")
    council_to_healthboard = council_to_healtboard.rename(columns = {'CA': 'Council_area_code'})
    council = gp.read_file(os.path.join(new_shape_file, "Council_shapefile.shp"))
    council = council.to_crs({'init': 'epsg:4326'})
    council = council.rename(columns = {'la_s_code':'Council_area_code'})
    df = council.merge(council_to_healthboard, on = 'Council_area_code', how ='inner')

    # Plotting stuff 
    fig, ax = plt.subplots(figsize=(15,15))

    geo_df1.plot(ax = ax, markersize=35, color = "blue", marker = "*", label = "Postcode Sector")

    geo.geometry.plot(ax = ax, color = "red", markersize=20, alpha = 0.8, label = 'SiteName')

    #healthboard[healthboard["HBName"]=="Lothian"].plot(ax = ax, alpha = 0.6)
    #healthboard[healthboard["HBName"]=="Lothian"].boundary.plot(ax = ax, color = "black", alpha = 0.6)

    df[df["HB"]=="S08000024"].boundary.plot(ax =ax, color = "black", alpha = 0.1)
    df[df["HB"]=="S08000024"].plot(ax =ax, cmap = "viridis", alpha = 0.1)

    links_gp.plot(ax =ax, alpha = 0.25, color='brown', linestyle = "-")


enter image description here

My links_gp.plot has 40 time periods, as a result I want to make one plot, and have a button to adjust the parameters of time. Or if not possible a series of 40 plots. I've tried numerous ways but keep failing on this. I would really appreciate if someone could guide me on this.

Derek O
  • 16,770
  • 4
  • 24
  • 43
  • Can you add your DataFrame(s) into the question as [formatted text](https://stackoverflow.com/questions/20109391/how-to-make-good-reproducible-pandas-examples)? Otherwise it's very difficult to reproduce your code because we cannot copy/paste your data. Doing this will also make it more likely that someone tries to help answer your question. Best of luck! – Derek O Jul 16 '21 at 22:33

1 Answers1

0

I'm aware that you are using matplotlib, but if you don't mind using bokeh instead, you could use the following. To create an interactive plot with a possibility to adjust a parameter, bokeh provides a slider widget which can be used to change the plot based on a custom filter function.

An example from a geopandas dataframe with LineString geometries similar to the one you posted:

import geopandas as gpd
from bokeh.io import show, output_notebook
from bokeh.models import (CDSView, ColumnDataSource, CustomJS, 
                          CustomJSFilter, Slider, Column)
from bokeh.layouts import column
from bokeh.plotting import figure

# prepare data source
links_gp['x'] = links_gp.apply(lambda row: list(row['geometry'].coords.xy[0]), axis=1)
links_gp['y'] = links_gp.apply(lambda row: list(row['geometry'].coords.xy[1]), axis=1)
# drop geometry column, because it can't be serialized to ColumnDataSource
links_gp.drop('geometry', axis=1, inplace=True)
linesource = ColumnDataSource(links_gp)

p = figure(title = 'Bokeh Time Slider', 
           plot_height = 500,
           plot_width = 600, 
           toolbar_location = 'below',
           tools = "pan, wheel_zoom, box_zoom, reset")
slider = Slider(title='Time Period', start=1, end=40, step=1, value=1)

# Callback triggers the filter when the slider moves
callback = CustomJS(args=dict(source=linesource), 
                    code="""source.change.emit();""")
slider.js_on_change('value', callback)

# Custom filter that selects all lines of the time period based on the slider value
custom_filter = CustomJSFilter(args=dict(slider=slider), 
                               code="""
var indices = [];
// iterate through rows of data source and check if time period value equals the slider value
for (var i = 0; i < source.get_length(); i++){
 if (source.data['Time Period'][i] == slider.value){
 indices.push(true);
 } else {
 indices.push(false);
 }
}
return indices;
""")

# Use filter to determine which lines are visible
view = CDSView(source=linesource, filters=[custom_filter])

# plot lines to map
p.multi_line('x', 'y', source=linesource, color='red', line_width=3, view=view)

layout = column(p, slider)
show(layout)

This will be the result of the above code.

CodeBard
  • 256
  • 2
  • 4
  • Thank you for the wonderful comment! But I notice that these are just points and not the lines that one gets from line strings. Also, if I were to use shape files along with it, how will I go with it ? – Jimjamlorde Jul 18 '21 at 07:53
  • These are in fact all `LineString`s not points. It's just that my data source had very short segments far apart from each other, so it only looks like it were points. Do you mean adding additional data from other shapefiles to the plot? You can always add more geometries from other dataframes with `p.circle()`, `p.multi_line()` or `p.patches()` (bokehs terms for point, line, polygon). Maybe this helps you further: https://automating-gis-processes.github.io/2016/Lesson5-interactive-map-bokeh.html#creating-interactive-maps-using-bokeh-and-geopandas – CodeBard Jul 18 '21 at 10:18
  • Lovely, thank you! It means a lot to me. I will implement this today on my code and let you know how it goes! Many thanks once again. – Jimjamlorde Jul 18 '21 at 11:11
  • while I sincerely appreciate the answer, is it possible to provide what I was originally asking. How can I create 40 plots, using a for loop or something with regards to this problem. – Jimjamlorde Aug 08 '21 at 16:24