Create simple maps with geojson, pandas and matplotlib¶

Let's say you want to make a map of a roadtrip. With a map, it's easy to show people where you've been. This notebook will help you do just that.

It's easy and takes 3 steps:

  1. Import geojson data
  2. Plotting geo data with geo pandas
  3. Adding locations to a map

Curious? Let's dig in!

1. Import geojon data¶

The first thing we need to do is plot a map. To do so, we'll fetch geojson data from a geojson service. The service we'll be using is gadm.org.

We first define a set of countries we want to fetch the geojson data from. The example below include the files for Sweden, Norway, Finland and Denmark.

In [10]:
geo_jsons = [
    'https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_SWE_0.json',
    'https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_NOR_0.json',
    'https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_FIN_0.json',
    'https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_DNK_0.json',
]

Next up, we'll import the required packages to fetch the data and store it in a dataframe.

In [11]:
import geopandas as gpd
import pandas as pd

And now, we fetch the data per country, combine it into a single geo dataframe and check the result.

In [12]:
geo_df_total = pd.DataFrame()

for geo_json in geo_jsons:
    geo_df = gpd.read_file(geo_json)
    
    geo_df_total = gpd.GeoDataFrame( pd.concat( [geo_df_total, geo_df], ignore_index=True) )

geo_df_total.head()
Out[12]:
GID_0 COUNTRY geometry
0 SWE Sweden MULTIPOLYGON (((13.48970 55.38240, 13.45080 55...
1 NOR Norway MULTIPOLYGON (((7.49970 57.96100, 7.49310 57.9...
2 FIN Finland MULTIPOLYGON (((22.10860 59.76960, 22.11080 59...
3 DNK Denmark MULTIPOLYGON (((11.51270 54.62320, 11.50860 54...

Nice, this is looking good!

2. Plot geodata with geopandas¶

With the data ready, it's time to plot the data, simply call the plot() on the geo dataframe:

In [14]:
geo_df_total.plot()
Out[14]:
<AxesSubplot:>

Easy right?

To move towards our next visualisation, we'll modify this code a bit to allow for:

  • a scatter plot on the map
  • to save the map as an image (requires matplotlib)

Let's have a look:

In [8]:
import matplotlib.pyplot as plt

geo_axes = geo_df_total.plot()
geo_axes.scatter([20], [63], c='red')
plt.savefig('map_with_single_marker_test.jpg')

Ah, yes. We have everything we need to continue.

3. Adding locations to a map¶

Chances are that your trip include multiple locations. We need to make a list for all the locations we want to plot. The list should at least include:

  • Type of location (column name = Type): what type of location did you visit? For my example trip, I have 3 location types: Hike, City, and Camping. We'll use these to change the scatter icon.
  • Latitude and Longitude (column name = Latlong): the geolocation of the location. The value is the latlong value you get when you copy coordinates from Google Maps using right-click on a marker. This includes both the lat and long in one value (e.g. 55.67590592342015, 12.56973072017913). The code will split these by the , and turn them into floats.

The order of rows will be used to plot the line between all the dots.

The code below transforms the input data sheet into the data we'll use in for the plot. It consists of four collections of x and y values for hikes, cities, campings, and lines.

In [25]:
df_locations = pd.read_excel('locations lat long.xlsx')

hikes = {
    'x': [],
    'y': [],
}

cities = {
    'x': [],
    'y': [],
}

campings = {
    'x': [],
    'y': [],
}

lines = {
    'x': [],
    'y': [],
}

for index, local in df_locations.iterrows():
    coords = local['Latlong'].split(', ')
    x = float(coords[1])
    y = float(coords[0])
    
    lines['x'].append(x)
    lines['y'].append(y)
    
    if local['Type'] == 'Hike':
        hikes['x'].append(x)
        hikes['y'].append(y)

        
    if local['Type'] == 'City':
        cities['x'].append(x)
        cities['y'].append(y)
        
    if local['Type'] == 'Camping':
        campings['x'].append(x)
        campings['y'].append(y)
        
len(hikes['x']), len(cities['x']), len(campings['x']), len(lines['x'])
Out[25]:
(33, 6, 41, 80)

Alright, let's put it all together now. Using the created collections, we can plot the data on the map. Things to consider are:

  • zorder: make sure that the map is behind the scatter and line plots.
  • design: play around with the configuration like colors (c), marker size (s) and other settings.
  • hide axis: we hide the axis for to remove distractions.
In [26]:
geo_axes = geo_df_total.plot(facecolor='#eaeaea', edgecolor='#fff', linewidth=.2, figsize=(5, 8), zorder=0)

marker_size = 50

geo_axes.plot(lines['x'], lines['y'], c='teal', linewidth=.5, zorder=2)
geo_axes.scatter(campings['x'], campings['y'], c='dimgrey', s=marker_size, marker='^', zorder=3, edgecolor='white')
geo_axes.scatter(cities['x'], cities['y'], c='dimgrey', s=marker_size*.5, marker='s', zorder=3, edgecolor='white')
geo_axes.scatter(hikes['x'], hikes['y'], c='teal', s=marker_size, zorder=3, edgecolor='white')

plt.gcf().axes[0].axis('off')
plt.savefig('map_with_markers_and_lines.jpg')

And that is a pretty nice map if you ask me :)

I hope you enjoyed this notebook and it can be of help.


Want to say hi? Reach out to me here.