Batch count vector features intersecting series of blocks in PostgreSQL
Posted by: Admire Nyakudya | in Database | 2 years, 7 months ago | Comments

The national mapping agency in South Africa (NGI ) caters for national mapping, digital topographic and other Geo-Spatial Information services. The department is mandated to provide spatial data for the whole country. In most cases, they have an in-house team that is responsible for digitizing aerial photography, field validation to produce vector data that is used to make 1:50 000 topographic maps.

The department has recently floated a tender in which they encourage service providers to participate in the mapping exercise for some other parts of the country. The department is mandated to update all the spatial data on a 5-year cycle. As an interested party, I set out to find how much work would be involved in mapping these areas based on data available for these areas.

The current coverage of areas that need to be mapped is:


I extracted all the sheets numbers from the supplied bid documents and created a text file which I then loaded into QGIS. I then imported this layer into a PostgreSQL database and called it sample. 

I then proceeded to run the following SQL:


SELECT,b.sheet_number,b.sheet_name, b.geom FROM

"index1in50k" as b JOIN sample as a ON

b.sheet_number =;

The SQL above created a spatial layer indicating all blocks that needed to be captured.

In my database, I had already loaded the spatial layers I had acquired the previous year into a schema called ngi. The schema contained about 40 odd layers. I set out to find the number of features from each layer in the schema ngi intersecting each block specified in the table ngi_work.

I wrote the resulting SQL to test how many features intersected a block.

        WITH clipping_test AS
            ST_Multi(ST_Intersection(st_transform(v.wkb_geometry,4326),m.geom)) AS intersection_geom,
              ngi.phys_landform_natural_exp_areal as v,
             (select sheet_number,st_union(geom) as geom from ngi_work where sheet_number = '2328BD' group by sheet_number) 
             as m
              ST_Intersects(st_transform(v.wkb_geometry,4326), m.geom))
            (SELECT count(*) FROM clipping_test WHERE ST_Geometrytype(intersection_geom)='ST_MultiPolygon')

After running the SQL I figured out that it could run it successfully and now I had to script the whole procedure so that it could run for all the blocks and for all the layers that are in the schema ngi.

The full script to do this is attached below.

import psycopg2
import sys
import csv

def main():
# Define our connection string
connection = psycopg2.connect(host='localhost', database='foo', user='fool', password='foobar',
except psycopg2.OperationalError as e:

cursor = connection.cursor()

# SQL to query the ngi_work table getting all sheets to be used
check_value = """ select sheet_number from ngi_work """
valid_sheets = cursor.fetchall()
for sheet in valid_sheets:
index_block = sheet[0]
cursor = connection.cursor()
# Retrieve all the spatial layers from the schema ngi
""" select f_table_name,f_geometry_column,f_table_schema from geometry_columns where f_table_schema = 'ngi';
# retrieve the records from the database
rows = cursor.fetchall()
for row in rows:
ngi_table = row[0]
geom_field_name = row[1]
db_schema = row[2]
cursor = connection.cursor()
source_geom_type = """select ST_GeometryType(%s) from %s.%s""" % (geom_field_name, db_schema, ngi_table)
row = cursor.fetchone()
# Define geometry type ie ST_Multilinestring
geo_type = row[0]
# Create SQL to count records
layer_sql = ("""
WITH clipping_test AS
ST_Multi(ST_Intersection(st_transform(v.%s,4326),m.geom)) AS intersection_geom,
%s.%s as v,
(select sheet_number,st_union(geom) as geom from ngi_work where sheet_number = '%s' group by sheet_number)
as m
ST_Intersects(st_transform(v.%s,4326), m.geom))
(SELECT count(*) FROM clipping_test WHERE ST_Geometrytype(intersection_geom)='%s')
""") % (
geom_field_name, db_schema, ngi_table, index_block, geom_field_name, geo_type
cursor = connection.cursor()
feature_count = cursor.fetchone()[0]
feature_row = [index_block, ngi_table, feature_count]
with open('/tmp/features.csv', 'a') as csvFile:
writer = csv.writer(csvFile)

if __name__ == "__main__":
Currently unrated


Have any questions? Contact us.
We would love to hear from you!
Subscribe to our Mailing list (spam-free, industry-relevant occasional emails)

Subscribe to our mailing list

* indicates required