From 0e6175426d6adf41055bdd38ad99b331f7cad779 Mon Sep 17 00:00:00 2001 From: swainn Date: Wed, 22 May 2019 22:48:08 -0600 Subject: [PATCH] Adds support for defining raster data type explicitly for GDAL rasters. --- mapkit/RasterConverter.py | 18 ++++++++++++--- mapkit/RasterLoader.py | 23 +++++++++++++++---- .../raster_converter_gdal_raster_format.py | 2 +- setup.py | 2 +- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/mapkit/RasterConverter.py b/mapkit/RasterConverter.py index 7ec6944..745aec5 100644 --- a/mapkit/RasterConverter.py +++ b/mapkit/RasterConverter.py @@ -29,6 +29,7 @@ class RasterConverter(object): MAX_HEX_DECIMAL = 255 NO_DATA_VALUE_MIN = float(-1.0) NO_DATA_VALUE_MAX = float(0.0) + GDAL_ASCII_DATA_TYPES = ['Int32', 'Float32', 'Float64'] def __init__(self, sqlAlchemyEngineOrSession, colorRamp=None): """ @@ -868,12 +869,23 @@ def getAsKmlPngAnimation(self, tableName, timeStampedRasters=[], rasterIdFieldNa return ET.tostring(kml), binaryPNGs - def getAsGrassAsciiRaster(self, tableName, rasterId=1, rasterIdFieldName='id', rasterFieldName='raster', newSRID=None): + def getAsGrassAsciiRaster(self, tableName, rasterId=1, rasterIdFieldName='id', rasterFieldName='raster', + newSRID=None, dataType=None): """ Returns a string representation of the raster in GRASS ASCII raster format. """ + options = {} + + if dataType: + if dataType not in self.GDAL_ASCII_DATA_TYPES: + raise ValueError('"{}" is not a valid data type. Must be one of "{}"'.format( + dataType, '", "'.join(self.GDAL_ASCII_DATA_TYPES))) + + options = {'AAIGRID_DATATYPE': dataType} + # Get raster in ArcInfo Grid format - arcInfoGrid = self.getAsGdalRaster(rasterFieldName, tableName, rasterIdFieldName, rasterId, 'AAIGrid', newSRID).splitlines() + arcInfoGrid = self.getAsGdalRaster(rasterFieldName, tableName, rasterIdFieldName, rasterId, 'AAIGrid', newSRID, + **options).splitlines() ## Convert arcInfoGrid to GRASS ASCII format ## # Get values from header which look something this: @@ -950,7 +962,7 @@ def getAsGdalRaster(self, rasterFieldName, tableName, rasterIdFieldName, rasterI # Compile options if kwargs: optionsList = [] - for key, value in kwargs.iteritems(): + for key, value in kwargs.items(): kwargString = "'{0}={1}'".format(key, value) optionsList.append(kwargString) diff --git a/mapkit/RasterLoader.py b/mapkit/RasterLoader.py index af6b854..ef71fcb 100644 --- a/mapkit/RasterLoader.py +++ b/mapkit/RasterLoader.py @@ -24,6 +24,7 @@ class RasterLoader(object): PostGIS table with a raster field. If the table does not exist already, it will be created. ''' + RASTER_DATA_TYPES = ["1BB", "2BUI", "4BUI", "8BSI", "8BUI", "16BSI", "16BUI", "32BSI", "32BUI", "32BF", "64BF"] def __init__(self, engine, raster2pgsql=''): ''' @@ -111,10 +112,15 @@ def rasterToWKB(cls, rasterPath, srid, noData, raster2pgsql): return wellKnownBinary @classmethod - def grassAsciiRasterToWKB(cls, session, grassRasterPath, srid, noData=0): + def grassAsciiRasterToWKB(cls, session, grassRasterPath, srid, noData=0, dataType='32BF'): """ Load GRASS ASCII rasters directly using the makeSingleBandWKBRaster method. Do this to eliminate the raster2pgsql dependency. + :param session: SQLAlchemy session object bound to a PostGIS enabled database + :param grassRasterPath: Path to the GRASS ASCII raster to read into the database. + :param srid: SRID of the raster + :param noData: Value of cells to be considered as cells containing no cells + :param dataType: Data type of the values of the raster. One of "1BB", "2BUI", "4BUI", "8BSI", "8BUI", "16BSI", "16BUI", "32BSI", "32BUI", "32BF", or "64BF". Defaults to "32BF". """ # Constants NUM_HEADER_LINES = 6 @@ -173,12 +179,13 @@ def grassAsciiRasterToWKB(cls, session, grassRasterPath, srid, noData=0): skewX=0, skewY=0, srid=srid, dataArray=dataArrayString, - noDataValue=noData) + noDataValue=noData, + dataType=dataType) return wellKnownBinary @classmethod - def makeSingleBandWKBRaster(cls, session, width, height, upperLeftX, upperLeftY, cellSizeX, cellSizeY, skewX, skewY, srid, dataArray, initialValue=None, noDataValue=None): + def makeSingleBandWKBRaster(cls, session, width, height, upperLeftX, upperLeftY, cellSizeX, cellSizeY, skewX, skewY, srid, dataArray, initialValue=None, noDataValue=None, dataType='32BF'): """ Generate Well Known Binary via SQL. Must be used on a PostGIS database as it relies on several PostGIS database functions. @@ -195,6 +202,7 @@ def makeSingleBandWKBRaster(cls, session, width, height, upperLeftX, upperLeftY, :param initialValue: Initial / default value of the raster cells :param noDataValue: Value of cells to be considered as cells containing no cells :param dataArray: 2-dimensional list of values or a string representation of a 2-dimensional list that will be used to populate the raster values + :param dataType: Data type of the values of the raster. One of "1BB", "2BUI", "4BUI", "8BSI", "8BUI", "16BSI", "16BUI", "32BSI", "32BUI", "32BF", or "64BF". Defaults to "32BF". """ # Stringify the data array if isinstance(dataArray, str): @@ -209,6 +217,10 @@ def makeSingleBandWKBRaster(cls, session, width, height, upperLeftX, upperLeftY, if noDataValue is None: noDataValue = 'NULL' + if dataType not in cls.RASTER_DATA_TYPES: + raise ValueError('"{}" is not a valid raster data type. Must be one of "{}"'.format( + dataType, '" ,"'.join(cls.RASTER_DATA_TYPES))) + # Cell size in the Y direction must be negative if cellSizeY > 0: print('RASTER LOADER WARNING: cellSizeY should be defined as negative.') @@ -219,7 +231,7 @@ def makeSingleBandWKBRaster(cls, session, width, height, upperLeftX, upperLeftY, SELECT ST_SetValues( ST_AddBand( ST_MakeEmptyRaster({0}::integer, {1}::integer, {2}, {3}, {4}, {5}, {6}, {7}, {8}::integer), - 1::integer, '32BF'::text, {9}::double precision, {10}::double precision + 1::integer, '{12}'::text, {9}::double precision, {10}::double precision ), 1, 1, 1, ARRAY{11}::double precision[][] ); @@ -234,7 +246,8 @@ def makeSingleBandWKBRaster(cls, session, width, height, upperLeftX, upperLeftY, srid, initialValue, noDataValue, - dataArrayString) + dataArrayString, + dataType) result = session.execute(statement) diff --git a/mapkit/examples/raster_converter_gdal_raster_format.py b/mapkit/examples/raster_converter_gdal_raster_format.py index f1d516d..57309b5 100644 --- a/mapkit/examples/raster_converter_gdal_raster_format.py +++ b/mapkit/examples/raster_converter_gdal_raster_format.py @@ -11,7 +11,7 @@ # Get supported gdal raster formats gdalFormats = RasterConverter.supportedGdalRasterFormats(engine) -for key, value in gdalFormats.iteritems(): +for key, value in gdalFormats.items(): print key, value # Configure raster converter diff --git a/setup.py b/setup.py index 91e910a..f7de205 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ ] setup(name='mapkit', - version='1.2.3', + version='1.2.4', description='Mapping tools for PostGIS-enabled PostgreSQL databases.', long_description='', author='Nathan Swain',