#!/usr/bin/env python # coding: utf-8 # # Edinburgh Living Landscape Pollinator Pledge # # This notebook illustrates how to plot the location data collected by the [Edinburgh Pollinator Pledge](https://edinburghlivinglandscape.org.uk/pollinatorpledge/) initiative. # # It uses the [folium](https://github.com/python-visualization/folium) library, which is a Python interface to [leaflet.js](https://leafletjs.com). # ## Preliminary Steps # # We start off by importing a few libraries # In[1]: get_ipython().run_line_magic('matplotlib', 'inline') from os import path import folium import geopandas as gpd import matplotlib.pyplot as plt import pandas as pd from shapely.geometry import Point # Next, we fetch the main pledge data which was provided for this challenge. We've stored it on GitHub for convenience. # In[49]: url = 'https://raw.githubusercontent.com/prewired/workshops/master/data/processed/' fn = 'swt_pollinator_data-2019-02-15.csv' fn = path.join(url, fn) fn # The [pandas](https://pandas.pydata.org) library is powerful tool for loading tabular data into a structure called a `DataFrame`. The `read_csv()` method takes file-like object and returns a `DataFrame`. Although it's probably not required, we can make sure that the date column in the input is parsed correctly. We also shorten one of the column labels, which is inconveniently long. # # The `head()` method allows us to look at the first few rows of the `DataFrame`. # In[50]: pollinator_data = pd.read_csv(fn, parse_dates=['Entry Date']) pollinator_data = pollinator_data.rename(columns={'What type of space do you have?': 'type'}) pollinator_data.head() # ## Version 1: Import location using CircleMarkers # # This approach pulls the data from the `DataFrame` directly. # # We will ignore all columns apart from the first three — this is just for cosmetic reasons, we could skip this step. # In[4]: df = pollinator_data[['latitude', 'longitude', 'type']] df = df.dropna(subset=['latitude', 'longitude']) # drop any rows that have missing geo-coordinates df.shape # rows x columns # In[5]: df.head() # To make it easier to import data into a map, we will create a list of triples with the data we need. The Python `zip()` method creates an iterable of n-tuples from *n* input iterables. # In[6]: locations = zip(df.latitude, df.longitude, df.type) list(locations)[:5] # we convert the iterable to a list before indexing into it # Now that we have an iterable of locations, it is straighforward to feed them into a folium map. In this approach, we can style the markers in various ways, so we've chosen to represent them as a red `CircleMarker`. # In[7]: tiles = "openstreetmap" edinburgh_centre = (55.953251, -3.188267) m = folium.Map(location=edinburgh_centre, tiles=tiles, zoom_start=12) for loc in locations: point = [loc[0], loc[1]] folium.CircleMarker(location=point, radius = 5, popup= loc[2], color = 'red', weight = 1, fill='true', fill_color='red', fill_opacity=0.25).add_to(m) # m.save('pollinators_circlmarkers.html') # do this if you want to save the map as a standalone html file. m # ## Version 2: Import locations as GeoJSON # # This alternative approach uses [GeoPandas](https://geopandas.readthedocs.io/en/latest/) to help organise the data as GeoJSON. # # We start off by creating a list of shapely `Point` objects. # In[8]: points = [Point(x, y) for x, y in zip(df.longitude, df.latitude)] polli_gdf = gpd.GeoDataFrame(df, geometry=points) # create a GeoDataFrame polli_gdf.crs = {"init": "epsg:4326"} # set a the Coordinate Reference System polli_gdf.shape # In[9]: tiles = "openstreetmap" edinburgh_centre = (55.953251, -3.188267) m = folium.Map(location=edinburgh_centre, tiles=tiles, zoom_start=12) style_function = lambda x: {"fillColor": "#00FFFFFF", "color": "#000000"} polli_geo = folium.GeoJson( polli_gdf, tooltip=folium.GeoJsonTooltip( fields=["type"], labels=True, sticky=False, ), style_function=style_function ) m.add_child(polli_geo) m.save("pollinators_markers.html") m # ## Adding a new layer: Edinburgh Green Space Audit # # One of the nice things we can do with this kind of map is add a new layer. It would be interesting to see how the pollinator locations relate to other green spaces in the city. So let's use data from the Council's Green Space Audit. You can find out some more information about it on the Council's [Mapping Portal](http://data.edinburghcouncilmaps.info/datasets/223949a6212f4068b30aa6ed8fc2e1ef_15) # # We can fetch the data in GeoJSON format from the Mapping Portal using the GeoPandas `read_file()` method. # In[10]: edinburgh_greenspaces = gpd.read_file( "http://data.edinburghcouncilmaps.info/datasets/223949a6212f4068b30aa6ed8fc2e1ef_15.geojson" ) # If we look at the columns in the `GeoDataFrame`, there is quite a lot of information: # In[11]: edinburgh_greenspaces.columns # However, we are mainly interested in the `geometry` column, which consists of a series of shapely `Polygon` objects. # In[12]: edinburgh_greenspaces['geometry'][:10] # Folium's `GeoJson()` method makes it simple to read in this data and add it to the map `m` that we created earlier. # In[13]: style_function = lambda x: {"fillColor": "#00FFFFFF", "color": "#000000"} greenspace_geo = folium.GeoJson( edinburgh_greenspaces, tooltip=folium.features.GeoJsonTooltip( fields=["NAME", "PAN65", "AuditScore"], labels=True, sticky=False, ), style_function=style_function, ) m.add_child(greenspace_geo) # ## Bee Tracks # # SWT has supplied us with polygon data which shows bumblebee movement across the city. The following `.shp` file is just a big polygon which we can again read in using GeoPandas. # In[48]: # the url that we used before! # url = 'https://raw.githubusercontent.com/prewired/workshops/master/data/processed/' fn = 'Bombus_HSI_0.5_dissolve.geojson' fn = path.join(url, fn) bombus = gpd.read_file(fn) # The default `plot()` method in GeoPandas uses the matplotlib library. # In[44]: bombus.plot() # To make this look a bit bigger, and get rid of the axes, we need to use a bit more of matplotlib. # In[45]: f, ax = plt.subplots(1, figsize=(12, 12)) bombus.plot(ax = ax) ax.set_axis_off() plt.show()