Get the tools we need! We will always start like this.
import folium import branca import pandas as pd print(folium.__file__) print(folium.__version__)
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.
palmisano_tuscany_sites = pd.read_csv("https://raw.githubusercontent.com/ropitz/spatialarchaeology/master/data/site_centriods_tuscany.txt") palmisano_tuscany_sites.head()
|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.
palmisano_tuscany_sites_iron_age = palmisano_tuscany_sites[(palmisano_tuscany_sites['Period']=="Iron Age") ] palmisano_tuscany_sites_iron_age.head()
|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.
#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
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.
#Size surely matters... get the number of sites in each site category. palmisano_tuscany_sites_iron_age['SizeQual'].value_counts()
F 82 E 80 D 74 B 60 C 24 Name: SizeQual, dtype: int64
#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
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.
#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
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.
#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()
#look at the first line of your list to check it seems to have worked data_heat
[41.51531639941, 12.63932357767, 0.2]
#to make the heatmap, we need to get an extra tool, so... import folium.plugins as plugins
# 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