Creating a Density Heat Map with Leaflet

Interested in learning ArcPy? check out this course.

A Heat Map is a way of representing the density or intensity value of point data by assigning a colour gradient to a raster where the cell colour is based on clustering of points or an intensity value. The colour gradient usually ranges from cool/cold colours such as hues of blue, to warmer/hot colours such as yellow, orange and hot red. When mapping the density of points that represent the occurrence of an event, a raster cell is coloured red when many points are in close proximity and blue when points are dispersed (if using the colour range above). Therefore, the higher the concentration of points in an area the greater the ‘heat’ applied to the raster. When mapping intensity, points are assigned an intensity value, the higher the value the hotter the colour assigned to the raster, with lower intensity values assigned cooler colours. Here, we will look at mapping density.

I have created a Python script that creates 250 randomized points in a localised area and 250 in a larger region covering Co. Kildare in Ireland. The script is at the bottom of the post. The output from the script is a GeoJSON file with the point data and also a JavaScript file with the point data. The reason for two files is that GeoJSON coordinates are long, lat while Leaflet points are lat,long. The JavaScript file will be used to create the heat map raster while the GeoJSON file will be used to add the points to the map as a vector.

Unrealistic Scenario: A mad scientist has cloned the Great Irish Elk from the DNA of a carcass and bred 500 of them, the animals grew smarter and escaped. They are beginning to get out of control as they wreak havoc across the land destroying local flora and fauna. Luckily the scientist had the foresight to tag each elk with a GPS tracker. Each point represents the current location of an individual elk. The heat map will aid hunters in focusing their attentions on saving the flowers and critters that dwell amongst them.

Let’s get started and begin to build the HTML file for creating the WebMap. The template below creates a WebMap using Leaflet panned and zoomed to Kildare with an OSM basemap.

Note: Wordpress doesn’t seem to like script or div tags in the code below so if using the code fix the space added to the tags.

Save the code below as a HTML file.

<!DOCTYPE html>
<html>
<head>
 <title>
  Heatmap with Leaflet
 </title>
 <!-- link to leaflet css --> 
 <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
 <!-- map is full width and height of device -->
 <meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=no" />
 <style>
  body{
   padding: 0;
   margin: 0;
  }
  html, body, #map {
   height: 100%;
  }
 </style>
</head>
<body>
 <!-- leaflet js -->
 < script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></ script>
 < div id="map">
 </ div>
 < script>
  var map = L.map('map',{center: [53.15, -6.7], zoom: 10});
 
  // OSM Baselayer
  L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map);
 </ script>
 
</body>
</html>
Leaflet OSM

Click map to open in browser

We need to access two plugins, Leaflet.heat and Leaflet.ajax. Download leaflet-heat.js from here and leaflet.ajax.js from here and place them in the same directory as the HTML file. Leaflet.heat allows for the heat map to be generated while Leaflet.ajax enables adding GeoJSON data to the map from an external source. Add the following below the reference to leaflet.js in the <body>. Remember to correct the spaces in the script tags.

< script src="leaflet-heat.js"></ script>
< script src="leaflet.ajax.min.js"></ script>
< script src="heat_points.js"></ script>

The third script tag above accesses the heat_points.js file created by running the Python script. Make sure that this file is also in the same directory as your HTML file.

Next we add the boundary of Co. Kildare to the map. The data is stored in an external GeoJSON file. You can access this file here, if following this tutorial save the file to the same directory as your HTML. Before adding the polygon to the map we will define a style for it. Under the line that adds the OSM base layer add the following.

var kildareStyle = {
 "fillColor": "#CC9933", 
 "color": "#000000",
 "weight": 2, 
 "fillOpacity": 0.2
 };

This will style the Kildare polygon with a translucent brown fill and a black outline. Let’s add the polygon to the map using the Leaflet.ajax plugin…

var kildare = new L.GeoJSON.AJAX('kildare.geojson', {style:kildareStyle}).addTo(map);

Save your HTML and open in a browser. You will notice that the polygon has not been added. This is because the files need to be accessed via a server. There are two things you can do here. Add the files to a hosted server like this map that shows where the tutorial is currently at, or use Python to act as a server for you. Open up the command prompt and set the current working directory to where your files are located. Type in the following to the command prompt and press enter…

python -m SimpleHTTPServer

Open up a browser window and type 127.0.0.1:8000/ followed by the name of your html. For example 127.0.0.1:8000/leaflet_heatmap.htmlThe map should open with the Kildare GeoJSON polygon styled and added to the map like below.

Kildare GeoJSON

The map was set up for a desktop so you might need to zoom and pan if using a mobile device. Let’s add the heat layer to the map with one line of code. Place the following under the code that added the Kildare polygon.

var heat = L.heatLayer(heat_points, {radius:12,blur:25,maxZoom:11}).addTo(map);

heat_points in the code above refers to the heat_points variable stored in the heat_points.js file created from the Python script. You can get these points here. This allows us to easily access lat, long as required by Leaflet. Save your file and open in a browser via the Python server. radius is the size of the points, the blur value makes points merge more fluidly, a lower value creates individual points and extreme high values will lose all heat. maxZoom is the zoom level where the map looks best. Other options are minOpacity which is the opacity that the heat will start at, and gradient which allows you to define the colour gradient of the heat map e.g  {gradient: {.4:”blue”,.6:”cyan”,.7:”lime”,.8:”yellow”,1:”red”}}. For more information see Chapter 3 from Leaflet.js Essentials from Packt Publishing and the Leaflet.heat page.

Leaflet Heatmap

There is a high density of elk focused on a location in the west of Kildare indicating that the culling should begin there. Populated areas such as Leixlip, Prosperous, and  Kildare town should also be a priority.

To complete the map let’s add the GeoJSON points to the map. The GeoJSON point file can be accessed here. First we define a style, the code below will be a simple black dot for each elk. Place this code under the kildareStyle used to style the Kildare polygon.

var pointStyle = {
 radius: 2,
 fillColor: "#000000",
 color: "#000000",
 weight: 1,
 fillOpacity: 1
 };

We now use the Leafet.ajax plugin to add the data to the map and the pointToLayer option for a GeoJSON layer. Place the code below under the code that adds the Kildare polygon to the map.

var points = new L.GeoJSON.AJAX('points.geojson', {pointToLayer: function (feature, latlng) {
   return L.circleMarker(latlng, pointStyle);
 }}).addTo(map);

Save the HTML and open in your browser (via a server). Click on the map below to open a hosted version of this example in your browser.

Leaflet Elk Points

Those elks don’t stand a chance unless they learn to shoot lasers out of their antlers.

This was an introduction to creating a density Heat Map with Leaflet.js. Play around with the options; radius, blur, maxZoom etc. to understand how they effect the heatmap layer and use a search engine and the sources below for additional information.

Sources

Leaflet.js Essentials (Packt Publishing)
Calvin Metcalf
Vladimir Agafonkin

Python Script for Coordinate Generation

import random

# create a list of 500 points
coordinates = []

for coord in range(250):
 x1 = round(random.uniform(-7.1650,-6.4700),4)
 y1 = round(random.uniform(52.8320,53.4670),4)
 x2 = round(random.uniform(-7.0510,-7.0680),4) 
 y2 = round(random.uniform(53.1800,53.1960),4)

 point1 = [x1,y1]
 point2 = [x2,y2]
 
 coordinates.append(point1)
 coordinates.append(point2)

# open a geojson file to add the points to
# change the path to suit your setup
f = open("C:/blog/leaflet/points.geojson", "w")

# opening bracket
f.write('[')

count = 1

# add data to the geojson file (formatted)
for coord in coordinates:
 if count == 1:
  f.write('{\n \
  \t"type": "Feature",\n \
  \t"geometry": {\n \
  \t\t"type": "Point",\n \
  \t\t"coordinates": [' + str(coord[0]) + ',' + str(coord[1]) + ']\n \
  \t}\n \
  }')
 
  count = count + 1
 
 else:
  f.write(',{\n \
  \t"type": "Feature",\n \
  \t"geometry": {\n \
  \t\t"type": "Point",\n \
  \t\t"coordinates": [' + str(coord[0]) + ',' + str(coord[1]) + ']\n \
  \t}\n \
  }')
 
  count = count + 1

# closing bracket
f.write(']')
# close the file
f.close()

# open a JavaScript file to store coordinates in lat,long
# change the path to suit your setup
f2 = open("C:/blog/leaflet/heat_points.js", "w")
# create a variable
f2.write('var heat_points = [')

count = 1

# write data to js file
for coord in coordinates:
 if count == 1:
  f2.write('[' + str(coord[1]) + ',' + str(coord[0]) + ']')

  count = count + 1
 
 else:
  f2.write(',[' + str(coord[1]) + ',' + str(coord[0]) + ']')

  count = count + 1

# closing bracket
f2.write(']')
# close file
f2.close()

Haversine Distances with Python, EURO 2016 & Historic Results

Irish JerseyIreland kick off their European Championship 2016 Group E on the 13th of June at the Stade de France, Saint-Denis, in the northern suburbs of Paris at 6pm local time (5pm on the Emerald Isle). Our opponents that evening will be Sweden, followed by a trek down south to Bordeaux to face Belgium on the 18th, and then a northern journey to Lille to take on Italy in the final group game on the 22nd.

Euro 2016 Overview

Click on map to enlarge

I have a flight from Dublin to Paris, a train from Paris to Bordeaux, Bordeaux to Lille, Lille to Paris, and a flight back to Dublin. Let’s use the simple Haversine formula to calculate approximately how far I will be travelling to follow the boys in green this summer. Just to note, I am not being pessimistic by not sticking around for the knockout stages, I have every faith that we will get out of the group, a two week holiday in France is going to take its toll on the bank account! But who knows…

The Haversine formula calculates the distance between two points on a sphere, also known as the Greater Circle distance, here’s the Python code similar to rosettacode.org

from math import radians, sin, cos, sqrt, asin

def haversine(coords_1, coords_2):

 lon1 = coords_1[0]
 lat1 = coords_1[1]
 lon2 = coords_2[0]
 lat2 = coords_2[1]
 
 # Earth radius in kilometers
 earth_radius = 6372.8

 # convert from degrees to radians
 dLon = radians(lon2 - lon1)
 dLat = radians(lat2 - lat1) 
 lat1 = radians(lat1)
 lat2 = radians(lat2)

 # mathsy stuff for you to research...if you're so inclined to
 a = sin(dLat/2)**2 + cos(lat1)*cos(lat2)*sin(dLon/2)**2
 c = 2*asin(sqrt(a))

 # return the distance rounded to 2 decimal places
 return round((earth_radius * c),2)

Now to use the function to calculate my approximate travelling distance. First define the coordinates as lists.

# lon - lat
dub_airport = [-6.2700, 53.4214]
cdg_airport = [2.5478, 49.0097]
paris = [2.3831, 48.8390]
bordeaux = [-0.5800, 44.8400]
lille = [3.0583, 50.6278]

And then use the coordinates as parameters for the haversine function. Print the distance for each leg and the total distance.

leg_1 = haversine(dub_airport, cdg_airport)
print "Leg 1: " + str(leg_1) + " Km"

leg_2 = haversine(paris, bordeaux)
print "Leg 2: " + str(leg_2) + " Km"

leg_3 = haversine(bordeaux, lille)
print "Leg 3: " + str(leg_3) + " Km"

leg_4 = haversine(lille, paris)
print "Leg 4: " + str(leg_4) + " Km"

leg_5 = haversine(cdg_airport, dub_airport)
print "Leg 5: " + str(leg_5) + " Km"

total_dist = leg_1 + leg_2 + leg_3 + leg_4 + leg_5
print "Total Distance " + str(total_dist) + " Km"

The output from running the complete script is…

Leg 1: 785.3 Km
Leg 2: 498.57 Km
Leg 3: 698.71 Km
Leg 4: 204.79 Km
Leg 5: 785.3 Km
Total Distance 2972.67 Km

Just over 2,970 Km! Ok so I could have been more accurate with getting the road length from my house to the airport, using the Haversine to find the distance from Dublin Airport to Charles De Gaulle, and then using road and rail networks to calculate my internal travel in France but the idea here was to introduce you to the Haversine formula.

And here’s a little map to show my up coming travels. These maps were made by exporting data from ArcGIS as an emf and imported in CorelDraw, a graphic design package for styling.

Euro 2016 Travel

Click on the map to enlarge

That’s it for the Haversine, Python and GIS in general. If you have no interest in football you can stop reading here. From here down it’s just historic results against our opponents and opinions.

SWEDEN

IRE v SWE

We have faced Sweden in two world cup qualifying campaigns, in qualification for one European Championship, and four times in a friendly match. It doesn’t bode well that we have never beaten Sweden in a competitive game, losing four times and drawing twice.

vrs Sweden Competitive

Our dominance over Sweden in friendlies see us with a 75% win percentage, only losing once in four meetings.

v Sweden Friendlies

Overall: P10 W3 D2 L5 F13 A16 – WIN% 30

BELGIUM

BEL v IRE

Similar to our record against Sweden, Ireland have never beaten Belgium in a competitive fixture, although in the seven competitive games we have only been beaten twice, drawing the other five.

v Belgium Competitive

We have to look to the friendly results for Ireland to get the better of Belgium, with Ireland edging the score at four wins to three.

v Belgium Friendlies

Overall: P14 W4 D5 L5 F24 A25 – WIN% 28.57

ITALY

ITA v IRE

Well would you look at that!! We have beaten at least one of our upcoming opponents in a competitive match. Ok so one win out of eight but I am every the optimist and from 1994 onwards our record against Italy is pretty decent both competitively and in friendlies.

v Italy Competitive

Our overall record against Italy looks dismal but recent results give Ireland hope. Since and including our meeting with Italy at World Cup 94 the head-to-head is two wins each and three draws. If you offered my a draw now against Italy I’d take it. Remember there are now twenty-four teams in the competition meaning the best placed third team in each group qualifies for the second round.

v Italy Friendlies

Overall: P13 W2 D3 L8 F9 A20 – WIN% 16.67

Do we stand a chance of getting out of the group?

Let me know your thoughts. With three teams qualifying from four out of the six groups Ireland have every chance of progressing. A win and a draw will more than likely be the minimum requirement to achieve passage to the next phase, but let’s not forget that Ireland reached the Quarter Finals of Italia 90 having not won a single match! Beat Sweden in the opener and get a draw against Italy. If only it was that simple 🙂

#COYBIG

Data used to create maps was downloaded from Natural Earth.
Historic results collate from the FAI and Wikipedia.

CSV to Shapefile with pyshp

Interested in learning ArcPy? check out this course.

In this post I will look at extracting point data from a CSV file and creating a Shapefile with the pyshp library. The data consists of the location of trees with various attributes generated by the Fingal County Council in Ireland. The data can be downloaded as a CSV file from dublinked.ie.

pyshp is a pure Python library designed to provide read and write support for the ESRI Shapefile (.shp) format and only utilizes Python’s standard library to achieve this. The library can be downloaded from https://code.google.com/p/pyshp/ and placed in the site-packages folder of your Python installation. Alternatively you can use easy-install…

easy_install pyshp

…or pip.

pip install pyshp

NOTE: You should make yourself familiar with the pyshp library by visiting Joel Lawhead’s examples and documents here.

The full code is at the bottom of the post, the following is a walkthrough. When ready to go open your favourite editor and import the modules required for the task at hand.

import shapefile, csv

We will use the getWKT_PRJ function discussed in a previous post.

def getWKT_PRJ (epsg_code):
 import urllib
 wkt = urllib.urlopen("http://spatialreference.org/ref/epsg/{0}/prettywkt/".format(epsg_code))
 remove_spaces = wkt.read().replace(" ","")
 output = remove_spaces.replace("\n", "")
 return output

Create an instance of the Shapefile Writer( ) class and declare the POINT geometry type.

trees_shp = shapefile.Writer(shapefile.POINT)

Set the autoBalance to 1. This enforces that for every record there must be a corresponding geometry.

trees_shp.autoBalance = 1

Create the field names and data types for each.

trees_shp.field("TREE_ID", "C")
trees_shp.field("ADDRESS", "C")
trees_shp.field("TOWN", "C")
trees_shp.field("TREE_SPEC", "C")
trees_shp.field("SPEC_DESC", "C")
trees_shp.field("COMMONNAME", "C")
trees_shp.field("AGE_DESC", "C")
trees_shp.field("HEIGHT", "C")
trees_shp.field("SPREAD", "C")
trees_shp.field("TRUNK", "C")
trees_shp.field("TRUNK_ACTL", "C")
trees_shp.field("CONDITION", "C")

Create a counter variable to keep track of the number of feature written to the Shapefile.

counter = 1

Open the CSV file in read mode.

with open('C:/csv_to_shp/Trees.csv', 'rb') as csvfile:
 reader = csv.reader(csvfile, delimiter=',')

Skip the header.

next(reader, None)

Loop through each row and assign each attribute in the row to a variable.

for row in reader:
 tree_id = row[0]
 address = row[1]
 town = row[2]
 tree_species = row[3]
 species_desc = row[4]
 common_name = row[5]
 age_desc = row[6]
 height = row[7]
 spread = row[8]
 trunk = row[9]
 trunk_actual = row[10]
 condition = row[11]
 latitude = row[12]
 longitude = row[13]

Set the geometry for each record based on the longitude and latitude vales.

trees_shp.point(float(longitude),float(latitude))

Create a matching record for the geometry using the attributes.

trees_shp.record(tree_id, address, town, tree_species, species_desc, common_name, age_desc,height, spread, trunk, trunk_actual, condition)

Print to screen the current feature number and increase the counter.

print "Feature " + str(counter) + " added to Shapefile."
 counter = counter + 1

Save the Shapefile to a location and name the file.

trees_shp.save("C:/csv_to_shp/Fingal_Trees")

Create a projection file (.prj)

prj = open("C:/csv_to_shp/Fingal_Trees.prj", "w")
epsg = getWKT_PRJ("4326")
prj.write(epsg)
prj.close()

Save and run the script. The number of features should be printed to the console.

Number of features

If you open the original CSV file you can see that there are also 33670 records. Navigate to the file location where you saved the Shapefile output. You should see four files shown below.

Shapfile

And just to make sure that the data is correct, here I have opened it up in QGIS.

QGIS - Fingal Trees

And the attribute table…

QGIS Attribute Table

And here’s the full code…

# import libraries
import shapefile, csv

# funtion to generate a .prj file
def getWKT_PRJ (epsg_code):
 import urllib
 wkt = urllib.urlopen("http://spatialreference.org/ref/epsg/{0}/prettywkt/".format(epsg_code))
 remove_spaces = wkt.read().replace(" ","")
 output = remove_spaces.replace("\n", "")
 return output

# create a point shapefile
trees_shp = shapefile.Writer(shapefile.POINT)

# for every record there must be a corresponding geometry.
trees_shp.autoBalance = 1

# create the field names and data type for each.
trees_shp.field("TREE_ID", "C")
trees_shp.field("ADDRESS", "C")
trees_shp.field("TOWN", "C")
trees_shp.field("TREE_SPEC", "C")
trees_shp.field("SPEC_DESC", "C")
trees_shp.field("COMMONNAME", "C")
trees_shp.field("AGE_DESC", "C")
trees_shp.field("HEIGHT", "C")
trees_shp.field("SPREAD", "C")
trees_shp.field("TRUNK", "C")
trees_shp.field("TRUNK_ACTL", "C")
trees_shp.field("CONDITION", "C")

# count the features
counter = 1

# access the CSV file
with open('C:/csv_to_shp/Trees.csv', 'rb') as csvfile:
 reader = csv.reader(csvfile, delimiter=',')
 # skip the header
 next(reader, None)

#loop through each of the rows and assign the attributes to variables
 for row in reader:
  tree_id = row[0]
  address = row[1]
  town = row[2]
  tree_species = row[3]
  species_desc = row[4]
  common_name = row[5]
  age_desc = row[6]
  height = row[7]
  spread = row[8]
  trunk = row[9]
  trunk_actual = row[10]
  condition = row[11]
  latitude = row[12]
  longitude = row[13]

  # create the point geometry
  trees_shp.point(float(longitude),float(latitude))
  # add attribute data
  trees_shp.record(tree_id, address, town, tree_species, species_desc, common_name, age_desc,height, spread, trunk, trunk_actual, condition)

  print "Feature " + str(counter) + " added to Shapefile."
  counter = counter + 1

# save the Shapefile
trees_shp.save("C:/csv_to_shp/Fingal_Trees")

# create a projection file
prj = open("C:/csv_to_shp/Fingal_Trees.prj", "w")
epsg = getWKT_PRJ("4326")
prj.write(epsg)
prj.close()

Any problems let me know.

Labelling in ArcGIS with Formatting Tags and Expressions

Interested in learning ArcPy? check out this course.

I recently sat an interview test where I had to use labelling in ArcGIS Desktop without the aid of the internet or notes for guidance. I must admit I was pretty stumped when it came to formatting labels beyond using the GUI (Labels tab in the Layer Properties) and stepping into the world of expressions, so I decided to rectify this and explore the options. ESRI maintain a fantastic help resource that can be found at here (for 10.2), where you can find what you need to get started. The following examples are some neat ways you can format labels using tags and expressions. They’re quite basic but act as a foundation to build upon.

Open the Layer Properties of the layer you wish to label and switch to the Labels tab. Click on the Expression… button to open the Label Expression window. Switch the Parser at the bottom of the window to Python.

In this first example I will simply concatenate a string with a attribute (also a string), the custom string will be placed on the first line of the label and the attribute of the county name placed on the second. This is achieved with the following…

"This is the geographic region of\n" + [COUNTYNAME]

Labelling - Concatenation[COUNTYNAME] represents the field names COUNTYNAME in the attribute table of the data I am working with. Next we will concatenate the area on a new line and round the decimal places to two. We cast the area to a string so the concatenation can be preformed.

"This is the geographic region of\n" + [COUNTYNAME] + "\nArea: " + str(round(float([Shape_Area]),2)) + " sq m"

Labelling - RoundingNext we force labels to be presented in upper case text. The Advanced checkbox must be checked to create multiline expressions. You could also replace upper with lower in the below code snippet to force text to be lower case, or replace with title to capitalize the first letter in each word (proper case).

def FindLabel ([COUNTYNAME]):
 label = [COUNTYNAME]
 label = label.upper()
 return label

Labelling - Upper CaseStack text on new lines by using replace. The expression below replaces spaces in the COUNTYNAME attribute with n which forces text after a space onto a new line and removes the space.

def FindLabel ([COUNTYNAME]):
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 return label

Labelling - ReplaceLets make the text bold  by using format tags. Each tag has an opening < > and closing </ > tag.

def FindLabel ([COUNTYNAME]):
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 return "<BOL>" + label + "</BOL>"

Labelling - Bold…and then add some colour. Missing RGB values are assumed to be 0.

def FindLabel ([COUNTYNAME]):
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 return "<BOL><CLR red='255'>" + label + "</CLR></BOL>"

Labelling - ColourSo how about a custom colour…

def FindLabel ([COUNTYNAME]):
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 return "<BOL><CLR red='125' green='105' blue='190'>" + label + "</CLR></BOL>"

Labelling - Custom Colour…and italics and an underline

def FindLabel ([COUNTYNAME]):
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 return "<UND>" + "REGIONn" + "</UND>" + "<BOL><ITA><CLR red='125' green='105' blue='190'>" + label + "</CLR></ITA></BOL>"

Labelling - Italics/UnderlineWe’ll throw back in the area and format sq m with a superscripted 2 instead…(use SUB if you need to subscript text)

def FindLabel ([COUNTYNAME], [Shape_Area]):
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 area = str(round(float([Shape_Area]),2))
 return "<UND>" + "REGIONn" + "</UND>" + "<BOL><ITA><CLR red='125' green='105' blue='190'>" + label + "</CLR></ITA></BOL>" + "nArea: " + area + "m" + "<SUP>" + "2" + "</SUP>"

Labelling - Superscript

Other format tags are <ACP> for all capitals, <SCP> for small capitals, <CHR spacing = ‘200’> for character spacing or <CHR width = ‘150’> for character width, <WRD spacing = ‘200’> for word spacing and <LIN leading = ’25’> for line leading.

Style labels based on attributes. If the area is greater than 1,000,000,000 sq m the label will be styled like the figure above with a colour, if not it will remain black.

def FindLabel ([COUNTYNAME], [Shape_Area]):
 area = str(round(float([Shape_Area]),2))
 label = [COUNTYNAME]
 label = label.upper().replace(" ", "\n")
 if float([Shape_Area]) > 1000000000:
 return "<UND>" + "REGIONn" + "</UND>" + "<BOL><ITA><CLR red='125' green='105' blue='190'>" + label + "</CLR></ITA></BOL>" + "nArea: " + area + "m" + "<SUP>" + "2" + "</SUP>"
 else:
 return "<UND>" + "REGIONn" + "</UND>" + "<BOL><ITA>" + label + "</ITA></BOL>" + "nArea: " + area + "m" + "<SUP>" + "2" + "</SUP>"

Labelling - Attribute StylingThis has just been a quick intro into using expression and format tags for labelling. The Information was found in the online ArcGIS Help that can be found here.

Book Review: Python Scripting for ArcGIS by Paul A. Zandbergen

Title: Python Scripting for ArcGIS
Author: Paul A. Zanbergen
Publisher: ESRI Press
Year: 2013
Aimed at: Python/ArcPy – beginners, ArcGIS – knowledgeable
Purchased from: www.bookdepository.com

Python Scripting for ArcGIS

This book is a fantastic stepping stone for beginners into the enchanted world of ArcPy. ArcPy is a Python site package that provides access to the extensive set of geoprocessing tools available in ArcGIS. Besides enabling programmatic geospatial analysis ArcPy modules also facilitate data management, data conversion and map document management.

I think a quote from the Preface pages of this book aptly sums up what the book is all about.

“a little bit of code goes a long way.”

As an introductory text your eyes will be opened to how small snippets of code can run geoprocessing tools that can form the basis for extensive geospatial analysis. You won’t find in-depth spatial analysis or data management techniques but you will find an easy to read, easy to follow informative text book that provides the theory behind using Python/ArcPy and will act as a reference to the capabilities of ArcPy.

Before purchasing this book I read a number of reviews. While an overwhelming majority applauded the book there where a few who complained about the basic introduction to Python provided. Even though there is a chapter dedicated to creating Python functions and classes one review that sticks out in my mind wanted in-depth object orientated programming for GIS Python which to me is miles beyond the scope of this book. The author does a great job of providing a primer to the Python language but this is not what this book is about. There are a myriad of Python text books for beginners and also online tutorials out there and I would certainly recommend making use of these and getting comfortable with the general syntax, data structures and data types before diving head first into using Python for geospatial activities.

I bought this book because I wanted a foundation for ArcPy that I could build upon. While progressing through the text I was constantly looking to the ArcGIS Resources pages for more information about geoprocessing tools encountered and the syntax required to implement them programmatically. I would recommend using this book in tandem with the Resource pages for the ultimate beginner experience. The book is extremely informative for a beginner’s text but it will be your genuine interest in the material that will take you well beyond what’s on offer here.

The book and topics are well designed with each chapter building upon the previous. The first part introduces the Python language, development environments (PythonWIn and the Interactive Python WIndow in ArcMap), and the basics of geoprocessing. Part two is where you begin your ArcPy experience, writing scripts and learning about ArcPy modules and their capabilities. Part three introduces some specialized tasks such as automating ArcMap workflows through map scripting and error handling is also discussed. Part four provides an introduction to creating your own custom tool.

Some of the more interesting materials I found covered in this book were; working with the mapping module for automating map document tasks, accessing and manipulating data with cursors and the data access module, working with geometries and rasters, and creating custom tools. These will provide the springboard for you to dive into more advanced scripting.

Overall Verdict: The book was a great investment (c. €60). It would be hard to find a better way to introduce yourself to ArcPy. It won’t teach you everything you need to know to build applicable scripts but provides an invaluable foundation. Highly recommended for beginners.