Ticket #21273: oracle_xe_sdo_geometry.py

File oracle_xe_sdo_geometry.py, 10.2 KB (added by vincent.hussey@…, 8 years ago)

Initial class for handling SDO_GEOMETRY

Line 
1class sdo_geometry(object):
2    '''
3    Process an Oracle SDO_GEOMETRY object as returned by cx_Oracle.
4    '''
5    # definitions
6    geom_types = {'00': 'UNKNOWN_GEOMETRY', # UNKNOWN_GEOMETRY
7                  '01': 'POINT', # POINT
8                  '02': 'LINESTRING', # LINE or CURVE
9                  '03': 'POLYGON', # POLYGON
10                  '04': 'GEOMETRYCOLLECTION', # COLLECTION
11                  '05': 'MULTIPOINT', # MULTIPOINT
12                  '06': 'MULTILINESTRING', # MULTILINE or MULTICURVE
13                  '07': 'MULTIPOLYGON'} # MULTIPOLYGON
14   
15    # SDO_ETYPES
16    # first element of triplet in SDO_ELEM_INFO
17    sdo_etype = {0: 'UNSUPPORTED_GEOMETRY_ETYPE',
18                 1: 'POINT_ETYPE',
19                 2: 'LINE_ETYPE', 
20                 4: 'COMPOUND_LINESTRING_ETYPE', 
21                 1003: 'EXTERIOR_CLOSED_SHAPE_ETYPE', 
22                 2003: 'INTERIOR_CLOSED_SHAPE_ETYPE',
23                 1005: 'COMPOUND_EXTERIOR_CLOSED_SHAPE_ETYPE', 
24                 2005: 'COMPOUND_INTERIOR_CLOSED_SHAPE_ETYPE'}
25   
26    # SDO_INTERPRETATIONS
27    # second element of triplet in SDO_ELEM_INFO
28    # applies to points - sdo_etype 1
29    sdo_interpretation_point = {0: 'ORIENTED_POINT', 
30                                1: 'SIMPLE_POINT'
31                                # n > 1: point cluster with n points
32                                }
33   
34    # applies to lines - sdo_etype 2
35    sdo_interpretation_line = {1: 'STRAIGHT_SEGMENTS', 
36                               2: 'CURVED_SEGMENTS'}
37   
38    # applies to polygons - sdo_etypes 1003 and 2003           
39    sdo_interpretation_multi = {1: 'SIMPLE_POLY', 
40                                2: 'ARCS_POLY', 
41                                3: 'RECTANGLE', 
42                                4: 'CIRCLE'} 
43   
44    # complex geometries - sdo_etypes 4, 1005, 2005 always have n > 1
45    # n is the number of contiguous subelements
46    # subsequent subelements each define one element
47   
48   
49       
50    # init function
51    def __init__(self, sdo_geometry_obj=None, debug=False, strict=False):
52        '''
53        Read the geometry from the sdo_geometry object.
54        Debug - produce some debug output.
55        Strict - if False (default), convert geometry to a supported type where possible,
56        e.g. Oriented Point to Point
57        '''
58        # read the geometry from the sdo_geometry object
59        self.geometry = sdo_geometry_obj
60        try:
61            self.g_type = str(int(self.geometry.__getattribute__('SDO_GTYPE')))
62            self.g_srid = int(self.geometry.__getattribute__('SDO_SRID'))
63            self.g_point = [self.geometry.__getattribute__('SDO_POINT').X, self.geometry.__getattribute__('SDO_POINT').Y, self.geometry.__getattribute__('SDO_POINT').Z]
64            self.g_eleminfo_arr = self.geometry.__getattribute__('SDO_ELEM_INFO')
65            self.g_ords_arr = self.geometry.__getattribute__('SDO_ORDINATES')
66        except AttributeError:
67            if debug:
68                print 'Not a geometry'
69            return None
70        self.dims = self.get_dims()
71        self.topology = self.has_topology()
72        self.gtype = self.get_gtype()
73        #self.wkb = self.get_wkb()
74        self.valid = False
75        self.wkt = self.get_wkt()
76        #self.coord_dim = self.st_coorddim()
77        #self.is_valid = self.st_isvalid()
78   
79    # functions
80    def get_dims(self):
81        '''
82        Return dimensions of the geometry.
83        This is extracted from the first character of the SDO_ETYPE value
84        '''
85        return int(self.g_type[0])
86   
87    def st_coorddim(self):
88        '''
89        Return dimensions of the geometry.
90        This is extracted from the first character of the SDO_ETYPE value
91        This function is a synonym of get_dims
92        '''
93        return self.get_dims()
94   
95    def has_topology(self):
96        '''
97        Return true if the geometry has topology, false if it doesn't, otherwise None
98        This is extracted from the second character of the SDO_ETYPE value
99        '''
100        if 0 <= int(self.g_type[1]) <= 1:
101            return int(self.g_type[1])
102        else:
103            return None
104           
105    def get_geometry_text(self):
106        '''
107        Return the type of geometry.
108        This is extracted from the third and fourth characters of the SDO_ETYPE value
109        '''
110        return self.geom_types[self.g_type[2:4]]
111       
112    def get_gtype(self):
113        '''
114        Return the type of geometry.
115        This is extracted from the third and fourth characters of the SDO_ETYPE value
116        '''
117        return int(self.g_type[2:4])
118               
119    def get_srid(self):
120        '''
121        Return the srid of the data.
122        This is as defined in the database and may be an Oracle specific format (not EPSG).
123        '''
124        return self.g_srid   
125   
126    def get_num_elements(self):
127        '''
128        Return the total number of elements in the SDO_ORDINATES array.
129        These may be used more than once (end and start adjacent elements).
130        '''
131        if self.g_eleminfo_arr:
132            return len(self.g_eleminfo_arr)
133        else:
134            return None
135           
136    def get_etype(self):
137        '''
138        Return the SDO_ETYPE value, if it is defined.
139        '''
140        if not self.g_eleminfo_arr:
141            return None
142        else:
143            return int(self.g_eleminfo_arr[1])
144
145    def get_interpretation(self):
146        '''
147        Return the SDO_INTERPRETATION value, if it is defined.
148        '''
149        if not self.g_eleminfo_arr:
150            return None
151        else:
152            return int(self.g_eleminfo_arr[2])
153           
154    def get_point_text(self, point):
155        '''
156        Convert a point (2d or 3d list) into WKT text.
157        '''
158        if self.dims == 2:
159            return '%.12f %.12f' % (point[0], point[1])
160        else:
161            return '%.12f %.12f %.12f' % (point[0], point[1], point[2])
162           
163    def to_points(self, l,n):
164        '''
165        Convert a list l into a list of smaller lists of dimension n.
166        '''
167        return [l[i:i+n] for i in xrange(0, len(l), n)]
168       
169    def is_valid(self):
170        '''
171        Return True for valid geometry, False for invalid geometry, None for unsupported
172        '''
173        # Place holder for now.
174        return None
175
176    def get_wkt(self):
177        '''
178        Calculate the WKB for the geometry.
179        Point geometry may require only SDO_POINT, all other geometries require the use of SDO_ELEM_INFO and SDO_ORDINATES.
180        Geometry may be simple or complex.  Simple geometries are defined in one SDO_ELEM_INFO triplet, Complex geometries require multiple SDO_ELEM_INFO triplets.
181        '''
182        geom_type = self.get_geometry_text()
183        if geom_type == 'UNKNOWN_GEOMETRY':
184            return None
185       
186        elif geom_type == 'POINT':
187            if self.g_point:
188                #return 'SRID=%d:%s(%s)' % (get_srid(g_srid), geom_types[geometry_type], get_point_text(g_point, dims))
189                point_text = '%s(%s)' % (geom_type, self.get_point_text(self.g_point))
190                self.valid = True
191                return 'SRID=%d:%s' % (self.get_srid(), point_text)
192            else:
193                pass # need to extract point from sdo_ordinates
194                # case 1 - simple point
195                # case 2 - oriented point
196       
197        elif geom_type == 'LINESTRING':
198            # simple element, with a single SDO_ELEM_INFO triplet.
199            # each point is listed sequentially in the SDO_ORDINATES
200            # direct conversion to WKT
201           
202            # sanity check - may need to expand
203            if self.get_etype() != 2 or len(self.g_eleminfo_arr) != 3:
204                self.valid = False
205                return None
206            # straight segments
207            if self.get_interpretation() == 1 or (self.get_interpretation() == 2 and strict == False):
208                points = self.to_points(self.g_ords_arr, self.dims)
209                ls_body = ''
210                for point in points:
211                    ls_body += self.get_point_text(point)+','
212                ls_body = ls_body[:-1]
213                ls_text = '%s(%s)' % (geom_type, ls_body)
214                self.valid = True
215                return 'SRID=%d:%s' % (self.get_srid(), ls_text)
216            # curved segments
217            elif self.get_interpretation() == 2 and strict == True:
218                # to do
219                return None
220           
221        elif geom_type == 'POLYGON':
222            # simple element, with a single SDO_ELEM_INFO triplet.
223            # the triple can have the following values
224            # [0] - 1003 or 2003 (SDO_ETYPE)
225            # [1] - 1, 2, 3, or 4 (SDO_INTERPRETATION)
226            # [2] - 1
227            # each point is listed sequentially in the SDO_ORDINATES
228            # last point is the same as first point
229            # direct conversion to WKT
230           
231            # sanity check - may need to expand
232            if (self.get_etype() != 1003 and self.get_etype() != 2003) or len(self.g_eleminfo_arr) != 3:
233                self.valid = False
234                return None
235            # straight segments
236            if self.get_interpretation() == 1 or (self.get_interpretation() < 4 and strict == False):
237                points = self.to_points(self.g_ords_arr, self.dims)
238                poly_body = ''
239                for point in points:
240                    poly_body += self.get_point_text(point)+','
241                poly_body = poly_body[:-1]
242                poly_text = '%s(%s)' % (geom_type, poly_body)
243                self.valid = True
244                return 'SRID=%d:%s' % (self.get_srid(), poly_text)
245            # curved segments
246            #elif self.get_interpretation() == 2 and strict == True:
247                # to do
248            #    return None
249           
250        elif geom_type == 'GEOMETRYCOLLECTION':
251            pass 
252           
253        elif geom_type == 'MULTIPOINT':
254            pass 
255           
256        elif geom_type == 'MULTILINESTRING':
257            pass 
258           
259        elif geom_type == 'MULTIPOLYGON':
260            pass 
261        else:
262            return None
263   
Back to Top