def max_zoom(self): # Return manual override value if provided if self.rasterlayer.max_zoom is not None: return self.rasterlayer.max_zoom # Get max zoom from metadata if not hasattr(self.rasterlayer, 'metadata'): raise RasterException('Could not determine max zoom level.') max_zoom = self.rasterlayer.metadata.max_zoom # Reduce max zoom by one if zoomdown flag was disabled if not self.rasterlayer.next_higher: max_zoom -= 1 return max_zoom
def hex_to_rgba(value, alpha=255): """ Converts a HEX color string to a RGBA 4-tuple. """ value = value.lstrip('#') # Check length and input string property if len(value) not in [1, 2, 3, 6] or not value.isalnum(): raise RasterException('Invalid color, could not convert hex to rgb.') # Repeat values for shortened input value = (value * 6)[:6] # Convert to rgb return int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16), alpha
def extract_metadata(self): """ Extract and store metadata for the raster and its bands. """ self.log('Extracting metadata from raster.') # Try to compute max zoom try: max_zoom = self.compute_max_zoom() except GDALException: raise RasterException( 'Failed to compute max zoom. Check the SRID of the raster.') # Extract global raster metadata meta = self.rasterlayer.metadata meta.uperleftx = self.dataset.origin.x meta.uperlefty = self.dataset.origin.y meta.width = self.dataset.width meta.height = self.dataset.height meta.scalex = self.dataset.scale.x meta.scaley = self.dataset.scale.y meta.skewx = self.dataset.skew.x meta.skewy = self.dataset.skew.y meta.numbands = len(self.dataset.bands) meta.srs_wkt = self.dataset.srs.wkt meta.srid = self.dataset.srs.srid meta.max_zoom = max_zoom meta.save() # Extract band metadata for i, band in enumerate(self.dataset.bands): bandmeta = RasterLayerBandMetadata.objects.filter( rasterlayer=self.rasterlayer, band=i).first() if not bandmeta: bandmeta = RasterLayerBandMetadata( rasterlayer=self.rasterlayer, band=i) bandmeta.nodata_value = band.nodata_value bandmeta.min = band.min bandmeta.max = band.max # Depending on Django version, the band statistics include std and mean. if hasattr(band, 'std'): bandmeta.std = band.std if hasattr(band, 'mean'): bandmeta.mean = band.mean bandmeta.save() self.log('Finished extracting metadata from raster.')
def open_raster_file(self): """ Get raster source file to extract tiles from. This makes a local copy of rasterfile, unzips the raster and reprojects it into web mercator if necessary. The reprojected raster is stored for reuse such that reprojection does only happen once. The local copy of the raster is needed if files are stored on remote storages. """ reproj, created = RasterLayerReprojected.objects.get_or_create( rasterlayer=self.rasterlayer) # Check if the raster has already been reprojected has_reprojected = reproj.rasterfile.name not in (None, '') # Create workdir raster_workdir = getattr(settings, 'RASTER_WORKDIR', None) self.tmpdir = tempfile.mkdtemp(dir=raster_workdir) # Choose source for raster data, use the reprojected version if it exists. if self.rasterlayer.source_url and not has_reprojected: url = self.rasterlayer.source_url if url.lower().startswith('http') or url.startswith('file'): url_path = urlparse(self.rasterlayer.source_url).path filename = url_path.split('/')[-1] filepath = os.path.join(self.tmpdir, filename) urlretrieve(self.rasterlayer.source_url, filepath) elif url.startswith('s3'): # Get the bucket name and file key, assuming the following url # strucure: s3://BUCKET_NAME/BUCKET_KEY bucket_name = url.split('s3://')[1].split('/')[0] bucket_key = '/'.join(url.split('s3://')[1].split('/')[1:]) # Assume the file name is the last piece of the key. filename = bucket_key.split('/')[-1] filepath = os.path.join(self.tmpdir, filename) # Get file from s3. s3 = boto3.resource('s3', endpoint_url=self.s3_endpoint_url) bucket = s3.Bucket(bucket_name) bucket.download_file(bucket_key, filepath, ExtraArgs={'RequestPayer': 'requester'}) else: raise RasterException( 'Only http(s) and s3 urls are supported.') else: if has_reprojected: rasterfile_source = reproj.rasterfile else: rasterfile_source = self.rasterlayer.rasterfile if not rasterfile_source.name: raise RasterException( 'No data source found. Provide a rasterfile or a source url.' ) # Copy raster file source to local folder filepath = os.path.join(self.tmpdir, os.path.basename(rasterfile_source.name)) rasterfile = open(filepath, 'wb') for chunk in rasterfile_source.chunks(): rasterfile.write(chunk) rasterfile.close() # If the raster file is compressed, decompress it, otherwise try to # open the source file directly. if os.path.splitext(filepath)[1].lower() == '.zip': # Open and extract zipfile zf = zipfile.ZipFile(filepath) zf.extractall(self.tmpdir) # Remove zipfile os.remove(filepath) # Get filelist from directory matches = [] for root, dirnames, filenames in os.walk(self.tmpdir): for filename in fnmatch.filter(filenames, '*.*'): matches.append(os.path.join(root, filename)) # Open the first raster file found in the matched files. self.dataset = None for match in matches: try: self.dataset = GDALRaster(match) break except GDALException: pass # Raise exception if no file could be opened by gdal. if not self.dataset: raise RasterException('Could not open rasterfile.') else: self.dataset = GDALRaster(filepath) # Override srid if provided if self.rasterlayer.srid: try: self.dataset = GDALRaster(self.dataset.name, write=True) except GDALException: raise RasterException( 'Could not override srid because the driver for this ' 'type of raster does not support write mode.') self.dataset.srs = self.rasterlayer.srid