web/lib/django/contrib/gis/gdal/layer.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 # Needed ctypes routines
       
     2 from ctypes import c_double, byref
       
     3 
       
     4 # Other GDAL imports.
       
     5 from django.contrib.gis.gdal.base import GDALBase
       
     6 from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
       
     7 from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
       
     8 from django.contrib.gis.gdal.feature import Feature
       
     9 from django.contrib.gis.gdal.field import OGRFieldTypes
       
    10 from django.contrib.gis.gdal.geomtype import OGRGeomType
       
    11 from django.contrib.gis.gdal.geometries import OGRGeometry
       
    12 from django.contrib.gis.gdal.srs import SpatialReference
       
    13 
       
    14 # GDAL ctypes function prototypes.
       
    15 from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api
       
    16 
       
    17 # For more information, see the OGR C API source code:
       
    18 #  http://www.gdal.org/ogr/ogr__api_8h.html
       
    19 #
       
    20 # The OGR_L_* routines are relevant here.
       
    21 class Layer(GDALBase):
       
    22     "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object."
       
    23 
       
    24     #### Python 'magic' routines ####
       
    25     def __init__(self, layer_ptr, ds):
       
    26         """
       
    27         Initializes on an OGR C pointer to the Layer and the `DataSource` object
       
    28         that owns this layer.  The `DataSource` object is required so that a 
       
    29         reference to it is kept with this Layer.  This prevents garbage 
       
    30         collection of the `DataSource` while this Layer is still active.
       
    31         """
       
    32         if not layer_ptr:
       
    33             raise OGRException('Cannot create Layer, invalid pointer given')
       
    34         self.ptr = layer_ptr
       
    35         self._ds = ds
       
    36         self._ldefn = capi.get_layer_defn(self._ptr)
       
    37         # Does the Layer support random reading?
       
    38         self._random_read = self.test_capability('RandomRead')
       
    39 
       
    40     def __getitem__(self, index):
       
    41         "Gets the Feature at the specified index."
       
    42         if isinstance(index, (int, long)):
       
    43             # An integer index was given -- we cannot do a check based on the
       
    44             # number of features because the beginning and ending feature IDs
       
    45             # are not guaranteed to be 0 and len(layer)-1, respectively.
       
    46             if index < 0: raise OGRIndexError('Negative indices are not allowed on OGR Layers.')
       
    47             return self._make_feature(index)
       
    48         elif isinstance(index, slice):
       
    49             # A slice was given
       
    50             start, stop, stride = index.indices(self.num_feat)
       
    51             return [self._make_feature(fid) for fid in xrange(start, stop, stride)]
       
    52         else:
       
    53             raise TypeError('Integers and slices may only be used when indexing OGR Layers.')
       
    54 
       
    55     def __iter__(self):
       
    56         "Iterates over each Feature in the Layer."
       
    57         # ResetReading() must be called before iteration is to begin.
       
    58         capi.reset_reading(self._ptr)
       
    59         for i in xrange(self.num_feat):
       
    60             yield Feature(capi.get_next_feature(self._ptr), self._ldefn)
       
    61 
       
    62     def __len__(self):
       
    63         "The length is the number of features."
       
    64         return self.num_feat
       
    65 
       
    66     def __str__(self):
       
    67         "The string name of the layer."
       
    68         return self.name
       
    69 
       
    70     def _make_feature(self, feat_id):
       
    71         """
       
    72         Helper routine for __getitem__ that constructs a Feature from the given
       
    73         Feature ID.  If the OGR Layer does not support random-access reading,
       
    74         then each feature of the layer will be incremented through until the
       
    75         a Feature is found matching the given feature ID.
       
    76         """
       
    77         if self._random_read:
       
    78             # If the Layer supports random reading, return.
       
    79             try:
       
    80                 return Feature(capi.get_feature(self.ptr, feat_id), self._ldefn)
       
    81             except OGRException:
       
    82                 pass
       
    83         else:
       
    84             # Random access isn't supported, have to increment through
       
    85             # each feature until the given feature ID is encountered.
       
    86             for feat in self:
       
    87                 if feat.fid == feat_id: return feat
       
    88         # Should have returned a Feature, raise an OGRIndexError.    
       
    89         raise OGRIndexError('Invalid feature id: %s.' % feat_id)
       
    90 
       
    91     #### Layer properties ####
       
    92     @property
       
    93     def extent(self):
       
    94         "Returns the extent (an Envelope) of this layer."
       
    95         env = OGREnvelope()
       
    96         capi.get_extent(self.ptr, byref(env), 1)
       
    97         return Envelope(env)
       
    98 
       
    99     @property
       
   100     def name(self):
       
   101         "Returns the name of this layer in the Data Source."
       
   102         return capi.get_fd_name(self._ldefn)
       
   103 
       
   104     @property
       
   105     def num_feat(self, force=1):
       
   106         "Returns the number of features in the Layer."
       
   107         return capi.get_feature_count(self.ptr, force)
       
   108 
       
   109     @property
       
   110     def num_fields(self):
       
   111         "Returns the number of fields in the Layer."
       
   112         return capi.get_field_count(self._ldefn)
       
   113 
       
   114     @property
       
   115     def geom_type(self):
       
   116         "Returns the geometry type (OGRGeomType) of the Layer."
       
   117         return OGRGeomType(capi.get_fd_geom_type(self._ldefn))
       
   118 
       
   119     @property
       
   120     def srs(self):
       
   121         "Returns the Spatial Reference used in this Layer."
       
   122         try:
       
   123             ptr = capi.get_layer_srs(self.ptr)
       
   124             return SpatialReference(srs_api.clone_srs(ptr))
       
   125         except SRSException:
       
   126             return None
       
   127 
       
   128     @property
       
   129     def fields(self):
       
   130         """
       
   131         Returns a list of string names corresponding to each of the Fields
       
   132         available in this Layer.
       
   133         """
       
   134         return [capi.get_field_name(capi.get_field_defn(self._ldefn, i)) 
       
   135                 for i in xrange(self.num_fields) ]
       
   136     
       
   137     @property
       
   138     def field_types(self):
       
   139         """
       
   140         Returns a list of the types of fields in this Layer.  For example,
       
   141         the list [OFTInteger, OFTReal, OFTString] would be returned for
       
   142         an OGR layer that had an integer, a floating-point, and string
       
   143         fields.
       
   144         """
       
   145         return [OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))]
       
   146                 for i in xrange(self.num_fields)]
       
   147 
       
   148     @property 
       
   149     def field_widths(self):
       
   150         "Returns a list of the maximum field widths for the features."
       
   151         return [capi.get_field_width(capi.get_field_defn(self._ldefn, i))
       
   152                 for i in xrange(self.num_fields)]
       
   153 
       
   154     @property 
       
   155     def field_precisions(self):
       
   156         "Returns the field precisions for the features."
       
   157         return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i))
       
   158                 for i in xrange(self.num_fields)]
       
   159 
       
   160     def _get_spatial_filter(self):
       
   161         try:
       
   162             return OGRGeometry(geom_api.clone_geom(capi.get_spatial_filter(self.ptr)))
       
   163         except OGRException:
       
   164             return None
       
   165 
       
   166     def _set_spatial_filter(self, filter):
       
   167         if isinstance(filter, OGRGeometry):
       
   168             capi.set_spatial_filter(self.ptr, filter.ptr)
       
   169         elif isinstance(filter, (tuple, list)):
       
   170             if not len(filter) == 4:
       
   171                 raise ValueError('Spatial filter list/tuple must have 4 elements.')
       
   172             # Map c_double onto params -- if a bad type is passed in it
       
   173             # will be caught here.
       
   174             xmin, ymin, xmax, ymax = map(c_double, filter)
       
   175             capi.set_spatial_filter_rect(self.ptr, xmin, ymin, xmax, ymax)
       
   176         elif filter is None:
       
   177             capi.set_spatial_filter(self.ptr, None)
       
   178         else:
       
   179             raise TypeError('Spatial filter must be either an OGRGeometry instance, a 4-tuple, or None.')
       
   180 
       
   181     spatial_filter = property(_get_spatial_filter, _set_spatial_filter)
       
   182 
       
   183     #### Layer Methods ####
       
   184     def get_fields(self, field_name):
       
   185         """
       
   186         Returns a list containing the given field name for every Feature
       
   187         in the Layer.
       
   188         """
       
   189         if not field_name in self.fields:
       
   190             raise OGRException('invalid field name: %s' % field_name)
       
   191         return [feat.get(field_name) for feat in self]
       
   192 
       
   193     def get_geoms(self, geos=False):
       
   194         """
       
   195         Returns a list containing the OGRGeometry for every Feature in
       
   196         the Layer.
       
   197         """
       
   198         if geos:
       
   199             from django.contrib.gis.geos import GEOSGeometry
       
   200             return [GEOSGeometry(feat.geom.wkb) for feat in self]
       
   201         else:
       
   202             return [feat.geom for feat in self]
       
   203 
       
   204     def test_capability(self, capability):
       
   205         """
       
   206         Returns a bool indicating whether the this Layer supports the given
       
   207         capability (a string).  Valid capability strings include:
       
   208           'RandomRead', 'SequentialWrite', 'RandomWrite', 'FastSpatialFilter',
       
   209           'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions',
       
   210           'DeleteFeature', and 'FastSetNextByIndex'.
       
   211         """
       
   212         return bool(capi.test_capability(self.ptr, capability))