source: grass/branches/develbranch_6/scripts/v.rast.stats/v.rast.stats

Last change on this file was 63790, checked in by neteler, 10 years ago

v.rast.stats: don't use TRANSACTION with DBF driver (trac #2457)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id
  • Property svn:mime-type set to text/x-sh
File size: 11.4 KB
Line 
1#!/bin/sh
2
3############################################################################
4#
5# MODULE: v.rast.stats
6# AUTHOR(S): Markus Neteler, Otto Dassau
7# PURPOSE: Calculates univariate statistics from a GRASS raster map
8# only for areas covered by vector objects on a per-category base
9# COPYRIGHT: (C) 2005, 2011 by the GRASS Development Team
10#
11# This program is free software under the GNU General Public
12# License (>=v2). Read the file COPYING that comes with GRASS
13# for details.
14#
15#############################################################################
16
17#%Module
18#% description: Calculates univariate statistics from a raster map based on a vector map and uploads statistics to new attribute columns.
19#% keywords: vector, raster, statistics
20#%End
21#%flag
22#% key: c
23#% description: Continue if upload column(s) already exist
24#%END
25#%flag
26#% key: e
27#% description: Calculate extended statistics
28#%END
29#%option
30#% key: vector
31#% type: string
32#% key_desc: name
33#% gisprompt: old,vector,vector
34#% description: Name of vector polygon map
35#% required : yes
36#%End
37#%option
38#% key: layer
39#% type: integer
40#% gisprompt: old_layer,layer,layer
41#% description: A single vector map can be connected to multiple database tables. This number determines which table to use
42#% answer: 1
43#% required : no
44#%end
45#%option
46#% key: raster
47#% type: string
48#% key_desc: name
49#% gisprompt: old,cell,raster
50#% description: Name of raster map to calculate statistics from
51#% required : yes
52#%END
53#%option
54#% key: colprefix
55#% type: string
56#% description: Column prefix for new attribute columns
57#% required : yes
58#%end
59#%option
60#% key: percentile
61#% type: integer
62#% description: Percentile to calculate (requires extended statistics flag)
63#% options: 0-100
64#% answer: 90
65#% required : no
66#%end
67
68if [ -z "$GISBASE" ] ; then
69 echo "You must be in GRASS GIS to run this program." >&2
70 exit 1
71fi
72
73if [ "$1" != "@ARGS_PARSED@" ] ; then
74 exec g.parser "$0" "$@"
75fi
76
77#### check if we have awk
78if [ ! -x "`which awk`" ] ; then
79 g.message "awk required, please install awk or gawk first"
80 exit 1
81fi
82
83# setting environment, so that awk works properly in all languages
84unset LC_ALL
85LC_NUMERIC=C
86export LC_NUMERIC
87
88#### setup temporary file
89TEMPFILE="`g.tempfile pid=$$`"
90if [ $? -ne 0 ] || [ -z "$TEMPFILE" ] ; then
91 g.message -e "Unable to create temporary files"
92 exit 1
93fi
94
95### variables for temp files
96SQLTMP="$TEMPFILE.sql"
97STATSTMP="$TEMPFILE.csv"
98COLNAMETMP="$TEMPFILE.col"
99
100# we need a random name
101TMPNAME=`basename "$TEMPFILE"`
102
103cleanup()
104{
105 # restore settings:
106 g.region region="$TMPNAME"
107 g.remove region="$TMPNAME" --quiet
108 g.remove rast="${VECTOR}_$TMPNAME" --quiet
109 g.remove MASK --quiet 2>/dev/null
110 if [ $MASKFOUND -eq 1 ] ; then
111 g.message "Restoring previous MASK..."
112 g.rename "${TMPNAME}_origmask",MASK --quiet
113 fi
114 rm -f "$TEMPFILE" "$TMPNAME" "$TEMPFILE.cats" "$SQLTMP"
115}
116
117# what to do in case of user break:
118exitprocedure()
119{
120 g.message -e 'User break!'
121 cleanup
122 exit 1
123}
124# shell check for user break (signal list: trap -l)
125trap "exitprocedure" 2 3 15
126
127RASTER="$GIS_OPT_RASTER"
128COLPREFIX="$GIS_OPT_COLPREFIX"
129
130### setup enviro vars ###
131MAPSET=`g.gisenv get=MAPSET`
132
133# does map exist in CURRENT mapset?
134eval `g.findfile element=vector file="$GIS_OPT_VECTOR" mapset="$MAPSET"`
135VECT_MAPSET=`echo "$GIS_OPT_VECTOR" | grep '@' | cut -f2 -d'@'`
136if [ -z "$VECT_MAPSET" ] ; then
137 VECT_MAPSET="$MAPSET"
138fi
139if [ ! "$file" ] || [ "$VECT_MAPSET" != "$MAPSET" ] ; then
140 g.message -e "Vector map <$GIS_OPT_VECTOR> not found in current mapset"
141 exit 1
142else
143 VECTOR=`echo "$GIS_OPT_VECTOR" | cut -f1 -d'@'`
144 VECTORFULL="${VECTOR}@${VECT_MAPSET}"
145fi
146
147# check the input vector map
148eval `v.info -t map="$VECTORFULL" layer="$GIS_OPT_LAYER"`
149if [ "$areas" -eq 0 ] ; then
150 g.message -e "No areas found in vector map"
151 exit 1
152fi
153
154# check the input raster map
155eval `g.findfile element=cell file="$RASTER"`
156if [ ! "$file" ] ; then
157 g.message -e "Raster map <$RASTER> not found"
158 exit 1
159fi
160
161# check presence of raster MASK, put it aside
162MASKFOUND=0
163eval `g.findfile element=cell file=MASK`
164if [ "$file" ] ; then
165 g.message "Raster MASK found, temporarily disabled"
166 g.rename MASK,"${TMPNAME}_origmask" --quiet
167 MASKFOUND=1
168fi
169
170# get RASTER resolution of map which we want to query:
171# fetch separated to permit for non-square cells (latlong etc)
172NSRES=`r.info -s "$RASTER" | grep nsres | cut -d'=' -f2`
173if [ $? -ne 0 ] ; then
174 g.message -e "An error occurred reading the input raster map resolution."
175 cleanup
176 exit 1
177fi
178EWRES=`r.info -s "$RASTER" | grep ewres | cut -d'=' -f2`
179
180# save current settings:
181g.region save="$TMPNAME" --quiet
182
183# temporarily aligning current region to raster <$RASTER>
184g.region align="$RASTER"
185
186# create raster from vector map for r.univar
187v.to.rast in="$VECTORFULL" out="${VECTOR}_$TMPNAME" layer="$GIS_OPT_LAYER" use=cat type=area --quiet
188if [ $? -ne 0 ] ; then
189 g.message -e "An error occurred while converting vector to raster"
190 cleanup
191 exit 1
192fi
193
194# dump cats to file to avoid "too many argument" problem:
195r.category "${VECTOR}_$TMPNAME" fs=';' --quiet | cut -d';' -f1 > "$TEMPFILE.cats"
196# echo "List of categories found: $CATSLIST"
197NUMBER=`cat "$TEMPFILE.cats" | wc -l | awk '{print $1}'`
198if [ "$NUMBER" -lt 1 ] ; then
199 g.message -e "No categories found in raster map"
200 cleanup
201 exit 1
202fi
203
204# check if DBF driver used, in this case cut to 10 chars col names:
205DBFDRIVER=0
206v.db.connect -gl map="$VECTOR" fs="|" layer="$GIS_OPT_LAYER" | \
207 cut -d'|' -f5 | grep -i 'dbf' --quiet
208if [ $? -eq 0 ] ; then
209 DBFDRIVER=1
210else
211 # in case a table is missing, we'll trap a crash later...
212 DBFDRIVER=0
213fi
214
215if [ "$DBFDRIVER" -eq 1 ] ; then
216 # `wc -c` reports number of chars + 1 for the newline
217 if [ `echo "$COLPREFIX" | wc -c` -gt 10 ] ; then
218 g.message -e "Cannot create unique names for columns. \
219 Either use a shorter column prefix or switch to another DB \
220 backend such as SQLite. DBF limits the length to 10 characters"
221 cleanup
222 exit 1
223 fi
224fi
225
226# we need this for non-DBF driver:
227DB_SQLDRIVER=`v.db.connect -gl map="$VECTOR" fs="|" layer="$GIS_OPT_LAYER" | cut -d'|' -f5`
228g.message -d message="sql driver: [$DB_SQLDRIVER]"
229
230DB_DATABASE="`v.db.connect -gl map=$VECTOR fs='|' layer=$GIS_OPT_LAYER | cut -d'|' -f4`"
231g.message -d message="database: [$DB_DATABASE]"
232
233# find out which table is linked to the vector map on the given layer
234TABLE=`v.db.connect map="$VECTOR" -gl fs='|' layer="$GIS_OPT_LAYER" | cut -d'|' -f2`
235if [ -z "$TABLE" ] ; then
236 g.message -e 'There is no table connected to this map! Run v.db.connect or v.db.addtable first.'
237 exit 1
238fi
239g.message -d message="table: [$TABLE]"
240
241# get key column
242KEYCOL=`v.db.connect map="$VECTOR" -gl fs='|' layer="$GIS_OPT_LAYER" | cut -d'|' -f3`
243
244BASECOLS="n min max range mean stddev variance cf_var sum"
245
246# do extended stats?
247if [ $GIS_FLAG_E -eq 1 ] ; then
248 # namespace is limited in DBF but the % value is important
249 if [ "$DBFDRIVER" -eq 1 ] ; then
250 PERCCOL="per$GIS_OPT_PERCENTILE"
251 else
252 PERCCOL="percentile_$GIS_OPT_PERCENTILE"
253 fi
254 EXTRACOLS="first_quartile median third_quartile $PERCCOL"
255else
256 EXTRACOLS=""
257fi
258
259unset ADDCOLS
260unset COLNAMES
261for i in $BASECOLS $EXTRACOLS ; do
262 # check if column already present
263 if [ "$DBFDRIVER" -eq 1 ] ; then
264 CURRCOLUMN="`echo "${COLPREFIX}_$i" | cut -b1-10`"
265 else
266 CURRCOLUMN="${COLPREFIX}_$i"
267 fi
268 if [ -n "$COLNAMES" ] ; then
269 COLNAMES="${COLNAMES},$CURRCOLUMN"
270 else
271 COLNAMES="$CURRCOLUMN"
272 fi
273 g.message -d message="Looking for <$CURRCOLUMN> in <$VECTOR> ..."
274 v.info -c "$VECTORFULL" layer="$GIS_OPT_LAYER" --quiet | sed -e 's+^+|+' -e 's+$+|+' | \
275 grep "|$CURRCOLUMN|" --quiet
276 if [ $? -eq 0 ] ; then
277 if [ "$GIS_FLAG_C" -ne 1 ] ; then
278 g.message -e "Cannot create column <$CURRCOLUMN> (already present). \
279 Use -c flag to update values in this column."
280 cleanup
281 exit 1
282 fi
283 else
284 if [ -n "$ADDCOLS" ] ; then
285 ADDCOLS="$ADDCOLS,"
286 fi
287 if [ "$i" = "n" ] ; then
288 COLTYPE="INTEGER"
289 else
290 COLTYPE="DOUBLE PRECISION"
291 fi
292 ADDCOLS="${ADDCOLS}$CURRCOLUMN $COLTYPE"
293 fi
294done
295
296if [ -n "$ADDCOLS" ] ; then
297 g.message -v "Adding columns <$ADDCOLS>"
298# v.db.addcol map="$VECTORFULL" columns="$ADDCOLS" layer="$GIS_OPT_LAYER"
299# if [ $? -ne 0 ] ; then
300# g.message -e "Cannot continue (problem adding columns)."
301# cleanup
302# exit 1
303# fi
304
305 # code borrowed from v.db.addcol
306 colnum=`echo "$ADDCOLS" | awk -F, '{print NF}'`
307
308 n=1
309 while [ "$n" -le "$colnum" ]
310 do
311 col=`echo "$ADDCOLS" | cut -d',' -f$n`
312
313 echo "ALTER TABLE $TABLE ADD COLUMN $col" | \
314 db.execute database="$DB_DATABASE" driver="$DB_SQLDRIVER"
315 if [ $? -eq 1 ] ; then
316 g.message -e "Cannot continue (problem adding column)."
317 exit 1
318 fi
319 n=`expr $n + 1`
320 done
321fi
322
323# calculate statistics for zones:
324g.message -v "Processing data ..."
325
326# get rid of any earlier attempts
327rm -f "$SQLTMP" "$STATSTMP" "$COLNAMETMP"
328
329# Processing statistics
330unset $BASECOLS $EXTRACOLS
331if [ $GIS_FLAG_E -eq 1 ] ; then
332 r.univar -t -e map="$RASTER" \
333 zones="${VECTOR}_$TMPNAME" percentile="$GIS_OPT_PERCENTILE" | \
334 cut -f1,3,5-8,10-13,15-18 -d'|' | sed 's+nan+NULL+g' > "$STATSTMP"
335else
336 r.univar -t map="$RASTER" zones="${VECTOR}_$TMPNAME" | \
337 cut -f1,3,5-8,10-13 -d'|' | sed 's+nan+NULL+g' > "$STATSTMP"
338fi
339
340if [ $GIS_FLAG_E -eq 1 ] && [ "$DBFDRIVER" -eq 1 ] ; then
341 eval "$PERCCOL=\$percentile_$GIS_OPT_PERCENTILE"
342fi
343
344# create array with new column names
345col1="`echo $COLNAMES | cut -f1 -d','`"
346col2="`echo $COLNAMES | cut -f2 -d','`"
347col3="`echo $COLNAMES | cut -f3 -d','`"
348col4="`echo $COLNAMES | cut -f4 -d','`"
349col5="`echo $COLNAMES | cut -f5 -d','`"
350col6="`echo $COLNAMES | cut -f6 -d','`"
351col7="`echo $COLNAMES | cut -f7 -d','`"
352col8="`echo $COLNAMES | cut -f8 -d','`"
353col9="`echo $COLNAMES | cut -f9 -d','`"
354if [ $GIS_FLAG_E -eq 1 ] ; then
355 col10="`echo $COLNAMES | cut -f10 -d','`"
356 col11="`echo $COLNAMES | cut -f11 -d','`"
357 col12="`echo $COLNAMES | cut -f12 -d','`"
358 col13="`echo $COLNAMES | cut -f13 -d','`"
359fi
360
361# create SQL file for extended and normal statistics
362g.message -v "Creating SQL file ..."
363if [ "$DBFDRIVER" -eq 1 ] ; then
364 echo "" > "$SQLTMP"
365else
366 echo "BEGIN TRANSACTION;" > "$SQLTMP"
367fi
368if [ $GIS_FLAG_E -eq 1 ] ; then
369 sed -e '1d' "$STATSTMP" | awk -F "|" \
370 '{printf "\nUPDATE '${TABLE}' SET '${col1}' = %i , '${col2}' = %.15g , \
371 '${col3}' = %.15g , '${col4}' = %.15g , '${col5}' = %.15g , '${col6}' = %.15g , \
372 '${col7}' = %.15g , '${col8}' = %.15g , '${col9}' = %.15g , '${col10}' = %.15g , \
373 '${col11}' = %.15g , '${col12}' = %2f , '${col13}' = %.15g \
374 WHERE '${KEYCOL}' = %i;", $2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$1}' >> "$SQLTMP"
375else
376 sed -e '1d' "$STATSTMP" | awk -F "|" \
377 '{printf "\nUPDATE '${TABLE}' SET '${col1}' = %i , '${col2}' = %.15g , \
378 '${col3}' = %.15g , '${col4}' = %.15g , '${col5}' = %.15g , '${col6}' = %.15g , \
379 '${col7}' = %.15g , '${col8}' = %.15g , '${col9}' = %.15g \
380 WHERE '${KEYCOL}' = %i;", $2,$3,$4,$5,$6,$7,$8,$9,$10,$1}' >> "$SQLTMP"
381fi
382if [ "$DBFDRIVER" -eq 1 ] ; then
383 echo "" >> "$SQLTMP"
384else
385 echo "COMMIT;" >> "$SQLTMP"
386fi
387g.message message="Updating the database ..."
388db.execute input="$SQLTMP" database="$DB_DATABASE" driver="$DB_SQLDRIVER"
389EXITCODE=$?
390
391cleanup
392
393if [ "$EXITCODE" -eq 0 ] ; then
394 g.message "Statistics calculated from raster map <$RASTER> and uploaded to \
395 attribute table of vector map <$VECTOR>."
396 g.message "Done."
397fi
398
399exit $EXITCODE
Note: See TracBrowser for help on using the repository browser.