def read_from_file(self, filename): # Open data file for reading # File must be kept open, otherwise GDAL methods segfault. fid = self.fid = gdal.Open(filename, gdal.GA_ReadOnly) if fid is None: msg = 'Could not open file %s' % filename raise Exception(msg) # Record raster metadata from file basename, ext = os.path.splitext(filename) # If file is ASCII, check that projection is around. # GDAL does not check this nicely, so it is worth an # error message if ext == '.asc': try: open(basename + '.prj') except IOError: msg = ('Projection file not found for %s. You must supply ' 'a projection file with extension .prj' % filename) raise RuntimeError(msg) # Look for any keywords self.keywords = read_keywords(basename + '.keywords') # Determine name if 'title' in self.keywords: rastername = self.keywords['title'] else: # Use basename without leading directories as name rastername = os.path.split(basename)[-1] self.name = rastername self.filename = filename self.projection = Projection(self.fid.GetProjection()) self.geotransform = self.fid.GetGeoTransform() self.columns = fid.RasterXSize self.rows = fid.RasterYSize self.number_of_bands = fid.RasterCount # Assume that file contains all data in one band msg = 'Only one raster band currently allowed' if self.number_of_bands > 1: msg = ('WARNING: Number of bands in %s are %i. ' 'Only the first band will currently be ' 'used.' % (filename, self.number_of_bands)) # FIXME(Ole): Let us use python warnings here raise Exception(msg) # Get first band. band = self.band = fid.GetRasterBand(1) if band is None: msg = 'Could not read raster band from %s' % filename raise Exception(msg)
def __init__(self, data=None, projection=None, geotransform=None, name='Raster layer', keywords=None): """Initialise object with either data or filename Input data: Can be either * a filename of a raster file format known to GDAL * an MxN array of raster data * None (FIXME (Ole): Remove this option) projection: Geospatial reference in WKT format. Only used if data is provide as a numeric array, geotransform: GDAL geotransform (6-tuple). (top left x, w-e pixel resolution, rotation, top left y, rotation, n-s pixel resolution). See e.g. http://www.gdal.org/gdal_tutorial.html Only used if data is provide as a numeric array, name: Optional name for layer. Only used if data is provide as a numeric array, keywords: Optional dictionary with keywords that describe the layer. When the layer is stored, these keywords will be written into an associated file with extension .keywords. Keywords can for example be used to display text about the layer in a web application. Note that if data is a filename, all other arguments are ignored as they will be inferred from the file. """ # Input checks if data is None: # Instantiate empty object self.name = name self.data = None self.projection = None self.coordinates = None self.filename = None self.keywords = {} return # Initialisation if isinstance(data, basestring): self.read_from_file(data) else: # Assume that data is provided as an array # with extra keyword arguments supplying metadata if keywords is None: self.keywords = {} else: msg = ('Specified keywords must be either None or a ' 'dictionary. I got %s' % keywords) assert isinstance(keywords, dict), msg self.keywords = keywords self.data = numpy.array(data, dtype='d', copy=False) self.filename = None self.name = name self.projection = Projection(projection) self.geotransform = geotransform self.rows = data.shape[0] self.columns = data.shape[1] self.number_of_bands = 1
def read_from_file(self, filename): """ Read and unpack vector data. It is assumed that the file contains only one layer with the pertinent features. Further it is assumed for the moment that all geometries are points. * A feature is a geometry and a set of attributes. * A geometry refers to location and can be point, line, polygon or combinations thereof. * The attributes or obtained through GetField() The full OGR architecture is documented at * http://www.gdal.org/ogr/ogr_arch.html * http://www.gdal.org/ogr/ogr_apitut.html Examples are at * danieljlewis.org/files/2010/09/basicpythonmap.pdf * http://invisibleroads.com/tutorials/gdal-shapefile-points-save.html * http://www.packtpub.com/article/geospatial-data-python-geometry """ basename, _ = os.path.splitext(filename) # Look for any keywords self.keywords = read_keywords(basename + '.keywords') # Determine name if 'title' in self.keywords: vectorname = self.keywords['title'] else: # Use basename without leading directories as name vectorname = os.path.split(basename)[-1] self.name = vectorname self.filename = filename self.geometry_type = None # In case there are no features fid = ogr.Open(filename) if fid is None: msg = 'Could not open %s' % filename raise IOError(msg) # Assume that file contains all data in one layer msg = 'Only one vector layer currently allowed' if fid.GetLayerCount() > 1: msg = ('WARNING: Number of layers in %s are %i. ' 'Only the first layer will currently be ' 'used.' % (filename, fid.GetLayerCount())) raise Exception(msg) layer = fid.GetLayerByIndex(0) # Get spatial extent self.extent = layer.GetExtent() # Get projection p = layer.GetSpatialRef() self.projection = Projection(p) # Get number of features N = layer.GetFeatureCount() # Extract coordinates and attributes for all features geometry = [] data = [] for i in range(N): feature = layer.GetFeature(i) if feature is None: msg = 'Could not get feature %i from %s' % (i, filename) raise Exception(msg) # Record coordinates ordered as Longitude, Latitude G = feature.GetGeometryRef() if G is None: msg = ('Geometry was None in filename %s ' % filename) raise Exception(msg) else: self.geometry_type = G.GetGeometryType() if self.geometry_type == ogr.wkbPoint: geometry.append((G.GetX(), G.GetY())) elif self.geometry_type == ogr.wkbPolygon: ring = G.GetGeometryRef(0) M = ring.GetPointCount() coordinates = [] for j in range(M): coordinates.append((ring.GetX(j), ring.GetY(j))) # Record entire polygon ring as an Mx2 numpy array geometry.append( numpy.array(coordinates, dtype='d', copy=False)) else: msg = ('Only point and polygon geometries are supported. ' 'Geometry in filename %s ' 'was %s.' % (filename, G.GetGeometryType())) raise Exception(msg) # Record attributes by name number_of_fields = feature.GetFieldCount() fields = {} for j in range(number_of_fields): name = feature.GetFieldDefnRef(j).GetName() # FIXME (Ole): Ascertain the type of each field? # We need to cast each appropriately? # This is issue #66 #feature_type = feature.GetFieldDefnRef(j).GetType() fields[name] = feature.GetField(j) #print 'Field', name, feature_type, j, fields[name] data.append(fields) # Store geometry coordinates as a compact numeric array self.geometry = geometry self.data = data
def __init__(self, data=None, projection=None, geometry=None, name='Vector layer', keywords=None): """Initialise object with either geometry or filename Input data: Can be either * a filename of a vector file format known to GDAL * List of dictionaries of fields associated with point coordinates * None projection: Geospatial reference in WKT format. Only used if geometry is provide as a numeric array, geometry: A list of either point coordinates or polygons name: Optional name for layer. Only used if geometry is provide as a numeric array keywords: Optional dictionary with keywords that describe the layer. When the layer is stored, these keywords will be written into an associated file with extension .keywords. Keywords can for example be used to display text about the layer in a web application. Note that if data is a filename, all other arguments are ignored as they will be inferred from the file. The geometry type will be inferred from the dimensions of geometry. If each entry is one set of coordinates the type will be ogr.wkbPoint, if it is an array of coordinates the type will be ogr.wkbPolygon. """ if data is None and projection is None and geometry is None: # Instantiate empty object self.name = name self.projection = None self.geometry = None self.geometry_type = None self.filename = None self.data = None self.extent = None self.keywords = {} return if isinstance(data, basestring): self.read_from_file(data) else: # Assume that data is provided as sequences provided as # arguments to the Vector constructor # with extra keyword arguments supplying metadata self.name = name self.filename = None if keywords is None: self.keywords = {} else: msg = ('Specified keywords must be either None or a ' 'dictionary. I got %s' % keywords) assert isinstance(keywords, dict), msg self.keywords = keywords msg = 'Geometry must be specified' assert geometry is not None, msg msg = 'Geometry must be a sequence' assert is_sequence(geometry), msg self.geometry = geometry self.geometry_type = get_geometry_type(geometry) msg = 'Projection must be specified' assert projection is not None, msg self.projection = Projection(projection) self.data = data if data is not None: msg = 'Data must be a sequence' assert is_sequence(data), msg msg = ('The number of entries in geometry and data ' 'must be the same') assert len(geometry) == len(data), msg
def check_data_integrity(layer_files): """Read list of layer files and verify that that they have the same projection and georeferencing. """ # Set default values for projection and geotransform. # Enforce DEFAULT (WGS84). # Choosing 'None' will use value of first layer. reference_projection = Projection(DEFAULT_PROJECTION) geotransform = None coordinates = None for layer in layer_files: # Ensure that projection is consistent across all layers if reference_projection is None: reference_projection = layer.projection else: msg = ('Projections in input layer %s is not as expected:\n' 'projection: %s\n' 'default: %s' '' % (layer, layer.projection, reference_projection)) assert reference_projection == layer.projection, msg # Ensure that geotransform and dimensions is consistent across # all *raster* layers if layer.is_raster: if geotransform is None: geotransform = layer.get_geotransform() else: msg = ('Geotransforms in input raster layers are different: ' '%s %s' % (geotransform, layer.get_geotransform())) # FIXME (Ole): Use high tolerance until we find out # why geoserver changes resolution. assert numpy.allclose(geotransform, layer.get_geotransform(), rtol=1.0e-1), msg # In either case of vector layers, we check that the coordinates # are the same if layer.is_vector: if coordinates is None: coordinates = layer.get_geometry() else: msg = ('Coordinates in input vector layers are different: ' '%s %s' % (coordinates, layer.get_geometry())) assert numpy.allclose(coordinates, layer.get_geometry()), msg msg = ('There are no data points to interpolate to. ' 'Perhaps zoom out or pan to the study area ' 'and try again') assert len(layer) > 0, msg # Check that arrays are aligned. # # We have observerd Geoserver resolution changes - see ticket:102 # https://github.com/AIFDR/riab/issues/102 # # However, both rasters are now downloaded with exactly the same # parameters since we have made bbox and resolution variable in ticket:103 # https://github.com/AIFDR/riab/issues/103 # # So if they are still not aligned, we raise an Exception # First find the minimum dimensions M = N = sys.maxint refname = '' for layer in layer_files: if layer.is_raster: if layer.rows < M: refname = layer.get_name() M = layer.rows if layer.columns < N: refname = layer.get_name() N = layer.columns # Then check for alignment for layer in layer_files: if layer.is_raster: data = layer.get_data() msg = ('Rasters are not aligned!\n' 'Raster %s has %i rows but raster %s has %i rows\n' 'Refer to issue #102' % (layer.get_name(), layer.rows, refname, M)) assert layer.rows == M, msg msg = ('Rasters are not aligned!\n' 'Raster %s has %i columns but raster %s has %i columns\n' 'Refer to issue #102' % (layer.get_name(), layer.columns, refname, N)) assert layer.columns == N, msg