In [13]:
from bokeh.io import show, push_notebook, output_notebook
from bokeh.palettes import YlOrRd8 as palette #Spectral6 as palette
from bokeh.palettes import brewer
from bokeh.plotting import figure, save
from bokeh.models import (
ColumnDataSource,
HoverTool,
LogColorMapper
)
import gdal
import geopandas as gpd
import pysal as ps
import numpy as np
# Plot the data using Jupyter Notebook - Initialize
output_notebook()
# Read the data with Geopandas
fp = r"D:\KOODIT\Opetus\Automating-GIS-processes\AutoGIS-Sphinx\data\TravelTimes_to_5975375_RailwayStation.shp"
fp = r"C:\HY-Data\HENTENKA\KOODIT\Opetus\Automating-GIS-processes\AutoGIS-Sphinx\data\TravelTimes_to_5975375_RailwayStation.shp"
data = gpd.read_file(fp)
# Get crs
CRS = data.crs
# Read Roads
roads_fp = r"D:\KOODIT\Opetus\Automating-GIS-processes\AutoGIS-Sphinx\data\Helsinki_map_layers\pääväylät.shp"
roads_fp = r"C:\HY-Data\HENTENKA\KOODIT\Opetus\Automating-GIS-processes\AutoGIS-Sphinx\data\Helsinki_map_layers\pääväylät.shp"
roads = gpd.read_file(roads_fp)
# Ensure that geometries are in same CRS
roads['geometry'] = roads['geometry'].to_crs(crs=CRS)
In [14]:
data.head()
Out[14]:
In [15]:
def getCoords(geom, coord_type):
"""Calculates coordinates ('x' or 'y') of a LinearRing geometry"""
if coord_type == 'x':
return geom.coords.xy[0]
elif coord_type == 'y':
return geom.coords.xy[1]
def getCoords(geom, coord_type):
if geom.geom_type.startswith("Multi"):
for i, part in enumerate(geom):
if coord_type == 'x':
if i == 0:
coord_arrays = part.coords.xy[0]
else:
coord_arrays = np.concatenate([coord_arrays, part.coords.xy[0]])
elif coord_type == 'y':
if i == 0:
coord_arrays = part.coords.xy[1]
else:
coord_arrays = np.concatenate([coord_arrays, part.coords.xy[1]])
return coord_arrays
else:
if coord_type == 'x':
return geom.coords.xy[0]
elif coord_type == 'y':
return geom.coords.xy[1]
def getLineCoords(row, geom_col, x_col, y_col):
row[x_col] = list(getCoords(row[geom_col], coord_type='x'))
row[y_col] = list(getCoords(row[geom_col], coord_type='y'))
return row
def getPolyCoords(row, geom_col, x_col, y_col):
"""Calculates coordinates ('x' or 'y') of a Polygon and updates the values into 'x_col' and 'y_col' that user defines."""
# Parse the exterior of the coordinate
exterior = row[geom_col].exterior
# Get the coordinates of the exterior
x = getCoords(exterior, coord_type='x')
y = getCoords(exterior, coord_type='y')
# Update the row with a list of Polygon coordinate list (remember Polygon can have holes, thus coords are within nested list)
row[x_col] = list(x)
row[y_col] = list(y)
# Return the result
return row
# Calculate the x and y coordinates
data = data.apply(getPolyCoords, geom_col='geometry', x_col='x', y_col='y', axis=1)
# Calculate the x and y values of road geometries
roads = roads.apply(getLineCoords, geom_col='geometry', x_col='x', y_col='y', axis=1)
# Replace No Data values (-1) with large number (999)
data = data.replace(-1, 999)
# Classify our travel times into 5 minute classes until 200 minutes
# Create a list of values where minumum value is 5, maximum value is 200 and step is 5.
breaks = [x for x in range(5, 200, 5)]
# Create names for the legend (until 60 minutes)
names = ["%s-%s " % (x-5, x) for x in range(5, 60, 5)]
# Add legend for over 60
names.append("60 <")
classifier = ps.User_Defined.make(bins=breaks)
pt_classif = data[['pt_r_tt']].apply(classifier)
car_classif = data[['car_r_t']].apply(classifier)
# Rename columns
pt_classif.columns = ['pt_r_tt_ud']
car_classif.columns = ['car_r_t_ud']
# Join back to main data
data = data.join(pt_classif)
data = data.join(car_classif)
# Assign legend names for the classes
data['label_pt'] = None
data['label_car'] = None
for i in range(len(names)):
# Update rows where class is i
data.loc[data['pt_r_tt_ud'] == i, 'label_pt'] = names[i]
data.loc[data['car_r_t_ud'] == i, 'label_car'] = names[i]
# Update all cells that didn't get any value with "60 <"
data['label_pt'] = data['label_pt'].fillna("60 <")
data['label_car'] = data['label_car'].fillna("60 <")
In [16]:
data.head()
Out[16]:
In [19]:
from bokeh.palettes import RdYlBu11 as palette #Spectral6 as palette
# See reference http://bokeh.pydata.org/en/latest/docs/reference/palettes.html
# Exclude Geometry column because it cannot be inserted as a bokeh datasource
df = data.drop('geometry', axis=1)
# Exclude geometry from roads as well
rdf = roads.drop('geometry', axis=1)
# Get Data sources
dfsource = ColumnDataSource(data=df)
rdfsource = ColumnDataSource(data=rdf)
TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
# Reverse the colors in color palette
#palette.reverse()
color_mapper = LogColorMapper(palette=palette)
p = figure(title="Travel times with Public transportation to Central Railway station", tools=TOOLS, plot_width=850) # plot_height=650,
# Do not add grid line
p.grid.grid_line_color = None
# Plot grid
grid = p.patches('x', 'y', source=dfsource, name="grid",
fill_color={'field': 'pt_r_tt_ud', 'transform': color_mapper},
fill_alpha=0.7, line_color="black", line_width=0.05, legend="label_pt")
# Add roads
r = p.multi_line('x', 'y', source=rdfsource, color="grey")
# Modify legend location
p.legend.location = "top_left"
p.legend.orientation = "vertical"
# Insert a circle on top of the Central Railway Station (coords in EurefFIN-TM35FIN)
station_x = 385752.214
station_y = 6672143.803
circle = p.circle(x=[station_x], y=[station_y], name="point", size=6, color="yellow")
# Configure hover tooltips
#hover_columns = ["pt_r_tt", "car_r_t"]
#hover_tootips = [(c, '@' + c) for c in hover_columns]
phover = HoverTool(names=["point"], renderers=[circle])
phover.tooltips=[("Destination", "Railway Station")]
ghover = HoverTool(renderers=[grid])
ghover.tooltips=[("YKR-ID", "@from_id"),
("PT time", "@pt_r_tt"),
("Car time", "@car_r_t"),
]
p.add_tools(ghover)
p.add_tools(phover)
show(p)
In [18]:
import bokeh
bokeh.__version__
Out[18]: