You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
7.1 KiB
187 lines
7.1 KiB
# Needed ctypes routines |
|
from ctypes import byref |
|
|
|
# Other GDAL imports. |
|
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope |
|
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException |
|
from django.contrib.gis.gdal.feature import Feature |
|
from django.contrib.gis.gdal.field import FIELD_CLASSES |
|
from django.contrib.gis.gdal.geometries import OGRGeomType |
|
from django.contrib.gis.gdal.srs import SpatialReference |
|
|
|
# GDAL ctypes function prototypes. |
|
from django.contrib.gis.gdal.prototypes.ds import \ |
|
get_extent, get_fd_geom_type, get_fd_name, get_feature, get_feature_count, \ |
|
get_field_count, get_field_defn, get_field_name, get_field_precision, \ |
|
get_field_width, get_field_type, get_layer_defn, get_layer_srs, \ |
|
get_next_feature, reset_reading, test_capability |
|
from django.contrib.gis.gdal.prototypes.srs import clone_srs |
|
|
|
# For more information, see the OGR C API source code: |
|
# http://www.gdal.org/ogr/ogr__api_8h.html |
|
# |
|
# The OGR_L_* routines are relevant here. |
|
class Layer(object): |
|
"A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." |
|
|
|
#### Python 'magic' routines #### |
|
def __init__(self, layer_ptr): |
|
"Needs a C pointer (Python/ctypes integer) in order to initialize." |
|
self._ptr = None # Initially NULL |
|
if not layer_ptr: |
|
raise OGRException('Cannot create Layer, invalid pointer given') |
|
self._ptr = layer_ptr |
|
self._ldefn = get_layer_defn(self._ptr) |
|
# Does the Layer support random reading? |
|
self._random_read = self.test_capability('RandomRead') |
|
|
|
def __getitem__(self, index): |
|
"Gets the Feature at the specified index." |
|
if isinstance(index, (int, long)): |
|
# An integer index was given -- we cannot do a check based on the |
|
# number of features because the beginning and ending feature IDs |
|
# are not guaranteed to be 0 and len(layer)-1, respectively. |
|
if index < 0: raise OGRIndexError('Negative indices are not allowed on OGR Layers.') |
|
return self._make_feature(index) |
|
elif isinstance(index, slice): |
|
# A slice was given |
|
start, stop, stride = index.indices(self.num_feat) |
|
return [self._make_feature(fid) for fid in xrange(start, stop, stride)] |
|
else: |
|
raise TypeError('Integers and slices may only be used when indexing OGR Layers.') |
|
|
|
def __iter__(self): |
|
"Iterates over each Feature in the Layer." |
|
# ResetReading() must be called before iteration is to begin. |
|
reset_reading(self._ptr) |
|
for i in xrange(self.num_feat): |
|
yield Feature(get_next_feature(self._ptr), self._ldefn) |
|
|
|
def __len__(self): |
|
"The length is the number of features." |
|
return self.num_feat |
|
|
|
def __str__(self): |
|
"The string name of the layer." |
|
return self.name |
|
|
|
def _make_feature(self, feat_id): |
|
""" |
|
Helper routine for __getitem__ that constructs a Feature from the given |
|
Feature ID. If the OGR Layer does not support random-access reading, |
|
then each feature of the layer will be incremented through until the |
|
a Feature is found matching the given feature ID. |
|
""" |
|
if self._random_read: |
|
# If the Layer supports random reading, return. |
|
try: |
|
return Feature(get_feature(self._ptr, feat_id), self._ldefn) |
|
except OGRException: |
|
pass |
|
else: |
|
# Random access isn't supported, have to increment through |
|
# each feature until the given feature ID is encountered. |
|
for feat in self: |
|
if feat.fid == feat_id: return feat |
|
# Should have returned a Feature, raise an OGRIndexError. |
|
raise OGRIndexError('Invalid feature id: %s.' % feat_id) |
|
|
|
#### Layer properties #### |
|
@property |
|
def extent(self): |
|
"Returns the extent (an Envelope) of this layer." |
|
env = OGREnvelope() |
|
get_extent(self._ptr, byref(env), 1) |
|
return Envelope(env) |
|
|
|
@property |
|
def name(self): |
|
"Returns the name of this layer in the Data Source." |
|
return get_fd_name(self._ldefn) |
|
|
|
@property |
|
def num_feat(self, force=1): |
|
"Returns the number of features in the Layer." |
|
return get_feature_count(self._ptr, force) |
|
|
|
@property |
|
def num_fields(self): |
|
"Returns the number of fields in the Layer." |
|
return get_field_count(self._ldefn) |
|
|
|
@property |
|
def geom_type(self): |
|
"Returns the geometry type (OGRGeomType) of the Layer." |
|
return OGRGeomType(get_fd_geom_type(self._ldefn)) |
|
|
|
@property |
|
def srs(self): |
|
"Returns the Spatial Reference used in this Layer." |
|
try: |
|
ptr = get_layer_srs(self._ptr) |
|
return SpatialReference(clone_srs(ptr)) |
|
except SRSException: |
|
return None |
|
|
|
@property |
|
def fields(self): |
|
""" |
|
Returns a list of string names corresponding to each of the Fields |
|
available in this Layer. |
|
""" |
|
return [get_field_name(get_field_defn(self._ldefn, i)) |
|
for i in xrange(self.num_fields) ] |
|
|
|
@property |
|
def field_types(self): |
|
""" |
|
Returns a list of the types of fields in this Layer. For example, |
|
the list [OFTInteger, OFTReal, OFTString] would be returned for |
|
an OGR layer that had an integer, a floating-point, and string |
|
fields. |
|
""" |
|
return [FIELD_CLASSES[get_field_type(get_field_defn(self._ldefn, i))] |
|
for i in xrange(self.num_fields)] |
|
|
|
@property |
|
def field_widths(self): |
|
"Returns a list of the maximum field widths for the features." |
|
return [get_field_width(get_field_defn(self._ldefn, i)) |
|
for i in xrange(self.num_fields)] |
|
|
|
@property |
|
def field_precisions(self): |
|
"Returns the field precisions for the features." |
|
return [get_field_precision(get_field_defn(self._ldefn, i)) |
|
for i in xrange(self.num_fields)] |
|
|
|
#### Layer Methods #### |
|
def get_fields(self, field_name): |
|
""" |
|
Returns a list containing the given field name for every Feature |
|
in the Layer. |
|
""" |
|
if not field_name in self.fields: |
|
raise OGRException('invalid field name: %s' % field_name) |
|
return [feat.get(field_name) for feat in self] |
|
|
|
def get_geoms(self, geos=False): |
|
""" |
|
Returns a list containing the OGRGeometry for every Feature in |
|
the Layer. |
|
""" |
|
if geos: |
|
from django.contrib.gis.geos import GEOSGeometry |
|
return [GEOSGeometry(feat.geom.wkb) for feat in self] |
|
else: |
|
return [feat.geom for feat in self] |
|
|
|
def test_capability(self, capability): |
|
""" |
|
Returns a bool indicating whether the this Layer supports the given |
|
capability (a string). Valid capability strings include: |
|
'RandomRead', 'SequentialWrite', 'RandomWrite', 'FastSpatialFilter', |
|
'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions', |
|
'DeleteFeature', and 'FastSetNextByIndex'. |
|
""" |
|
return bool(test_capability(self._ptr, capability))
|
|
|