Tip: Find Neighbor Polygons in a Layer in QGIS

Tutorial moved to http://www.qgistutorials.com/en/docs/find_neighbor_polygons.html

There are some use cases where you want to find all neighboring polygons of each of the polygons in a layer. With a little python script, we can accomplish this and much more in QGIS. Here is an example script you can use to find all polygons that touch each of the polygons in a layer and also add their names to the attribute table. A user also wanted to sum up the values of a given attribute from all the neighboring polygons, so I added that part to the script as well.

To demonstrate this, let’s take an example that given a layer of country polygons, you want to find countries that share the border and also what is the total population of the country’s neighbors.

  • Download the neighbors.py script and save it on your hard drive. 
  • Load your layer in QGIS. For this example, I am using the admin_0_countries layer from the Natural Earth dataset.
  • The script uses 2 fields to perform the action. a ‘name’ field and a field that you want to sum up. In this case the name field is ‘NAME’ and we want to sum up the population estimates from ‘POP_EST’ field. Open the neighborys.py script in a text editor like Notepad and change the field values from your layer.
  • Start the Python Console by going to Plugins → Python Console.
  • Now make sure you have selected the layer. Then run the script using the command execfile(“Path to your file”). I saved my file in D:/Data/scripts/neighbors.py, so I would run a command like execfile(“D:/Data/scripts/vertex.py”)
  • The script will take some time to run as it is comparing each polygon against ALL of the other polygons. If you get a message like QGIS is not responding, do not worry. Just let the script run and it will finish without problems. One the script finishes, open the attribute table. Voila! You now have 2 extra attribute fields in your table called ‘Neighbors’ and ‘Sum’.

Let me know if you run into problems. You will need the shapely python library installed on your system. On windows, it is part of the standard QGIS OsGeo4W installer. On Ubuntu, you can run ‘sudo apt-get install python-shapely’ to install the library.

The code in neighbors.py is as below. You can modify the code in the script to suit your needs, save it in a file and run it to perform the action.

from PyQt4.QtCore import *
from shapely.wkb import loads
# Replace the below value with the field containing name or id of the feature
# For example, if your field is called name then change the line below to
# name_field = 'name'
name_field = 'NAME'
# Replace the below value with the field name that you want to sum up
sum_field = 'POP_EST'
layer = qgis.utils.iface.activeLayer()
provider = layer.dataProvider()
# We add 2 attributes to the current layer
provider.addAttributes( [QgsField("Neighbors", QVariant.String),
                        QgsField("Sum", QVariant.Int)])
neighbor_name_index = provider.fieldNameIndex("Neighbors")
neighbor_sum_index = provider.fieldNameIndex("Sum")
allAttrs = provider.attributeIndexes()
# Select all features along with their attributes
feat = QgsFeature()
polygon_dict = {}
# Loop through all features and store their geometry and relevant attributes in
# a dictionary
while provider.nextFeature(feat):
 feature_id = feat.id()
 attrmap = feat.attributeMap()
 name = attrmap[provider.fieldNameIndex(name_field)].toString()
 sum_value = attrmap[provider.fieldNameIndex(sum_field)].toInt()[0]
 print 'Reading Geometry for %s' % name
 geom = feat.geometry()
 wkb = geom.asWkb()
 polygon = loads(wkb)
 polygon_dict[feature_id] = [ polygon, name, sum_value ]

# Now one-by-one take a feature and find all other features that touch its
# geometry
all_polygons = polygon_dict.keys()
attribute_dict = {}

for polygon_id in all_polygons:
 this_polygon, this_name, this_sum = polygon_dict[polygon_id]
 neighbor_list = []
 sum_of_neighbors = 0

 for polygon_id_compare in all_polygons:
   compare_polygon, compare_name, compare_sum = polygon_dict[polygon_id_compare]
   if this_polygon.touches(compare_polygon):
     sum_of_neighbors += compare_sum

 # Make a list of all neighbors' names
 neighbor_string = ','.join(neighbor_list)
 attribute_dict[polygon_id] = {neighbor_name_index: QVariant(neighbor_string),
                               neighbor_sum_index: QVariant(sum_of_neighbors)}

# Update the attribute table