Search code examples
pythonpandasgeopandasfolium

Plotting polygons with Folium and Geopandas don't work


I have tried to plot polygons to map with Geopandas and Folium using Geopandas official tutorial and this dataset. I tried to follow the tutorial as literally as I could but still Folium don't draw polygons. Matplotlib map works and I can create Folium map too. Code:

import pandas as pd
import geopandas as gdp
import folium
import matplotlib.pyplot as plt

df = pd.read_csv('https://geo.stat.fi/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=postialue:pno_tilasto&outputFormat=csv')
df.to_csv('coordinates.csv')

#limit to Helsinki and drop unnecessary columns 
df['population_2019'] = df['he_vakiy']
df['zipcode'] = df['postinumeroalue'].astype(int)
df['population_2019'] = df['population_2019'].astype(int)
df = df[df['zipcode'] < 1000]
df = df[['zipcode', 'nimi', 'geom', 'population_2019']]
df.to_csv('coordinates_hki.csv')
df.head()

#this is from there: https://gis.stackexchange.com/questions/387225/set-geometry-in-#geodataframe-to-another-column-fails-typeerror-input-must-be
from shapely.wkt import loads
df = gdp.read_file('coordinates_hki.csv')
df.geometry =  df['geom'].apply(loads)
df.plot(figsize=(6, 6))
plt.show()

df = df.set_crs(epsg=4326)
print(df.crs)
df.plot(figsize=(6, 6))
plt.show()

m = folium.Map(location=[60.1674881,24.9427473], zoom_start=10, tiles='CartoDB positron')
m

for _, r in df.iterrows():
    # Without simplifying the representation of each borough,
    # the map might not be displayed
    sim_geo = gdp.GeoSeries(r['geometry']).simplify(tolerance=0.00001)
    geo_j = sim_geo.to_json()
    geo_j = folium.GeoJson(data=geo_j,
                           style_function=lambda x: {'fillColor': 'orange'})
      
    folium.Popup(r['nimi']).add_to(geo_j)
    geo_j.add_to(folium.Popup(r['nimi']))                  
m

Solution

  • The trick here is to realize that your data is not in units of degrees. You can determine this by looking at the centroid of your polygons:

    >>> print(df.geometry.centroid)
    0     POINT (381147.564 6673464.230)
    1     POINT (381878.124 6676471.194)
    2     POINT (381245.290 6677483.758)
    3     POINT (381050.952 6678206.603)
    4     POINT (382129.741 6677505.464)
                       ...              
    79    POINT (397465.125 6676003.926)
    80    POINT (393716.203 6675794.166)
    81    POINT (393436.954 6679515.888)
    82    POINT (395196.736 6677776.331)
    83    POINT (398338.591 6675428.040)
    Length: 84, dtype: geometry
    

    These values are way bigger than the normal range for geospatial data, which is -180 to 180 for longitude, and -90 to 90 for latitude. The next step is to figure out what CRS it is actually in. If you take your dataset URL, and strip off the &outputFormat=csv part, you get this URL:

    https://geo.stat.fi/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=postialue:pno_tilasto
    

    Search for CRS in that document, and you'll find this:

    <gml:Envelope srsName="urn:ogc:def:crs:EPSG::3067" srsDimension="2">
    

    So, it turns out your data is in EPSG:3067, a standard for representing Finnish coordiates.

    You need to tell geopandas about this, and convert into WGS84 (the most common coordinate system) to make it compatible with folium.

    df.geometry =  df['geom'].apply(loads)
    df = df.set_crs('EPSG:3067')
    df = df.to_crs('WGS84')
    

    The function set_crs(), changes the coordinate system that GeoPandas expects the data to be in, but does not change any of the coordinates. The function to_crs() takes the points in the dataset and re-projects them into a new coordinate system. The effect of these two calls is to convert from EPSG:3067 to WGS84.

    By adding these two lines, I get the following result:

    map of helsinki