Open In Colab

Get the tools we need! We will always start like this.

In [1]:
import folium
import branca
import pandas as pd
print(folium.__file__)
print(folium.__version__)
/usr/local/lib/python3.6/dist-packages/folium/__init__.py
0.8.3

Get some data.

We'll experiment with the data from:

Palmisano, A., Bevan, A. and Shennan, S., 2018. Regional Demographic Trends and Settlement Patterns in Central Italy: Archaeological Sites and Radiocarbon Dates. Journal of Open Archaeology Data, 6(1), p.2. DOI: http://doi.org/10.5334/joad.43

These nice people provided the data behind their analysis so we can re-use it.

In [2]:
palmisano_tuscany_sites = pd.read_csv("https://raw.githubusercontent.com/ropitz/spatialarchaeology/master/data/site_centriods_tuscany.txt")

palmisano_tuscany_sites.head()
Out[2]:
OBJECTID Id Toponyms Type Period StartDate EndDate Longitude Latitude LocQual SizeHa SizeQual Source Source_id ORIG_FID
0 1 1 Padiglione settlement Iron Age -800 -700 12.639324 41.515316 A 0.2 E Attema et al. 2010, p. 229 15114 0
1 2 1 Padiglione settlement Republican Period -100 -30 12.636817 41.516111 A 0.2 E Attema et al. 2010, p. 229 15114 1
2 3 1 Padiglione settlement Imperial Period -30 400 12.636793 41.516111 A 0.2 E Attema et al. 2010, p. 229 15114 2
3 4 1 Padiglione settlement Late Antique/Early Medieval 400 500 12.636817 41.516111 A 0.2 E Attema et al. 2010, p. 229 15114 3
4 5 2 Astura settlement Iron Age -700 -600 12.769313 41.417720 A 34.0 D Attema et al. 2010, p. 186; Picarreta 1977, p.21 11201 4

Let's say your question is about how many iron age sites are present in the region, and what their distribution is like in space - that is where they are located and how many relatively speaking are in each area. Filter your big data file to get just the iron age sites.

In [3]:
palmisano_tuscany_sites_iron_age = palmisano_tuscany_sites[(palmisano_tuscany_sites['Period']=="Iron Age") ]

palmisano_tuscany_sites_iron_age.head()
Out[3]:
OBJECTID Id Toponyms Type Period StartDate EndDate Longitude Latitude LocQual SizeHa SizeQual Source Source_id ORIG_FID
0 1 1 Padiglione settlement Iron Age -800 -700 12.639324 41.515316 A 0.20 E Attema et al. 2010, p. 229 15114 0
4 5 2 Astura settlement Iron Age -700 -600 12.769313 41.417720 A 34.00 D Attema et al. 2010, p. 186; Picarreta 1977, p.21 11201 4
96 97 65 Piscina della Farna settlement Iron Age -700 -600 12.726173 41.482471 A 0.04 E Attema et al. 2010 11284 96
104 105 70 Cadolino settlement Iron Age -800 -700 12.662566 41.492596 A 1.00 E Attema et al. 2010 15003 104
133 134 81 Pineta della Campana settlement Iron Age -700 -600 12.670644 41.513748 A 0.08 E Attema et al. 2010 15029 133

Now we want to add this data to a simple map. You need to start by getting the coordiantes so you can center the map - that is focus on the area where your data is. You want to add a marker for each site.

In [9]:
#location is the mean of every lat and long point to centre the map.
location = palmisano_tuscany_sites_iron_age['Latitude'].mean(), palmisano_tuscany_sites_iron_age['Longitude'].mean()

#A basemap is then created using the location to centre on and the zoom level to start.
m = folium.Map(location=location,zoom_start=10)

#Each location in the DataFrame is then added as a marker to the basemap points are then added to the map
for i in range(0,len(palmisano_tuscany_sites_iron_age)):
    folium.Marker([palmisano_tuscany_sites_iron_age['Latitude'].iloc[i],palmisano_tuscany_sites_iron_age['Longitude'].iloc[i]]).add_to(m)
        
m
Out[9]:

Yay! You have a map. Now, a design question: what is a good starting zoom level. Do you want your map zoomed further out or further in given the extent of your data. In the cell above, play around with the 'zoom_start' parameter and find a good zoom level.

Now let's look at how to represent larger and smaller sites. The archaeologists in this project have a qualitative ranking of site size from large (A) to small (F). You can see the categories they've used, and that there are more small sites than larger ones. Say you want to make the two categories of sites that are the largest different colours.

In [10]:
#Size surely matters... get the number of sites in each site category. 
palmisano_tuscany_sites_iron_age['SizeQual'].value_counts()
Out[10]:
F    82
E    80
D    74
B    60
C    24
Name: SizeQual, dtype: int64
In [15]:
#now make a map just like you did before. Note that this time we're adding a scale bar with 'control_scale'
location = palmisano_tuscany_sites_iron_age['Latitude'].mean(), palmisano_tuscany_sites_iron_age['Longitude'].mean()
m = folium.Map(location=location,zoom_start=10,control_scale = True)

#Assign different colours to the two large site categories - B and C in this case
for i in range(0,len(palmisano_tuscany_sites_iron_age)):


    site_size = palmisano_tuscany_sites_iron_age['SizeQual'].iloc[i]
    if site_size == 'C':
        color = 'blue'
    elif site_size == 'B':
        color = 'green'
    else:
        color = 'red'
    
   
    folium.Marker([palmisano_tuscany_sites_iron_age['Latitude'].iloc[i],palmisano_tuscany_sites_iron_age['Longitude'].iloc[i]],icon=folium.Icon(color=color)).add_to(m)

m
Out[15]:

Now, go back into the cell above and experiment a bit. Try out some different colours. Do certain combinations work well? Try adding different colours for each of the smaller categories of sites. Does that make the map clearer or more confusing?

Maybe it makes more sense to show size by changing the size of the icon than the colour. Let's make another map that varies the size of the icon for each site based on its size in hectares.

In [31]:
#now make a map just like you did before. 
location = palmisano_tuscany_sites_iron_age['Latitude'].mean(), palmisano_tuscany_sites_iron_age['Longitude'].mean()
m = folium.Map(location=location,zoom_start=8,control_scale = True)


# Set the size for each circle icon by defining the 'radius' which is the radius of the circle
# Here we are multiplying the size in hectares (SizeHa) by 15. Try different values here to get icons a size you like
for i in range(0,len(palmisano_tuscany_sites_iron_age)):
   folium.Circle(
      location=[palmisano_tuscany_sites_iron_age['Latitude'].iloc[i],palmisano_tuscany_sites_iron_age['Longitude'].iloc[i]],
      popup=palmisano_tuscany_sites_iron_age.iloc[i]['Toponyms'],
      radius=palmisano_tuscany_sites_iron_age.iloc[i]['SizeHa']*15,
      color='crimson',
      fill=True,
      fill_color='crimson'
   ).add_to(m)
    
m
Out[31]:

Now what if you want to make a map that shows the concentration of sites in the iron age, that is areas with many and fewer sites?

This kind of map is often called a 'heatmap'. Essentially it shows areas with more sites as 'hotter' (generally red in colour) and areas with fewer sites as cooler.

In this kind of map surely larger sites should count more - we call this 'weighting'. The general idea is that a site that is twice as large should count for twice as much in our heatmap.

In [0]:
#first make a list of the coordinates of each site and its size in hectares, which we will use for the size-based weighting
data_heat = palmisano_tuscany_sites_iron_age[['Latitude','Longitude','SizeHa']].values.tolist()
In [17]:
#look at the first line of your list to check it seems to have worked
data_heat[0]
Out[17]:
[41.51531639941, 12.63932357767, 0.2]
In [0]:
#to make the heatmap, we need to get an extra tool, so...
import folium.plugins as plugins
In [20]:
# now make a map a bit like you did before, set your zoom level and your centre location. Then use the plugin to make the heatmap. 

m = folium.Map(location=location, zoom_start=10)
#tiles='stamentoner'

plugins.HeatMap(data_heat).add_to(m)

m
Out[20]: