Ticket #21273: oracle_xe_sdo_geometry.py

File oracle_xe_sdo_geometry.py, 10.2 KB (added by vincent.hussey@…, 11 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