Below is a python script I wrote for automatic colouring of maps. It gets the data from a csv file and colours each geographic entity according to a set of cutoff values defined in a list. At the bottom of this file, it creates maps based on this blank map of US states and this blank map of US counties. Unfortunately, the svg files that this program (specifically, the "tree.write" function) produces are pretty ugly if you just open them up and look at them in a text editor. I made some attempts to make it look better, but it ended up just breaking the code. So if you know how to fix that, please let me know.
I am not a very good programmer, and I'm sure this shows in the code below. Sorry about that...
"""
Version 2: Somewhat more "generalized" than Version 1. Now you define a set of cutoffs as a string, then the program a blank map,
a data file and you tell it what rows to look for in the data file. The actual request to make the map isn't until the bottom,
when two maps are made, one for a state map and one for a county map of the US.
"""
import xml.etree.ElementTree as etree
import csv
def is_number(s): #Returns true if s is a number, false otherwise
try:
float(s)
return True
except ValueError:
return False
def GetColour(value, Spectrum, CO):
"""
This function returns the colour you set to each value. The function must be told what spectrum to use, and
what list of cutoff values to use (CO). CO is a list of 8 cutoff values.
"""
RedSpectrum = ['#f4d7d7', '#e9afaf', '#de8787', '#d35f5f', '#c83737', '#a02c2c', '#782121', '#501616', '#280b0b'] #A series of increasingly dark reds.
GreenSpectrum = ['#d7f4d7', '#afe9af', '#87de87', '#5fd35f', '#37c837', '#2ca02c', '#217821', '#165016', '#0b280b'] #Increasingly dark greens.
if Spectrum == 'Red':
UseSpectrum = RedSpectrum
elif Spectrum == 'Green':
UseSpectrum = GreenSpectrum
else:
print("Spectrum not found. Assuming Red.")
UseSpectrum = RedSpectrum
if is_number(value):
import bisect
return UseSpectrum[bisect.bisect_left(CO, value)]
print("GetColour Error!")
return "ERROR"
def ColourMap(BlankMap, OutputMap, DataFile, CutOffs, NameCol = 0, DataCol = 1, Spectrum = 'Red', ZeroPad = 2):
"""
This function takes DataFile (a csv file with a "name" column and a "data" column), and colours the BlankMap accordingly.
It outputs OutputMap.
"""
ifile = open(DataFile)
reader = csv.reader(ifile)
Dictionary = {} #The values from the DataFile will be put into this dictionary.
tree = etree.parse(BlankMap)
root = tree.getroot()
ElList = tree.findall('//{http://www.w3.org/2000/svg}path')
notfound = []
for row in reader:
if is_number(row[DataCol]):
Dictionary[str(row[NameCol]).zfill(ZeroPad).strip()] = GetColour(float(row[DataCol]), Spectrum, CutOffs)
#print (Dictionary)
for element in ElList:
if element.get('id') in Dictionary:
element.set('style',"fill:" + Dictionary[element.get('id')])
#print (element.get('id') + ": " + str(Dictionary[element.get('id')]))
else:
notfound.append(element.get('id'))
tree.write(OutputMap)
print('These elements were not found:')
for entry in notfound:
print(entry)
def MakeLegend(CutOffs, Spectrum, Percent = True):
"""
Makes a legend to cut and paste into Wikipedia for a map. Input the same CutOffs and Spectrum as the map. If you do not want a percent
sign after the values, say "Percent = False" when calling this function.
"""
Legend = "Legend:\n" + "{{" + "col-begin" + "}}" + "\n" + "{{" + "col-break" + "}}" + "\n"
RedSpectrum = ['#f4d7d7', '#e9afaf', '#de8787', '#d35f5f', '#c83737', '#a02c2c', '#782121', '#501616', '#280b0b'] #A series of increasingly dark reds.
GreenSpectrum = ['#d7f4d7', '#afe9af', '#87de87', '#5fd35f', '#37c837', '#2ca02c', '#217821', '#165016', '#0b280b'] #Increasingly dark greens.
if Spectrum == 'Red':
UseSpectrum = RedSpectrum
elif Spectrum == 'Green':
UseSpectrum = GreenSpectrum
for x, i in enumerate(UseSpectrum):
if x < len(CutOffs):
Legend += "{{" + "legend|" + str(GetColour(CutOffs[x], Spectrum, CutOffs)) + '| <' + str(CutOffs[x])
else:
Legend += "{{" + "legend|" + str(GetColour(CutOffs[x-1]+1, Spectrum, CutOffs)) + '| >' + str(CutOffs[x-1])
if Percent:
Legend += '%}}\n'
else:
Legend += '}}\n'
Legend += "{{" + "col-end" + "}}" + "\n"
print(Legend)
Cutoffs1 = [3.75, 4, 4.25, 4.5, 4.75, 5, 5.25, 5.5]
Cutoffs2 = [5, 10, 15, 20, 25, 30, 35, 40]
#ColourMap('USA_Counties_with_FIPS_and_names.svg', 'US Poverty Rates.svg', 'FIPS Codes just counties.csv', Cutoffs2, NameCol = 0, DataCol = 2, Spectrum = 'Red', ZeroPad = 5)
#ColourMap('Blank_US_Map_with_borders.svg', 'US HDI.svg', 'US states by HDI.csv', Cutoffs1, NameCol = 1, DataCol = 2, Spectrum = 'Red', )
MakeLegend(Cutoffs2, 'Red', Percent = False)
MakeLegend(Cutoffs2, 'Red', Percent = True)