Spatial feature count

Suppose you have two vectors: one that contains points associated with surveyed sites, and another made of areas that represent fields or different regions of survey. You want to know how many sites are in each area and draw a map that brings this information.

Here are some result maps made with the tool d.vect.chart described in this page:

Barcharts with d.vect.chart based on the output of the script

Piecharts with d.vect.chart based on the output of the script

If your sites are classified into different types or classes (based on period or size), you might want also to know what distribution different classes have for each of your survey areas.

With GRASS

This is a shell script for GRASS that calls v.what.vect to know which points fall into each area. Counts for each area are written to a new column "count". This attribute can be used as input for d.vect.chart (bar chart).

You can you also provide a column name for the points vector, that contains a classification attribute. This way more columns are created, to be used for creating pie-charts. When you use pie-charts, the "count" column can be used to generate different sizes of graphics proportional to the total number of points in each area.

This script is quite slow. If someone with enough skills (not me) wants to write a better module, I think significant parts of code from v.qcount can be useful. It would be good to have such a feature included by default in GRASS, with an efficient and fast code.

To run the script, cd into the directory that contains the script from the GRASS prompt and call it (it's useful to chmod 755 v.count.points.sh).

#!/bin/sh
#
############################################################################

#
# MODULE:       v.count.points
#
# AUTHOR(S):    Stefano Costa steko@iosa.it
#
# PURPOSE:      generate summary statistics for point features 
# COPYRIGHT:    (C) 2007 by the GRASS Development Team
#
#               This program is free software under the GNU General Public
#               License (>=v2). Read the file COPYING that comes with GRASS
#               for details.
#
##
#############################################################################
#
#
#%Module
#% description: count the number of point features from vector A that are within areas from vector B
#%End
#%option
#% key: points
#% type: string
#% gisprompt: old,vector,vector
#% description: points vector to be counted for each area
#% required : yes
#%end
#%option
#%  key: polygon
#%  type: string
#%  description: polygon vector
#%  gisprompt: old,vector,vector
#%  required : yes
#%end
#%option
#% key: output
#% type: string
#% description: output layer
#% required : yes
#%end
#%option
#% key: classescol
#% type: string
#% description: column that contains classification for points
#% required : no
#%end


if  [ -z "$GISBASE" ] ; then
    echo "You must be in GRASS GIS to run this program."
 exit 1
fi

if [ "$1" != "@ARGS_PARSED@" ] ; then
  exec g.parser "$0" "$@"
fi

### setup enviro vars ###
eval `g.gisenv`
: ${GISBASE?} ${GISDBASE?} ${LOCATION_NAME?} ${MAPSET?}
LOCATION=$GISDBASE/$LOCATION_NAME/$MAPSET

points=$GIS_OPT_points
polygon=$GIS_OPT_polygon
output=$GIS_OPT_output

tempsql=`g.tempfile pid=$$`


# overwrites the output map if it already exists
g.copy vect=$points,temppnts,$polygon,$output --o

if [ -n "$GIS_OPT_classescol" ]; then
    classescol=$GIS_OPT_classescol
    echo "Using classes!"
    classes=$(db.select -c temppnts sql="select $classescol from temppnts" | uniq)
    for j in $classes; do
        v.db.addcol $output col="class_$j int"
    done
fi

v.db.addcol temppnts col="cat_area int"
v.db.addcol $output col="count int"
v.what.vect vector=temppnts qvect=$output column=cat_area qcolumn=cat

areas=$(db.select -c $output sql="select cat from $output" | uniq)

for i in $areas; do
    count=$( db.select -c temppnts sql="select cat_area from temppnts" | grep -c "^$i$")
    echo "UPDATE $output SET count = $count WHERE cat = $i;" >> $tempsql
    if [ -n "$GIS_OPT_classescol" ]; then
    for j in $classes; do
        kount=$( db.select -c temppnts sql="select $classescol from temppnts where cat_area = $i" | grep -c "^$j$")
        echo "UPDATE $output SET class_$j = $kount WHERE cat = $i;" >> $tempsql
    done
    fi
done

cat $tempsql | db.execute

g.remove vect=temppnts

echo "Output written to map $output"

With PostGIS

Flavio Rigolon suggested this approach using PostGIS:

dbname => CREATE TABLE new_table AS SELECT grid.cat, COUNT(id) AS count FROM points,grid WHERE points.the_geom && grid.the_geom AND CONTAINS (grid.the_geom,points.the_geom) GROUP BY grid.cat;

You should obtain a table. You can then JOIN new_table to your grid table using the cat column.

Updated: