123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- #!/usr/bin/env python
- ############################################################################
- #
- # MODULE: r3.in.xyz
- # AUTHOR: M. Hamish Bowman, Dunedin, New Zealand
- # PURPOSE: Run r.in.xyz in a loop for various z-levels and construct
- # a 3D raster. Unlike r.in.xyz, reading from stdin and z-scaling
- # won't work.
- #
- # COPYRIGHT: (c) 2011-2012 Hamish Bowman, and the GRASS Development Team
- # Port of r3.in.xyz(.sh) for GRASS 6.4.
- # This program is free software under the GNU General Public
- # License (>=v2). Read the file COPYING that comes with GRASS
- # for details.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- #############################################################################
- #%Module
- #% description: Create a 3D raster map from an assemblage of many coordinates using univariate statistics
- #% keywords: raster3d
- #% keywords: import
- #% keywords: voxel
- #% keywords: LIDAR
- #%End
- #%Flag
- #% key: s
- #% description: Scan data file for extent then exit
- #%End
- #%Flag
- #% key: g
- #% description: In scan mode, print using shell script style
- #%End
- #%Flag
- #% key: i
- #% description: Ignore broken lines
- #%End
- #%Option G_OPT_F_INPUT
- #% required: yes
- #% description: ASCII file containing input data
- #%End
- #%Option
- #% key: output
- #% type: string
- #% required: yes
- #% multiple: no
- #% key_desc: name
- #% description: Name for output raster map
- #% gisprompt: new,grid3,3d-raster
- #%End
- #%Option
- #% key: method
- #% type: string
- #% required: no
- #% multiple: no
- #% options: n,min,max,range,sum,mean,stddev,variance,coeff_var,median,percentile,skewness,trimmean
- #% description: Statistic to use for raster values
- #% answer: mean
- #% guisection: Statistic
- #%End
- #%Option
- #% key: type
- #% type: string
- #% required: no
- #% multiple: no
- #% options: float,double
- #% description: Storage type for resultant raster map
- #% answer: float
- #%End
- #%Option
- #% key: separator
- #% type: string
- #% required: no
- #% multiple: no
- #% key_desc: character
- #% description: Field separator
- #% answer: |
- #% guisection: Input
- #%End
- #%Option
- #% key: x
- #% type: integer
- #% required: no
- #% multiple: no
- #% description: Column number of x coordinates in input file (first column is 1)
- #% answer: 1
- #% guisection: Input
- #%End
- #%Option
- #% key: y
- #% type: integer
- #% required: no
- #% multiple: no
- #% description: Column number of y coordinates in input file
- #% answer: 2
- #% guisection: Input
- #%End
- #%Option
- #% key: z
- #% type: integer
- #% required: no
- #% multiple: no
- #% description: Column number of z coordinates in input file
- #% answer: 3
- #% guisection: Input
- #%End
- #%Option
- #% key: value_column
- #% type: integer
- #% required: no
- #% multiple: no
- #% label: Column number of data values in input file
- #% description: If not given or set to 0, the data points' z-values are used
- #% answer: 0
- #% guisection: Input
- #%End
- #%Option
- #% key: vrange
- #% type: double
- #% required: no
- #% key_desc: min,max
- #% description: Filter range for value column data (min,max)
- #%End
- #%option
- #% key: vscale
- #% type: double
- #% required: no
- #% multiple: no
- #% description: Scaling factor to apply to value column data
- #% answer: 1.0
- #%end
- #%Option
- #% key: percent
- #% type: integer
- #% required: no
- #% multiple: no
- #% options: 1-100
- #% description: Percent of map to keep in memory
- #% answer: 100
- #%End
- #%Option
- #% key: pth
- #% type: integer
- #% required: no
- #% multiple: no
- #% options: 1-100
- #% description: pth percentile of the values
- #% guisection: Statistic
- #%End
- #%Option
- #% key: trim
- #% type: double
- #% required: no
- #% multiple: no
- #% options: 0-50
- #% description: Discard <trim> percent of the smallest and <trim> percent of the largest observations
- #% guisection: Statistic
- #%End
- #%Option
- #% key: workers
- #% type: integer
- #% required: no
- #% multiple: no
- #% options: 1-256
- #% answer: 1
- #% description: Number of parallel processes to launch
- #%End
- import sys
- import os
- import atexit
- from grass.script import core as grass
- def cleanup():
- grass.run_command('g.mremove', flags = 'f',
- type = "rast", pattern = 'tmp.r3xyz.%d.*' % os.getpid(),
- quiet = True)
- def main():
- infile = options['input']
- output = options['output']
- method = options['method']
- dtype = options['type']
- fs = options['separator']
- x = options['x']
- y = options['y']
- z = options['z']
- value_column = options['value_column']
- vrange = options['vrange']
- vscale = options['vscale']
- percent = options['percent']
- pth = options['pth']
- trim = options['trim']
- workers = int(options['workers'])
- scan_only = flags['s']
- shell_style = flags['g']
- ignore_broken = flags['i']
- if workers is 1 and "WORKERS" in os.environ:
- workers = int(os.environ["WORKERS"])
- if not os.path.exists(infile):
- grass.fatal(_("Unable to read input file <%s>") % infile)
- addl_opts = {}
- if pth:
- addl_opts['pth'] = '%s' % pth
- if trim:
- addl_opts['trim'] = '%s' % trim
- if value_column:
- addl_opts['value_column'] = '%s' % value_column
- if vrange:
- addl_opts['vrange'] = '%s' % vrange
- if vscale:
- addl_opts['vscale'] = '%s' % vscale
- if ignore_broken:
- addl_opts['flags'] = 'i'
- if scan_only or shell_style:
- if shell_style:
- doShell = 'g'
- else:
- doShell = ''
- grass.run_command('r.in.xyz', flags = 's' + doShell, input = infile,
- output = 'dummy', sep = fs, x = x, y = y, z = z,
- **addl_opts)
- sys.exit()
- if dtype is 'float':
- data_type = 'FCELL'
- else:
- data_type = 'DCELL'
- region = grass.region(region3d = True)
- if region['nsres'] != region['nsres3'] or region['ewres'] != region['ewres3']:
- grass.run_command('g.region', flags = '3p')
- grass.fatal(_("The 2D and 3D region settings are different. Can not continue."))
- grass.verbose(_("Region bottom=%.15g top=%.15g vertical_cell_res=%.15g (%d depths)")
- % (region['b'], region['t'], region['tbres'], region['depths']))
- grass.verbose(_("Creating slices ..."))
- # to avoid a point which falls exactly on a top bound from being
- # considered twice, we shrink the
- # For the top slice we keep it though, as someone scanning the bounds
- # may have set the bounds exactly to the data extent (a bad idea, but
- # it happens..)
- eps = 1.0e-15
- # if there are thousands of depths hopefully this array doesn't get too
- # large and so we don't have to worry much about storing/looping through
- # all the finished process infos.
- proc = {}
- pout = {}
- depths = range(1, 1 + region['depths'])
- for i in depths:
- tmp_layer_name = 'tmp.r3xyz.%d.%s' % (os.getpid(), '%05d' % i)
- zrange_min = region['b'] + (region['tbres'] * (i-1))
- if i < region['depths']:
- zrange_max = region['b'] + (region['tbres'] * i) - eps
- else:
- zrange_max = region['b'] + (region['tbres'] * i)
- # spawn depth layer import job in the background
- #grass.debug("slice %d, <%s> %% %d" % (band, image[band], band % workers))
- grass.message(_("Processing horizontal slice %d of %d [%.15g,%.15g) ...")
- % (i, region['depths'], zrange_min, zrange_max))
- proc[i] = grass.start_command('r.in.xyz', input = infile, output = tmp_layer_name,
- sep = fs, method = method, x = x, y = y, z = z,
- percent = percent, type = data_type,
- zrange = '%.15g,%.15g' % (zrange_min, zrange_max),
- **addl_opts)
- grass.debug("i=%d, %%=%d (workers=%d)" % (i, i % workers, workers))
- #print sys.getsizeof(proc) # sizeof(proc array) [not so big]
- if i % workers is 0:
- # wait for the ones launched so far to finish
- for p_i in depths[:i]:
- pout[p_i] = proc[p_i].communicate()[0]
- if proc[p_i].wait() is not 0:
- grass.fatal(_("Trouble importing data. Aborting."))
- # wait for jSobs to finish, collect any stray output
- for i in depths:
- pout[i] = proc[i].communicate()[0]
- if proc[i].wait() is not 0:
- grass.fatal(_("Trouble importing data. Aborting."))
- del proc
- grass.verbose(_("Assembling 3D cube ..."))
- #input order: lower most strata first
- slices = grass.read_command('g.mlist', type = 'rast', sep = ',',
- pattern = 'tmp.r3xyz.%d.*' % os.getpid()).rstrip('\n')
- grass.debug(slices)
- if grass.run_command('r.to.rast3', input = slices, output = output) is 0:
- grass.message(_("Done. 3D raster map <%s> created.") % output)
- if __name__ == "__main__":
- options, flags = grass.parser()
- atexit.register(cleanup)
- main()
|