DM_spatial_processing.py
1 ## @package python.demo.DM_spatial_processing
2 # "
3 # example script showing how to spatially process an ODM.
4 # based on a generic neighborhood definition statistical features
5 # are extracted and attached as attributes to each point
6 # (script works in python 2 and 3)
7 #
8 
9 from __future__ import print_function # print function syntax as in python 3
10 import sys
11 import datetime
12 
13 from opals import pyDM
14 
15 
16 ##
17 # callback object computing the user-defined attributes '_pcount' and '_zmean'
18 class Kernel(pyDM.KernelPointEx):
19 
20  def __init__(self):
21  # initialize the base class (mandatory!)
22  if sys.version_info >= (3, 0):
23  super().__init__()
24  else:
25  super(Kernel, self).__init__()
26  return
27 
28  ##
29  # callback notifies about a changed leaf
30  def leafChanged(self, leaf, localTree):
31  print("process tile with id = %d" % leaf.id() )
32  return
33 
34  ##
35  # callback notifies that all leaf handels should be released
36  def releaseLeaf(self):
37  return
38 
39  ##
40  # callback for processing a point
41  def process(self, pt, neighbours):
42 
43  # add neighbour count (_pcount) and z mean of neighbours (_zmean) to the current point
44  zsum = 0
45  for n in neighbours.points():
46  zsum += n.z
47 
48  # numpyDict = neighbours.asNumpyDict() # get neighbours as numpy dict
49 
50  pt.info().set(0, neighbours.sizePoint())
51  if neighbours.sizePoint() > 0:
52  zmean = zsum / float(neighbours.sizePoint())
53  pt.info().set(1, zmean)
54 
55  # return True if the point was changed otherwise False
56  # this internally marks the current point leaf as changed (=needs to be written to disk)
57  return True
58 
59 
60 def DM_spatial_processing(filename, maxOutput=1000):
61  # pyDM.Datamanager.load parameter: filename(string), readOnly(bool) threadSafety(bool)
62  dm = pyDM.Datamanager.load(filename, False, False)
63  if not dm:
64  print("Unable to open ODM '" + odm + "'")
65  sys.exit(1)
66 
67  # creates the appropriate attribute layout for processing
68  lf = pyDM.AddInfoLayoutFactory()
69  lf.addColumn(pyDM.ColumnType.int32, "_pcount") # column 0
70  lf.addColumn(pyDM.ColumnType.float_, "_zmean") # column 1
71  layout = lf.getLayout()
72  kernelLayout = lf.getLayout()
73 
74 
75  # create spatial selection
76  query = pyDM.QueryDescriptor("sphere(r=1)") # use a sphere with radius 1m as neighbourhood definition (textual description)
77 
78  # sphere = pyDM.QuerySphere(r=1) # sphere with radius 1m (class based description)
79  # knn = pyDM.QueryKnn(k=12,dim=3) # 3d knn with 12 neighbours (class based description)
80  # query = pyDM.QueryDescriptor(sphere) # create actual neighbourhood definition: fixed distance
81  # query = pyDM.QueryDescriptor(knn & sphere) # create actual neighbourhood definition: knn with a maximum search radius
82 
83  print("Ready to append attribute '_pcount' and '_zmean' to '%s'" % filename)
84 
85  print("Number of tiles to process = %d" % dm.getPointIndex().sizeLeaf())
86 
87  # create kernel object
88  k = Kernel()
89 
90  start = datetime.datetime.now()
91  # create processor
92  # pyDM.ProcessorEx parameter: dm(datamnager), query(QueryDescriptor),
93  # processFilter, processLayout, processLayoutReadOnly,
94  # neighbourFilter, neighbourLayout, neighbourLayoutReadOnly
95  processor = pyDM.ProcessorEx(dm, query, None, layout, False, None, None, False)
96  processor.run(k) # perform computation
97 
98  diff = datetime.datetime.now() - start
99  print("Processing took %.2f [s]" % diff.total_seconds() )
100 
101  # save manager object
102  print("Save manager")
103  dm.save()
104 
105  print("done")
106 
107 if len(sys.argv) == 1:
108  print("ODM parameter missing")
109  sys.exit(-1)
110 
111 DM_spatial_processing(sys.argv[1])