File: oci2las.py

package info (click to toggle)
liblas 1.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 7,888 kB
  • ctags: 4,614
  • sloc: cpp: 31,630; xml: 4,195; python: 2,928; ansic: 2,439; cs: 2,411; sh: 143; makefile: 37
file content (347 lines) | stat: -rwxr-xr-x 11,732 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#!/usr/bin/env python

from liblas import file as lasfile
from liblas import header
from liblas import point
from liblas import srs

import glob
import struct

import os, sys


import cx_Oracle as oci

# big-endian DOUBLE, DOUBLE, DOUBLE, LONG, LONG
format = '>dddddll'
ptsize = struct.calcsize(format)


class Translator(object):

    def construct_parser(self):
        from optparse import OptionParser, OptionGroup
        usage = "usage: %prog [options] arg"
        parser = OptionParser(usage)
        g = OptionGroup(parser, "Base options", "Basic Translation Options")
        g.add_option("-c", "--connection", dest="connection",
                          help="OCI connection string", metavar="CONNECTION")
        g.add_option("-o", "--output", dest='output',
                          help="LAS file to write", metavar="OUTPUT")
        g.add_option("-s", "--sql", dest='sql',
                          help="SQL to select the point cloud ", metavar="SQL")
        g.add_option("-l", "--column", dest='column',
                          help="Column name containing the point cloud object ", metavar="COLUMN")

        g.add_option("-p", "--precision", dest='precision',
                          help="Numeric precision (# of digits) to maintain for the output file ", metavar="PRECISION")

        g.add_option("-r", "--srs", dest='srs',
                          help="Coordinate system override", metavar="SRS")
                          
        g.add_option("-w", "--overwrite",
                          action="store_true", dest="overwrite", 
                          help="overwrite the existing file")

        g.add_option("-m", "--min-offset",
                          action="store_true", dest="offset", default=False,
                          help="Use the minimum values as base for offset")
                          
        g.add_option("-q", "--quiet",
                          action="store_false", dest="verbose", default=False,
                          help="Don't say what we're doing on stdout")
        
        g.add_option("--compressed", action="store_true", dest="compressed",
                          default=False)
                  
        parser.add_option_group(g)

        if self.opts:
            g = OptionGroup(parser, "Special Options", "Special options")
            for o in self.opts:
                g.add_option(o)
            parser.add_option_group(g)
            
        parser.set_defaults(verbose=True, precision = 6)

        self.parser = parser
        
    def __init__(self, arguments, options=None):
        self.connection = None
        self.output = None
        self.sql = None
        
        self.opts = options
        self.construct_parser()
        self.options, self.args = self.parser.parse_args(args=arguments)
        
        if self.args:
            self.options.connection = self.args[0]
            
        if not self.options.output:
            try:
                self.options.output = self.args[1]
            except IndexError:
                self.options.output = 'output.las'
                
        if not self.options.sql:
            try:
                self.options.sql = self.args[2]
            except IndexError:
                raise self.parser.error("No SQL was provided to select the point cloud!")           

        if self.options.output:
            self.options.output = os.path.abspath(self.options.output)
            if os.path.isdir(self.options.output):
                raise self.parser.error("Output '%s' is a directory, not a file " % self.options.output)
            
            if os.path.exists(self.options.output):
                if not self.options.overwrite:
                    raise self.parser.error("Output file '%s' exists, but you have not selected the --overwrite option" % self.options.output)
        else:
            raise self.parser.error("No output was specified")

        try:
            self.options.precision = int(self.options.precision)
            if not self.options.precision:
                raise self.parser.error("Precision cannot be 0")
        except:
            raise self.parser.error("Precision was not an number")
            
        self.minx = None
        self.miny = None
        self.minz = None
        self.maxx = None
        self.maxy = None
        self.maxz = None
        self.count = 0
        self.first_point = True
        self.cloud_column = True
        self.header = None
        self.points = []
        
        if self.options.srs:
            self.srs = srs.SRS()
            self.srs.set_userinput(self.options.srs)
            print 'setting srs to %s' %self.srs.proj4
        else:
            self.srs = None
            
    def print_options(self):
        print self.options
        
    def connect(self):
        self.con = oci.Connection(self.options.connection)

    def is_block_table(self, cursor_description):
        output = True
        names = [   'OBJ_ID','BLK_ID','BLK_EXTENT','BLK_DOMAIN',
                    'PCBLK_MIN_RES','PCBLK_MAX_RES','NUM_POINTS',
                    'NUM_UNSORTED_POINTS','PT_SORT_DIM','POINTS']
        for name in cursor_description:
            if name.upper() not in names:
                return False

            
    def write_points(self, num_points, blob):
#	print 'writing block...', num_points

        if not self.points:
            for i in xrange(num_points):
                p = point.Point()
                p.header = self.header
                self.points.append(p)
        if (num_points > len(self.points)):
            for i in xrange(num_points-len(self.points)):
                p = point.Point()
                p.header = self.header
                self.points.append(p)
                    
        for i in xrange(num_points):
            rng = ptsize*i,ptsize*(i+1)
            d = struct.unpack(format,blob[ptsize*i:ptsize*(i+1)])
            x, y, z, time, classification, blk_id, pt_id = d
            p = self.points[i]
            p.x = x; p.y = y; p.z = z
            p.classification = int(classification)
            p.raw_time = time

            if self.first_point:
                self.minx = p.x
                self.miny = p.y
                self.maxx = p.x
                self.maxy = p.y
                self.minz = p.z
                self.maxz = p.z
                self.first_point = False

            # cumulate min/max for the header
            self.minx = min(self.minx, p.x)
            self.maxx = max(self.maxx, p.x)
        
            self.miny = min(self.miny, p.y)
            self.maxy = max(self.maxy, p.y)
        
            self.minz = min(self.minz, p.z)
            self.maxz = max(self.maxz, p.z)
        
            self.count += 1
            
            self.output.write(p)
    
    def summarize_files(self):
        pass
    
    def open_output(self):
        self.header = header.Header()
        
        prec = 10**-(self.options.precision-1)
        self.header.scale = [prec, prec, prec]
        
        if self.options.offset:
            h.offset = [self.minx, self.miny, self.minz]
            if self.options.verbose:
                print 'using minimum offsets', h.offset

        self.header.compressed = self.options.compressed
 
        if self.srs:
            self.header.srs = self.srs
        
        self.header.data_format_id = 1
        output = lasfile.File(self.options.output,mode='w',header=self.header)
        return output
    
    
    def rewrite_header(self):
        self.output.close()
        self.output = lasfile.File(self.options.output)
        h = self.output.header
        self.output.close()
        h.min = [self.minx, self.miny, self.minz]
        h.max = [self.maxx, self.maxy, self.maxz]

        rc = h.point_return_count
        rc[0] = self.count
        h.point_return_count = rc
        
        self.output = lasfile.File(self.options.output, mode='w+', header=h)
        self.output.close()
    
    def get_srid(self, srid):
        cur = self.con.cursor()
        if not srid: return ''
        cur.execute('SELECT WKTEXT,WKTEXT3D from MDSYS.CS_SRS where srid=%d'%(int(srid)))
        res = cur.fetchall()
        for wkt in res:
            text = wkt[0]
            text3d = wkt[1]
            s = srs.SRS()
            if text3d:
                s.wkt = text3d
            else:
                s.wkt = text
            return s
        
    def process(self):
        self.print_options()
        self.connect()

        self.cur = self.con.cursor()
#        self.cur.execute(self.options.sql)
#        clouds = []
#
#        res = self.cur.fetchall()

#        for row in res:
#            for column in row:
#                try:
#                    column.BASE_TABLE_COL
#                    clouds.append(column)
#                except AttributeError:
#                    # This column isn't a cloud
#                    pass

#        points = []
        
        # write an actual cloud column
#        for cloud in clouds:
#            cur2 = self.con.cursor()
#
#            cur2.execute('SELECT NUM_POINTS, POINTS, BLK_EXTENT FROM %s'% cloud.BLK_TABLE)
            
            
#            for num_points, blob, extent in cur2:
#                # set the SRS from the first geometry
#                if not self.srs:
#                    self.srs = self.get_srid(extent.SDO_SRID)
                    
#                b = blob.read()
#                points.append(self.get_points(num_points,b))
        
#        num_pts_index, blob_index = self.get_block_indexes(self.cur)
        
        # if we don't have a cloud object, we'll assume that NUM_POINTS and 
        # the POINTS blob exist in our already queried cursor
        clouds = None
        points = []
        print 'have srs?: %s' % bool(self.srs)

        self.output = self.open_output()

        if not clouds:
            cur = self.cur.execute(self.options.sql)
            num_pts_index, blob_index = self.get_block_indexes(cur)
            for row in cur:
                num_points = row[num_pts_index]
                blob = row[blob_index].read()
                self.write_points(num_points, blob)
                # try to set the SRS
                if not self.srs:
                    for col in row:
                        try:
                            col.SDO_SRID
                            self.srs = self.get_srid(col.SDO_SRID)
                            break
                        except AttributeError:
                            continue
                    
                    # if we still haven't been able to set an SRS, don't try 
                    # to anymore
                    if not self.srs:
                        self.srs = srs.SRS()

        
        self.rewrite_header()

    def get_block_indexes(self, cursor):
        
        num_pts_index = None
        blob_index = None
        i = 0
        for name in cursor.description:
            name = name[0]
            if name.upper() == 'POINTS':
                blob_index = i
            if name.upper() == 'NUM_POINTS':
                num_pts_index = i
            i+=1
        return (num_pts_index, blob_index)

def main():
    import optparse

    options = []
#     o = optparse.make_option("-r", "--remainder", dest="remainder",
#                          type="choice",default='end', 
#                           help="""what to do with the remainder -- place it at the beginning, 
# place it at the end, or evenly distribute it across the segment""",
#                           choices=['end','begin','uniform'])
#     options.append(o)
    
    d = Translator(sys.argv[1:], options=options)
    d.process()

if __name__=='__main__':
    main()