25 |
|
|
26 |
from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT |
from Thuban.Model.data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT |
27 |
from Thuban.Model.data import RAW_PYTHON, RAW_SHAPEFILE, RAW_WKT |
from Thuban.Model.data import RAW_PYTHON, RAW_SHAPEFILE, RAW_WKT |
28 |
|
from Thuban.Model.data import FileShapeStore |
29 |
|
|
30 |
def has_ogr_support(): |
def has_ogr_support(): |
31 |
"""Return whether this Thuban instance supports ogr file formats |
"""Return whether this Thuban instance supports ogr file formats |
230 |
class OGRShapeStore: |
class OGRShapeStore: |
231 |
"""Corresponds to an OGRLayer object, containing features/shapes and |
"""Corresponds to an OGRLayer object, containing features/shapes and |
232 |
providing the same methods like ShapefileStore. |
providing the same methods like ShapefileStore. |
233 |
|
|
234 |
|
In fact, for all file based shape stores, the class OGRFileShapeStore |
235 |
|
should be used. Only database shape stores should be |
236 |
|
used with OGRShapeStore. It is subject to re-factoring |
237 |
|
to end up with better class names and sensible base classes. |
238 |
""" |
""" |
239 |
|
|
240 |
|
# TODO: re-factor this class to be not responsible for file-based |
241 |
|
# stores anymore. |
242 |
|
|
243 |
def __init__(self, session, filename, layername, id_column = None): |
def __init__(self, session, filename, layername, id_column = None): |
244 |
"""Initialize the shapestore. |
"""Initialize the shapestore. |
245 |
|
|
424 |
""" |
""" |
425 |
return self.id_column |
return self.id_column |
426 |
|
|
427 |
|
class OGRFileShapeStore(FileShapeStore): |
428 |
|
"""Corresponds to an OGRLayer object, containing features/shapes and |
429 |
|
providing the same methods like ShapefileStore. |
430 |
|
""" |
431 |
|
|
432 |
|
def __init__(self, session, filename, layername, id_column = None): |
433 |
|
"""Initialize the shapestore. |
434 |
|
|
435 |
|
All required information is loaded from the datasource. |
436 |
|
""" |
437 |
|
self._bbox = None |
438 |
|
self.ogrdatasource = ogr.Open(filename) |
439 |
|
|
440 |
|
# filetype is depending on the driver used to open the file. |
441 |
|
self._filetype = self.ogrdatasource.GetDriver().GetName() |
442 |
|
if self._filetype == 'ESRI Shapefile': |
443 |
|
self._filetype = "shapefile" |
444 |
|
FileShapeStore.__init__(self, filename, |
445 |
|
sublayer_name = layername) |
446 |
|
|
447 |
|
self.ogrlayer = (self.ogrdatasource).GetLayerByName(layername) |
448 |
|
|
449 |
|
self._table = OGRTable(session, self.ogrdatasource, self.ogrlayer, |
450 |
|
id_column) |
451 |
|
|
452 |
|
self._open_ogrlayer(layername) |
453 |
|
|
454 |
|
def _open_ogrlayer(self, layername): |
455 |
|
"""Get all required information from the datasource. |
456 |
|
""" |
457 |
|
self.numshapes = self.ogrlayer.GetFeatureCount() |
458 |
|
self.shapetype = self.ogrlayer.GetLayerDefn().GetGeomType() |
459 |
|
|
460 |
|
extent = self.ogrlayer.GetExtent() |
461 |
|
if extent: |
462 |
|
self._bbox = [extent[0], extent[2], extent[1], extent[3]] |
463 |
|
else: |
464 |
|
self._bbox = None |
465 |
|
|
466 |
|
try: |
467 |
|
self.shapetype = ogrlib_shapetypes[self.shapetype] |
468 |
|
except: |
469 |
|
# if shapetype is not contained in ogrlib_shapetypes |
470 |
|
# treat it like SHAPETYPE_UNKNOWN |
471 |
|
self.shapetype = ogrlib_shapetypes[ogr.wkbUnknown] |
472 |
|
|
473 |
|
self.shapes = self.shapes() |
474 |
|
|
475 |
|
def FileType(self): |
476 |
|
"""Return the filetype.""" |
477 |
|
return self._filetype |
478 |
|
|
479 |
|
def BoundingBox(self): |
480 |
|
"""Return the bounding box of the shapes in the shape file. |
481 |
|
|
482 |
|
The coordinate system used is whatever was used in the shape file. |
483 |
|
If the shape file is empty, return None. |
484 |
|
""" |
485 |
|
return self._bbox |
486 |
|
|
487 |
|
def shapes(self): |
488 |
|
"""Return a collection of all features as OGRShape objects. |
489 |
|
""" |
490 |
|
shapes = {} |
491 |
|
self.ogrlayer.ResetReading() |
492 |
|
|
493 |
|
nextFeature = self.ogrlayer.GetNextFeature() |
494 |
|
while nextFeature is not None: |
495 |
|
fid = nextFeature.GetFID() |
496 |
|
shape = OGRShape(self, nextFeature) |
497 |
|
shapes[shape.ShapeID()] = shape |
498 |
|
nextFeature = self.ogrlayer.GetNextFeature() |
499 |
|
|
500 |
|
return shapes |
501 |
|
|
502 |
|
def OGRLayer(self): |
503 |
|
"""Return the OGRLayer object |
504 |
|
""" |
505 |
|
return self.ogrlayer |
506 |
|
|
507 |
|
def ShapeType(self): |
508 |
|
"""Return the type of the shapes in the shapestore. |
509 |
|
|
510 |
|
This is either SHAPETYPE_POINT, SHAPETYPE_ARC, SHAPETYPE_POLYGON, |
511 |
|
SHAEPTYPE_GEOMCOLL, SHAPETYPE_NONE or SHAPETYPE_UNKNOWN. |
512 |
|
""" |
513 |
|
return self.shapetype |
514 |
|
|
515 |
|
def RawShapeFormat(self): |
516 |
|
"""Return the raw data format of the shape data, i.e. RAW_PYTHON |
517 |
|
""" |
518 |
|
return RAW_PYTHON |
519 |
|
|
520 |
|
def NumShapes(self): |
521 |
|
"""Return the number of shapes in the shape store |
522 |
|
""" |
523 |
|
return self.numshapes |
524 |
|
|
525 |
|
def ShapesInRegion(self, bbox): |
526 |
|
"""Return an iterable over the shapes that overlap the bounding box. |
527 |
|
|
528 |
|
The bbox parameter should be the bounding box as a tuple in the |
529 |
|
form (minx, miny, maxx, maxy) in the coordinate system of the |
530 |
|
shape store. |
531 |
|
""" |
532 |
|
left, bottom, right, top = bbox |
533 |
|
|
534 |
|
# create a geometry which can be passed to the layer as spatial filter |
535 |
|
bboxpolygon = ogr.CreateGeometryFromWkt( |
536 |
|
('Polygon((%s %s, %s %s, %s %s,%s %s, %s %s))' |
537 |
|
%(left, bottom, left, top, right, top, |
538 |
|
right, bottom, left, bottom))) |
539 |
|
|
540 |
|
if self.ogrlayer.GetSpatialRef(): |
541 |
|
bboxpolygon.AssignSpatialReference(self.ogrlayer.GetSpatialRef()) |
542 |
|
|
543 |
|
self.ogrlayer.ResetReading() |
544 |
|
#ogrlayer.SetSpatialFilterRect(left, bottom, right, top) |
545 |
|
self.ogrlayer.SetSpatialFilter(bboxpolygon) |
546 |
|
|
547 |
|
numFeatures = self.ogrlayer.GetFeatureCount() |
548 |
|
# if no features are in bbox, return all features as shapesInRegion |
549 |
|
# (PostGIS sometimes returns no features even if they are within |
550 |
|
# the bounding box) |
551 |
|
if numFeatures == 0: |
552 |
|
self.ogrlayer.SetSpatialFilter(None) |
553 |
|
numFeatures = self.ogrlayer.GetFeatureCount() |
554 |
|
for feature in range(numFeatures): |
555 |
|
nextFeature = self.ogrlayer.GetNextFeature() |
556 |
|
yield self.shapes[nextFeature.GetFID()] |
557 |
|
|
558 |
|
self.ogrlayer.SetSpatialFilter(None) |
559 |
|
bboxpolygon.Destroy() |
560 |
|
|
561 |
|
def AllShapes(self): |
562 |
|
"""Return an iterable over the shapes in the shape store. |
563 |
|
""" |
564 |
|
for id in range(len(self.shapes)): |
565 |
|
yield self.shapes[id] |
566 |
|
|
567 |
|
def Shape(self, fid): |
568 |
|
"""Return the shape with fid = fid |
569 |
|
""" |
570 |
|
if fid in self.Table().ids.keys(): |
571 |
|
return self.shapes[fid] |
572 |
|
else: |
573 |
|
return None |
574 |
|
|
575 |
|
def Table(self): |
576 |
|
"""Return the table containing the attribute data.""" |
577 |
|
return self._table |
578 |
|
|
579 |
|
def Dependencies(self): |
580 |
|
"""Return the empty tuple. |
581 |
|
""" |
582 |
|
return () |
583 |
|
|
584 |
|
def OrigShapeStore(self): |
585 |
|
"""Return None.""" |
586 |
|
return None |
587 |
|
|
588 |
|
def Id_column(self): |
589 |
|
"""Return the id_column. |
590 |
|
""" |
591 |
|
return None |
592 |
|
|
593 |
class OGRTable(transientdb.AutoTransientTable): |
class OGRTable(transientdb.AutoTransientTable): |
594 |
"""A Table for an ogr datasource. |
"""A Table for an ogr datasource. |
595 |
""" |
""" |