/[thuban]/branches/WIP-pyshapelib-bramz/Extensions/ogr/ogrshapes.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Extensions/ogr/ogrshapes.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2577 - (show annotations)
Fri Mar 4 15:07:59 2005 UTC (20 years ago) by nhueffme
Original Path: trunk/thuban/Extensions/ogr/ogrshapes.py
File MIME type: text/x-python
File size: 21614 byte(s)
Latest changes on OGR extension.

Added another menu item to open OGR datasource, handling of geometry collections
possible.

1 # Copyright (C) 2004 by Intevation GmbH
2 # Authors:
3 # Nina Hueffmeyer <[email protected]>
4 #
5 # This program is free software under the GPL (>=v2)
6 # Read the file COPYING coming with the software for details.
7
8 __version__ = "$Revision$"
9 # $Source$
10 # $Id$
11
12 from __future__ import generators
13
14 try:
15 import ogr
16 except ImportError:
17 ogr = None
18
19 import os
20
21 from Thuban import _
22 from Thuban.Model import table
23 from Thuban.Model import transientdb
24 from Thuban.Model.transientdb import TransientDatabase
25
26 from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
27 from Thuban.Model.data import RAW_PYTHON, RAW_SHAPEFILE, RAW_WKT
28
29 def has_ogr_support():
30 """Return whether this Thuban instance supports ogr file formats
31
32 Having OGR support means that the ogr module can be
33 imported.
34 """
35 return ogr is not None
36
37 if ogr is not None:
38 SHAPETYPE_UNKNOWN = ogr.wkbUnknown
39 SHAPETYPE_GEOMCOLL = ogr.wkbGeometryCollection
40 SHAPETYPE_NONE = ogr.wkbNone
41
42 # mapping from ogr-lib shapetypes and table constants to our constants
43 ogrlib_shapetypes = {ogr.wkbPolygon: SHAPETYPE_POLYGON,
44 ogr.wkbLineString: SHAPETYPE_ARC,
45 ogr.wkbPoint: SHAPETYPE_POINT,
46 ogr.wkbUnknown: SHAPETYPE_UNKNOWN,
47 ogr.wkbNone: SHAPETYPE_NONE,
48 ogr.wkbGeometryCollection: SHAPETYPE_GEOMCOLL}
49
50 fieldtype_map = {ogr.OFTString: table.FIELDTYPE_STRING,
51 ogr.OFTInteger: table.FIELDTYPE_INT,
52 ogr.OFTReal: table.FIELDTYPE_DOUBLE}
53
54 else:
55 ogrlib_shapetypes = {}
56 fieldtype_map = {}
57 SHAPETYPE_UNKNOWN = 0
58 SHAPETYPE_GEOMCOLL = 7
59 SHAPETYPE_NONE = 100
60
61
62 class OGRShape:
63 """Represent one shape of an OGRShapeStore
64 """
65
66 def __init__(self, shapestore, shape):
67 """Initialize the shape object.
68
69 shapestore should be an instance of OGRShapeStore,
70 shape should be an instance of an OGRFeature.
71 """
72 self.ogrlayer = shapestore.ogrlayer
73 id_column = shapestore.Id_column()
74 self.feature = shape
75 if id_column is None:
76 self.shapeid = self.feature.GetFID()
77 else:
78 self.shapeid = self.feature.GetField(id_column)
79 self.geom = self.feature.GetGeometryRef()
80 if self.geom:
81 self.shapetype = self.geom.GetGeometryType()
82 self.bbox = self._compute_bbox()
83 self.points = self._points()
84 else:
85 self.shapetype = ogr.wkbNone
86 self.bbox = None
87 self.points = [[]]
88 try:
89 self.shapetype = ogrlib_shapetypes[self.shapetype]
90 except:
91 self.shapetype = ogrlib_shapetypes[ogr.wkbUnknown]
92
93 self.geoms = self._geoms()
94
95 def _geoms(self):
96 """Return a list of geometry objects.
97
98 If the shape is a geometry collection, all contained geometry
99 objects are stored to the list as OGRGeometry objects.
100 """
101 geoms = []
102 if self.shapetype == SHAPETYPE_GEOMCOLL:
103 for i in range(self.geom.GetGeometryCount()):
104 geoms.append(OGRGeometry(self, self.geom, i))
105 return geoms
106
107 def _compute_bbox(self):
108 """
109 Compute the bounding box of the shape as a tuple (minx,miny,maxx,maxy)
110 """
111 minx, maxx, miny, maxy = self.geom.GetEnvelope()
112 return (minx, miny, maxx, maxy)
113
114 def compute_bbox(self):
115 """
116 Return the bounding box of the shape as a tuple (minx,miny,maxx,maxy)
117 """
118 return self.bbox
119
120 def ShapeID(self):
121 """Return the feature id of this shape.
122 """
123 return self.shapeid
124
125 def Points(self):
126 """Return the coordinates of the shape as a list of lists of pairs
127 """
128 return self.points
129
130 def _points(self):
131 """Get the coordinates of the shape as a list of lists of pairs
132 """
133 shape = []
134
135 if self.geom is None:
136 return shape.append([])
137
138 # if geometry object is of type point or line
139 if self.geom.GetGeometryCount() == 0:
140 points =[]
141 for point in range(self.geom.GetPointCount()):
142 x = self.geom.GetX(point)
143 y = self.geom.GetY(point)
144 points.append((x, y))
145 return [points]
146 # if geometry object is of type polygon or multipolygon
147 for i in range(self.geom.GetGeometryCount()):
148 points = []
149 geometry = self.geom.GetGeometryRef(i)
150 # if geometry object is polygon
151 if geometry.GetGeometryCount() == 0:
152 for point in range(geometry.GetPointCount()):
153 x = geometry.GetX(point)
154 y = geometry.GetY(point)
155 points.append((x, y))
156 shape.append(points)
157 # if geometry object is of type multipolygon or geometry collection
158 else:
159 for j in range(geometry.GetGeometryCount()):
160 points = []
161 subgeom = geometry.GetGeometryRef(j)
162 for point in range(subgeom.GetPointCount()):
163 x = subgeom.GetX(point)
164 y = subgeom.GetY(point)
165 points.append((x, y))
166 shape.append(points)
167 return shape
168
169 def RawData(self):
170 """Return the shape id to use with the shapestore
171 """
172 return self.shapeid
173
174 def OGRLayer(self):
175 """Return the ogrlayer object
176 """
177 return self.ogrlayer
178
179 def ShapeType(self):
180 """Return the shapetype of this shape (may differ from the layer's
181 shapetype)
182 """
183 return self.shapetype
184
185 def GetGeoms(self):
186 """Return the list of geometries of this feature.
187
188 If this feature is a geometry collection, all contained geometries
189 are given. Else the returned list is empty.
190 """
191 return self.geoms
192
193 def GetGeom(self, index):
194 """Return the OGRGeometry object at the specified index.
195
196 This is not none only if the shape is a geometry collection.
197 """
198 if index < len(self.geoms):
199 return self.geoms[index]
200 else:
201 return None
202
203
204 class OGRGeometry:
205 """This class represents a geometry belonging to a specified feature.
206 """
207
208 def __init__(self, shape, geom, index):
209 """Initialize the geometry object.
210
211 shape should be an OGRShape, which this geometry belongs to.
212 geom is the base geometry, index is the ReferenceID.
213 """
214 self.shape = shape
215 self.index = index
216
217 self.geom = geom.GetGeometryRef(index)
218 try:
219 self.shapetype = ogrlib_shapetypes[self.geom.GetGeometryType()]
220 except:
221 self.shapetype = ogrlib_shapetypes[ogr.wkbUnknown]
222
223
224 def ShapeType(self):
225 """Return the shapetype of this geometry object."""
226 return self.shapetype
227
228
229 class OGRShapeStore:
230 """Corresponds to an OGRLayer object, containing features/shapes and
231 providing the same methods like ShapefileStore.
232 """
233
234 def __init__(self, session, filename, layername, id_column = None):
235 """Initialize the shapestore.
236
237 All required information is loaded from the datasource.
238 """
239 # if id_column is None, data is loaded from file, so we need path
240 # if id_column is not None, data is loaded from database
241 if id_column is None:
242 self.filename = os.path.abspath(filename)
243 else:
244 self.filename = filename
245 self.layername = layername
246
247 self.ogrdatasource = ogr.Open(filename)
248 self.ogrlayer = (self.ogrdatasource).GetLayerByName(layername)
249
250 if id_column is not None:
251 self.id_column = id_column
252 else:
253 self.id_column = None
254
255 self.table = OGRTable(session, self.ogrdatasource, self.ogrlayer,
256 self.id_column)
257
258 self._open_ogrlayer(layername)
259
260 def _open_ogrlayer(self, layername):
261 """Get all required information from the datasource.
262 """
263 self.numshapes = self.ogrlayer.GetFeatureCount()
264 self.shapetype = self.ogrlayer.GetLayerDefn().GetGeomType()
265
266 extent = self.ogrlayer.GetExtent()
267 if extent:
268 self.bbox = [extent[0], extent[2], extent[1], extent[3]]
269 else:
270 self.bbox = None
271
272 try:
273 self.shapetype = ogrlib_shapetypes[self.shapetype]
274 except:
275 # if shapetype is not contained in ogrlib_shapetypes
276 # treat it like SHAPETYPE_UNKNOWN
277 self.shapetype = ogrlib_shapetypes[ogr.wkbUnknown]
278
279 self.shapes = self.shapes()
280
281 def shapes(self):
282 """Return a collection of all features as OGRShape objects.
283 """
284 shapes = {}
285 self.ogrlayer.ResetReading()
286 if self.id_column is None:
287 nextFeature = self.ogrlayer.GetNextFeature()
288 while nextFeature is not None:
289 fid = nextFeature.GetFID()
290 shape = OGRShape(self, nextFeature)
291 shapes[shape.ShapeID()] = shape
292 nextFeature = self.ogrlayer.GetNextFeature()
293 else:
294 lay = self.ogrdatasource.ExecuteSQL("SELECT %s, * from %s"
295 % (self.id_column, self.layername))
296 if lay is not None:
297 lay.ResetReading()
298 nextFeature = lay.GetNextFeature()
299 while nextFeature is not None:
300 fid = nextFeature.GetField(0)
301 shape = OGRShape(self, nextFeature)
302 shapes[shape.ShapeID()] = shape
303 nextFeature = lay.GetNextFeature()
304 self.ogrdatasource.ReleaseResultSet(lay)
305 return shapes
306
307 def OGRLayer(self):
308 """Return the OGRLayer object
309 """
310 return self.ogrlayer
311
312 def FileName(self):
313 """Return the filename used to open the file
314 """
315 return self.filename
316
317 def FileType(self):
318 """Return the filetype. This is depending on the driver used to open
319 the file.
320 """
321 return self.ogrdatasource.GetDriver().GetName()
322
323 def ShapeType(self):
324 """Return the type of the shapes in the shapestore.
325
326 This is either SHAPETYPE_POINT, SHAPETYPE_ARC, SHAPETYPE_POLYGON,
327 SHAEPTYPE_GEOMCOLL, SHAPETYPE_NONE or SHAPETYPE_UNKNOWN.
328 """
329 return self.shapetype
330
331 def RawShapeFormat(self):
332 """Return the raw data format of the shape data, i.e. RAW_PYTHON
333 """
334 return RAW_PYTHON
335
336 def NumShapes(self):
337 """Return the number of shapes in the shape store
338 """
339 return self.numshapes
340
341 def BoundingBox(self):
342 """Return the bounding box of the shapes in the shapestore.
343 """
344 return self.bbox
345
346 def ShapesInRegion(self, bbox):
347 """Return an iterable over the shapes that overlap the bounding box.
348
349 The bbox parameter should be the bounding box as a tuple in the
350 form (minx, miny, maxx, maxy) in the coordinate system of the
351 shape store.
352 """
353 left, bottom, right, top = bbox
354
355 # create a geometry which can be passed to the layer as spatial filter
356 bboxpolygon = ogr.CreateGeometryFromWkt(
357 ('Polygon((%s %s, %s %s, %s %s,%s %s, %s %s))'
358 %(left, bottom, left, top, right, top,
359 right, bottom, left, bottom)))
360
361 if self.ogrlayer.GetSpatialRef():
362 bboxpolygon.AssignSpatialReference(self.ogrlayer.GetSpatialRef())
363
364 self.ogrlayer.ResetReading()
365 #ogrlayer.SetSpatialFilterRect(left, bottom, right, top)
366 self.ogrlayer.SetSpatialFilter(bboxpolygon)
367
368 numFeatures = self.ogrlayer.GetFeatureCount()
369 # if no features are in bbox, return all features as shapesInRegion
370 # (PostGIS sometimes returns no features even if they are within
371 # the bounding box)
372 if numFeatures == 0:
373 self.ogrlayer.SetSpatialFilter(None)
374 numFeatures = self.ogrlayer.GetFeatureCount()
375 for feature in range(numFeatures):
376 nextFeature = self.ogrlayer.GetNextFeature()
377 if self.id_column is None:
378 yield self.shapes[nextFeature.GetFID()]
379 else:
380 yield self.shapes[nextFeature.GetField(self.id_column)]
381
382 self.ogrlayer.SetSpatialFilter(None)
383 bboxpolygon.Destroy()
384
385 def AllShapes(self):
386 """Return an iterable over the shapes in the shape store.
387 """
388 for id in range(len(self.shapes)):
389 yield self.shapes[id]
390
391 def Shape(self, fid):
392 """Return the shape with fid = fid
393 """
394 if fid in self.table.ids.keys():
395 return self.shapes[fid]
396 else:
397 return None
398
399 def Table(self):
400 """Return the table containing the attribute data
401 """
402 return self.table
403
404 def Dependencies(self):
405 """Return the empty tuple.
406 """
407 return ()
408
409 def OrigShapeStore(self):
410 """Return None."""
411 return None
412
413 def Id_column(self):
414 """Return the id_column.
415 """
416 return self.id_column
417
418 class OGRTable(transientdb.AutoTransientTable):
419 """A Table for an ogr datasource.
420 """
421
422 def __init__(self, session, ds, layer, id_column):
423 """Initialize the OGRTable.
424
425 session - should be the current session.
426 ds - should be an instance of OGRDatasource.
427 layer - should be an instance of OGRLayer.
428 id_column - should be the name of the column used as ID column
429 """
430 self.datasource = ds
431 self.layer = layer
432 self.tablename = self.layer.GetName()
433 self.id_column = id_column
434
435 # Map column names and indices to column objects.
436 self.column_map = {}
437
438 # Map feature ids to ordinals.
439 self._map_ords_and_ids()
440
441 self._fetch_table_information()
442 self._fetch_table_content()
443
444 transientdb.AutoTransientTable.__init__(self, session.TransientDB(),
445 self)
446
447 def _fetch_table_information(self):
448 """Internal: Update information about the table
449 """
450 self.columns = []
451
452 layerdefn = self.layer.GetLayerDefn()
453 # if FID column is of interest
454 #col = OGRColumn("FID", table.FIELDTYPE_INT, layerdefn.GetFieldCount())
455 #self.columns.append(col)
456 for i in range(layerdefn.GetFieldCount()):
457 fielddef = layerdefn.GetFieldDefn(i)
458 fieldname = fielddef.GetName()
459 fieldtype = fieldtype_map[fielddef.GetType()]
460 fieldindex = layerdefn.GetFieldIndex(fieldname)
461 col = OGRColumn(fieldname, fieldtype, fieldindex)
462 if col is not None:
463 self.columns.append(col)
464
465 for col in self.columns:
466 self.column_map[col.name] = col
467 self.column_map[col.index] = col
468
469 def _fetch_table_content(self):
470 """Internal: Update information contained in the table
471 """
472 self.content = []
473 layerdefn = self.layer.GetLayerDefn()
474
475 self.layer.ResetReading()
476 for i in range(self.layer.GetFeatureCount()):
477 nextFeature = self.layer.GetNextFeature()
478 row = []
479 for j in range(layerdefn.GetFieldCount()):
480 row.append(nextFeature.GetField(j))
481 # if FID should be listed in the table
482 #if self.id_column is None:
483 # row.append(nextFeature.GetFID())
484 #else:
485 # row.append(nextFeature.GetField(self.id_column))
486 self.content.append(row)
487
488 def _map_ords_and_ids(self):
489 """Create collections which map ordinals to ids and verse visa.
490 """
491 self.ordinals = {}
492 self.ids = {}
493
494 if self.id_column is not None:
495 lay = self.datasource.ExecuteSQL("SELECT %s from %s"
496 %(self.id_column, self.tablename))
497 lay.ResetReading()
498 nextFeature = lay.GetNextFeature()
499 else:
500 self.layer.ResetReading()
501 nextFeature = self.layer.GetNextFeature()
502
503 ord = 0
504 while nextFeature is not None:
505 if self.id_column is not None:
506 id = nextFeature.GetField(self.id_column)
507 nextFeature = lay.GetNextFeature()
508 else:
509 id = nextFeature.GetFID()
510 nextFeature = self.layer.GetNextFeature()
511 self.ordinals[ord] = id
512 self.ids[id] = ord
513 ord = ord + 1
514 if self.id_column is not None:
515 self.datasource.ReleaseResultSet(lay)
516
517 def TableName(self):
518 """Return the name of the table, which is the name of the layer
519 """
520 return self.tablename
521
522 def Title(self):
523 """Return the title of the table.
524 """
525 return self.tablename
526
527 def Dependencies(self):
528 """Return an empty tuple.
529 """
530 return ()
531
532 def NumColumns(self):
533 """Return the number of columns.
534 """
535 return len(self.columns)
536
537 def Columns(self):
538 """Return all columns.
539 """
540 return self.columns
541
542 def Column(self, col):
543 """Return the column col. col can be either a string or an integer.
544 """
545 return self.column_map[col]
546
547 def HasColumn(self, col):
548 """Return if column col exists. col can be either a string or an
549 integer.
550 """
551 return self.column_map.has_key(col)
552
553 def NumRows(self):
554 """Return the number of rows in the table, which equals the number of
555 features in the layer.
556 """
557 return len(self.ids)
558
559 def RowIdToOrdinal(self, gid):
560 """Return the row ordinal given its id
561 """
562 if gid < 0:
563 return gid
564 else:
565 ord = self.ids[gid]
566 return ord
567
568 def RowOrdinalToId(self, num):
569 """Return the rowid for given its ordinal
570 """
571 if num >= 0:
572 id = self.ordinals[num]
573 return id
574 else:
575 return num
576
577 def ReadRowAsDict(self, row, row_is_ordinal = 0):
578 """Return a dictionary which contains all the fields.
579 """
580 if row_is_ordinal == 0:
581 rowId = self.RowIdToOrdinal(row)
582 else:
583 rowId = row
584 result = {}
585 for i in range(self.NumColumns()):
586 result[self.Column(i).name] = self.content[rowId][i]
587 return result
588
589 def ReadValue(self, row, col, row_is_ordinal = 0):
590 """Return the requested value.
591 """
592 if row_is_ordinal == 0:
593 rowId = self.RowIdToOrdinal(row)
594 else:
595 rowId = row
596 colIndex = self.column_map[col].index
597 return self.content[rowId][colIndex]
598
599 def ValueRange(self, col):
600 """Return the value range of the given column (given as string).
601 """
602
603 result = self.datasource.ExecuteSQL("SELECT min(%s), max(%s) FROM %s"
604 %(col, col, self.layer.GetName()))
605 result.ResetReading()
606 feature = result.GetNextFeature()
607 try:
608 min = feature.GetField(0)
609 max = feature.GetField(1)
610 except:
611 min = 0
612 max = 0
613 self.datasource.ReleaseResultSet(result)
614 return (min, max)
615
616 def UniqueValues(self, col):
617 """Return all the values being found in the column (given as string).
618 """
619 result = self.datasource.ExecuteSQL((
620 "SELECT DISTINCT %s FROM %s ORDER BY %s"
621 %(col,self.layer.GetName(),col)))
622 values = []
623 while 1:
624 feature = result.GetNextFeature()
625 if feature is None:
626 break
627 values.append(feature.GetField(0))
628 self.datasource.ReleaseResultSet(result)
629 return values
630
631 def SimpleQuery(self, left, comparison, right):
632 """Return the FIDs resulting from the given query.
633 """
634
635 if comparison not in ("==", "!=", "<", "<=", ">=", ">"):
636 raise ValueError("Comparison operator %r not allowed" %comparison)
637
638 if comparison == "==":
639 comparison = "="
640
641 if isinstance(right, OGRColumn):
642 right_template = right.name
643 else:
644 right_template = right
645
646 if self.id_column is None:
647 id = "FID"
648 else:
649 id = self.id_column
650 query = ("SELECT %s FROM %s WHERE %s %s %s ORDER BY %s"
651 % (id, self.tablename,left.name, comparison,
652 right_template, id))
653
654 lay = self.datasource.ExecuteSQL(query)
655 result = []
656 while lay is not None:
657 feature = lay.GetNextFeature()
658 if feature is None:
659 break
660 result.append(feature.GetField(0))
661 if lay is not None:
662 self.datasource.ReleaseResultSet(lay)
663 return result
664
665 def Id_column(self):
666 """Return the id_column.
667 """
668 return self.id_column
669
670
671 class OGRColumn:
672 """Column description for a table for an ogr file
673 """
674
675 def __init__(self, name, type, index):
676 self.name = name
677 self.type = type
678 self.index = index

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26