def test_insert_column_color(self): rat = get_rat(self.raster_layer_color, 1) self.assertTrue(rat.isValid()) model = RATModel(rat) tester = QAbstractItemModelTester( model, QAbstractItemModelTester.FailureReportingMode.Warning) column_count = model.columnCount(QModelIndex()) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_String) self.assertTrue(model.insert_column(3, field)[0]) self.assertEqual(model.columnCount(QModelIndex()), column_count + 1) self.assertEqual(model.headers.index('f1'), 3) # Error field = RATField('f1', gdal.GFU_Generic, gdal.GFT_String) self.assertFalse(model.insert_column(3, field)[0]) self.assertEqual(model.columnCount(QModelIndex()), column_count + 1)
def test_insert_column_dbf(self): rat = get_rat(self.raster_layer_dbf, 1) self.assertTrue(rat.isValid()) self.assertEqual(len(rat.keys), 17) # has color, so fields are one more than keys self.assertEqual(len(rat.keys), len(rat.fields) + 1) # Not valid insertions field = RATField('f1', gdal.GFU_MinMax, gdal.GFT_Real) self.assertFalse(rat.insert_column(4, field)[0]) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(0, field)[0]) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(1, field)[0]) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(100, field)[0]) field = RATField('SYSTMGRPNA', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(4, field)[0]) # Valid insertions field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertEqual(field.qgis_type, QVariant.Double) self.assertTrue(rat.insert_column(4, field)[0]) self.assertIn('f1', rat.fields.keys()) self.assertEqual(len(rat.data['f1']), len(rat.data['VALUE'])) field = RATField('f2', gdal.GFU_Generic, gdal.GFT_Integer) self.assertEqual(field.qgis_type, QVariant.Int) self.assertTrue(rat.insert_column(2, field)[0]) self.assertIn('f2', rat.fields.keys()) self.assertEqual(len(rat.data['f2']), len(rat.data['VALUE'])) self.assertEqual(rat.data['f2'][0], 0) field = RATField('f3', gdal.GFU_Generic, gdal.GFT_String) self.assertEqual(field.qgis_type, QVariant.String) self.assertTrue(rat.insert_column(len(rat.keys) - 1, field)[0]) self.assertIn('f3', rat.fields.keys()) self.assertEqual(len(rat.data['f3']), len(rat.data['VALUE'])) self.assertEqual(rat.data['f3'][0], '') field = RATField('R', gdal.GFU_Red, gdal.GFT_Integer) self.assertFalse(rat.insert_column(len(rat.keys) - 1, field)[0])
def test_insert_column(self): rat = get_rat(self.raster_layer, 1) self.assertTrue(rat.isValid()) self.assertEqual(len(rat.keys), 16) self.assertEqual(len(rat.keys), len(rat.fields)) # Not valid insertions field = RATField('f1', gdal.GFU_MinMax, gdal.GFT_Real) self.assertFalse(rat.insert_column(4, field)[0]) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(0, field)[0]) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(1, field)[0]) field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(100, field)[0]) field = RATField('significant_features', gdal.GFU_Generic, gdal.GFT_Real) self.assertFalse(rat.insert_column(4, field)[0]) # Valid insertions field = RATField('f1', gdal.GFU_Generic, gdal.GFT_Real) self.assertEqual(field.qgis_type, QVariant.Double) self.assertTrue(rat.insert_column(4, field)[0]) self.assertIn('f1', rat.fields.keys()) self.assertEqual(len(rat.data['f1']), len(rat.data['Value'])) field = RATField('f2', gdal.GFU_Generic, gdal.GFT_Integer) self.assertEqual(field.qgis_type, QVariant.Int) self.assertTrue(rat.insert_column(2, field)[0]) self.assertIn('f2', rat.fields.keys()) self.assertEqual(len(rat.data['f2']), len(rat.data['Value'])) self.assertEqual(rat.data['f2'][0], 0) field = RATField('f3', gdal.GFU_Generic, gdal.GFT_String) self.assertEqual(field.qgis_type, QVariant.String) self.assertTrue(rat.insert_column(len(rat.keys) - 1, field)[0]) self.assertIn('f3', rat.fields.keys()) self.assertEqual(len(rat.data['f3']), len(rat.data['Value'])) self.assertEqual(rat.data['f3'][0], '')
def addColumn(self): dlg = AddColumnDialog(self.model, self.iface) if self.model.has_color: dlg.mColumnType.hide() # List columns where insertion is allowed: skip value and count for field in self.model.rat.fields.values(): if field.usage not in {gdal.GFU_MinMax, gdal.GFU_PixelCount}: dlg.mColumn.addItem(field.name) # List allowed usages allowed_usages = self.allowedAddedUsages() # Set insertion point col = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).column() header = self.model.headers[col] dlg.mColumn.setCurrentIndex(dlg.mColumn.findText(header)) if not allowed_usages: if not self.model.has_color: dlg.mColor.setChecked(True) dlg.mStandardColumn.setEnabled(False) else: # We cannot add any field, we should never get here! rat_log( 'Cannot add any column: this should have been checked before getting so far!', Qgis.Critical) else: usages_info = rat_column_info() for usage in allowed_usages: dlg.mUsage.addItem(usages_info[usage]['name'], usage) if dlg.exec_() == QDialog.Accepted: position = dlg.mColumn.currentText() after = dlg.mAfter.isChecked() insertion_point = self.model.headers.index(position) + (1 if after else 0) if dlg.mColor.isChecked(): if not self.model.insert_color(insertion_point): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Colors"), QCoreApplication.translate( 'RAT', "An error occourred while adding colors to the RAT!" )) else: # Try to update colors from current raster self.model.rat.update_colors_from_raster(self.raster_layer) self.is_dirty = True else: data_type = dlg.mDataType.currentData() usage = dlg.mUsage.currentData() name = dlg.mName.text() field = RATField(name, usage, data_type) result, error_message = self.model.insert_column( insertion_point, field) if not result: QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Column"), QCoreApplication.translate( 'RAT', "An error occourred while adding a column to the RAT: %s" % error_message)) else: self.is_dirty = True
def create_rat_from_raster(raster_layer, is_dbf, path, feedback=QgsRasterBlockFeedback()) -> RAT: """Creates a new RAT object from a raster layer, an invalid RAT is returned in case of errors. :param raster_layer: raster layer :type raster_layer: QgsRasterLayer :param is_dbf: raster layer :type is_dbf: bool :return: new RAT :rtype: RAT """ if not can_create_rat(raster_layer): return RAT() renderer = raster_layer.renderer() band = renderer.band() is_range = False has_histogram = True if isinstance(renderer, QgsPalettedRasterRenderer): classes = renderer.classes() elif isinstance(renderer, QgsSingleBandPseudoColorRenderer): shader = renderer.shader() if not shader: rat_log('Invalid shader for renderer: %s' % renderer) return RAT() func = shader.rasterShaderFunction() classes = func.colorRampItemList() is_range = True has_histogram = False else: rat_log('Unsupported renderer: %s' % renderer) return RAT() if len(classes) == 0: return RAT() is_real = isinstance(classes[0].value, float) # If we have a range we have no histogram and separate min/max if is_range: fields = { 'Value Min': RATField('Value Min', gdal.GFU_Min, gdal.GFT_Real if is_real else gdal.GFT_Integer), 'Value Max': RATField('Value Max', gdal.GFU_Max, gdal.GFT_Real if is_real else gdal.GFT_Integer), } data = { RAT_COLOR_HEADER_NAME: [], 'Value Min': [], 'Value Max': [], } # Store min and max stats = raster_layer.dataProvider().bandStatistics( band, QgsRasterBandStats.Min | QgsRasterBandStats.Max, raster_layer.extent(), 0) # Set as min float value min_value = -3.40282e+38 # unused: max_value = stats.maximumValue else: fields = { 'Value': RATField('Value', gdal.GFU_MinMax, gdal.GFT_Real if is_real else gdal.GFT_Integer), } data = { RAT_COLOR_HEADER_NAME: [], 'Value': [] } histogram = raster_layer.dataProvider().histogram(band, feedback=feedback) histogram_values = [] if histogram.valid: fields['Count'] = RATField( 'Count', gdal.GFU_PixelCount, gdal.GFT_Integer) data['Count'] = [] vector_is_complete = (len(histogram.histogramVector) == len(classes)) for val in histogram.histogramVector: if val != 0 or vector_is_complete: histogram_values.append(val) fields['Class'] = RATField('Class', gdal.GFU_Name, gdal.GFT_String) data['Class'] = [] fields['R'] = RATField('R', gdal.GFU_Red, gdal.GFT_Integer) fields['G'] = RATField('G', gdal.GFU_Green, gdal.GFT_Integer) fields['B'] = RATField('B', gdal.GFU_Blue, gdal.GFT_Integer) fields['A'] = RATField('A', gdal.GFU_Alpha, gdal.GFT_Integer) data['R'] = [] data['G'] = [] data['B'] = [] data['A'] = [] i = 0 for klass in classes: data[RAT_COLOR_HEADER_NAME].append(klass.color) value = klass.value if klass.value != float('inf') else 3.40282e+38 if is_range: data['Value Min'].append(min_value) data['Value Max'].append(value) min_value = value else: data['Value'].append(value) if has_histogram: data['Count'].append(histogram_values[i]) data['Class'].append(klass.label) data['R'].append(klass.color.red()) data['G'].append(klass.color.green()) data['B'].append(klass.color.blue()) data['A'].append(klass.color.alpha()) i += 1 return RAT(data, is_dbf, fields, path)
def get_rat(raster_layer, band, colors=('R', 'G', 'B', 'A')): """Extracts RAT from raster layer and given band :param raster_layer: the raster layer to classify :type raster_layer: QgsRasterLayer :param band: band number (1-based) :type band: int :param colors: default name of the RGB(A) columns for sidecar DBF files, defaults to ('R', 'G', 'B', 'A'), these are searched first :type red_column_name: tuple, optional :return: RAT :rtype: RAT """ headers = [] values = {} fields = {} # For sidecar files path = None COLOR_ROLES = (gdal.GFU_Red, gdal.GFU_Green, gdal.GFU_Blue, gdal.GFU_Alpha) is_dbf = False ds = gdal.OpenEx(raster_layer.source()) if ds: band = ds.GetRasterBand(band) if band: rat = band.GetDefaultRAT() if rat is not None: for i in range(0, rat.GetColumnCount()): column = rat.GetNameOfCol(i) headers.append(column) values[column] = [] fields[column] = RATField( column, rat.GetUsageOfCol(i), rat.GetTypeOfCol(i)) for r in range(0, rat.GetRowCount()): for c in range(0, rat.GetColumnCount()): column = headers[c] if fields[column].type == gdal.GFT_Integer: values[headers[c]].append(rat.GetValueAsInt(r, c)) elif fields[column].type == gdal.GFT_Real: values[headers[c]].append( rat.GetValueAsDouble(r, c)) else: values[headers[c]].append( html.unescape(rat.GetValueAsString(r, c))) # Try to identify fields in case of RAT with wrong usages usages = [f.usage for f in fields.values()] if gdal.GFU_MinMax not in usages and not {gdal.GFU_Min, gdal.GFU_Max}.issubset(usages): try: field_name = [f.name for f in fields.values() if f.name.upper() == 'VALUE'][0] fields[field_name].usage = gdal.GFU_MinMax except IndexError: pass try: field_name = [f.name for f in fields.values() if f.name.upper() in ('VALUE MIN', 'MIN', 'MIN VALUE', 'VALUE_MIN', 'MIN_VALUE')][0] fields[field_name].usage = gdal.GFU_Min except IndexError: pass try: field_name = [f.name for f in fields.values() if f.name.upper() in ('VALUE MAX', 'MAX', 'MAX VALUE', 'VALUE_MAX', 'MAX_VALUE')][0] fields[field_name].usage = gdal.GFU_Max except IndexError: pass if gdal.GFU_PixelCount not in usages: try: field_name = [f.name for f in fields.values() if f.name.upper() == 'COUNT'][0] fields[field_name].usage = gdal.GFU_PixelCount except IndexError: pass path = raster_layer.source() + '.aux.xml' # Search for sidecar DBF files, `band` is ignored! if not values: info = QFileInfo(raster_layer.publicSource()) directory = info.dir().path() basename = info.baseName() filename = info.fileName() candidates = (basename + '.dbf', basename + '.vat.dbf', filename + '.dbf', filename + '.vat.dbf') for candidate in candidates: if os.path.exists(os.path.join(directory, candidate)): rat_layer = QgsVectorLayer(os.path.join( directory, candidate), 'rat', 'ogr') if rat_layer.isValid(): path = os.path.join(directory, candidate) # Get fields # Check if color fields are there, fall-back to RED GREEN BLUE ALPHA if not field_upper_names = [f.name().upper() for f in rat_layer.fields()] upper_colors = [c.upper() for c in colors] def _search_color(): color_found = True for color_field_name in upper_colors[:3]: if color_field_name not in field_upper_names: color_found = False return color_found if not _search_color() and colors == ('R', 'G', 'B', 'A'): upper_colors = ('RED', 'GREEN', 'BLUE', 'ALPHA') # Create fields for f in rat_layer.fields(): headers.append(f.name()) field_name_upper = f.name().upper() if field_name_upper in upper_colors: fields[f.name()] = RATField( f.name(), COLOR_ROLES[upper_colors.index(field_name_upper)], gdal.GFT_Integer if f.type() in (QVariant.Int, QVariant.LongLong) else gdal.GFT_Real) elif field_name_upper == 'COUNT': fields[f.name()] = RATField( f.name(), gdal.GFU_PixelCount, gdal.GFT_Integer) elif field_name_upper == 'VALUE': fields[f.name()] = RATField( f.name(), gdal.GFU_MinMax, gdal.GFT_Integer if f.type() in (QVariant.Int, QVariant.LongLong) else gdal.GFT_Real) elif field_name_upper in ('VALUE MIN', 'VALUE_MIN', 'MIN VALUE', 'MIN_VALUE'): fields[f.name()] = RATField( f.name(), gdal.GFU_Min, gdal.GFT_Integer if f.type() in (QVariant.Int, QVariant.LongLong) else gdal.GFT_Real) elif field_name_upper in ('VALUE MAX', 'VALUE_MAX', 'MAX VALUE', 'MAX_VALUE'): fields[f.name()] = RATField( f.name(), gdal.GFU_Max, gdal.GFT_Integer if f.type() in (QVariant.Int, QVariant.LongLong) else gdal.GFT_Real) else: if f.type() in (QVariant.Int, QVariant.LongLong): type = gdal.GFT_Integer elif f.type() == QVariant.Double: type = gdal.GFT_Real else: type = gdal.GFT_String fields[f.name()] = RATField( f.name(), gdal.GFU_Generic, type) for header in headers: values[header] = [] for f in rat_layer.getFeatures(): for header in headers: values[header].append(f.attribute(header)) is_dbf = True break # Colors if headers: red = None green = None blue = None alpha = None is_integer = False for name, f in fields.items(): if f.usage == gdal.GFU_Red: red = name is_integer = f.type == gdal.GFT_Integer continue if f.usage == gdal.GFU_Green: green = name continue if f.usage == gdal.GFU_Blue: blue = name continue if f.usage == gdal.GFU_Alpha: alpha = name continue if red and green and blue: headers.append(RAT_COLOR_HEADER_NAME) values[RAT_COLOR_HEADER_NAME] = [] for i in range(len(values[red])): func = 'fromRgb' if is_integer else 'fromRgbF' if alpha: values[RAT_COLOR_HEADER_NAME].append(getattr(QColor, func)( values[red][i], values[green][i], values[blue][i], values[alpha][i])) else: values[RAT_COLOR_HEADER_NAME].append(getattr(QColor, func)( values[red][i], values[green][i], values[blue][i])) return RAT(values, is_dbf, fields, path)
def get_rat(raster_layer, band, colors=('R', 'G', 'B', 'A')): """Extracts RAT from raster layer and given band :param raster_layer: the raster layer to classify :type raster_layer: QgsRasterLayer :param band: band number (1-based) :type band: int :param colors: name of the RGB(A) columns for sidecar DBF files, defaults to ('R', 'G', 'B', 'A') :type red_column_name: tuple, optional :return: RAT :rtype: RAT """ headers = [] values = {} fields = {} # For sidecar files path = None COLOR_ROLES = (gdal.GFU_Red, gdal.GFU_Green, gdal.GFU_Blue, gdal.GFU_Alpha) is_sidecar = False ds = gdal.OpenEx(raster_layer.source()) if ds: band = ds.GetRasterBand(band) if band: rat = band.GetDefaultRAT() if rat is not None: for i in range(0, rat.GetColumnCount()): column = rat.GetNameOfCol(i) headers.append(column) values[column] = [] fields[column] = RATField(column, rat.GetUsageOfCol(i), rat.GetTypeOfCol(i)) for r in range(0, rat.GetRowCount()): for c in range(0, rat.GetColumnCount()): column = headers[c] if fields[column].type == gdal.GFT_Integer: values[headers[c]].append(rat.GetValueAsInt(r, c)) elif fields[column].type == gdal.GFT_Real: values[headers[c]].append( rat.GetValueAsDouble(r, c)) else: values[headers[c]].append( html.unescape(rat.GetValueAsString(r, c))) path = raster_layer.source() + '.aux.xml' # Search for sidecar DBF files, `band` is ignored! if not values: info = QFileInfo(raster_layer.publicSource()) directory = info.dir().path() basename = info.baseName() filename = info.fileName() candidates = (basename + '.dbf', basename + '.vat.dbf', filename + '.dbf', filename + '.vat.dbf') for candidate in candidates: if os.path.exists(os.path.join(directory, candidate)): rat_layer = QgsVectorLayer(os.path.join(directory, candidate), 'rat', 'ogr') if rat_layer.isValid(): path = os.path.join(directory, candidate) for f in rat_layer.fields(): headers.append(f.name()) if f.name().upper() in colors: fields[f.name()] = RATField( f.name(), COLOR_ROLES[colors.index(f.name().upper())], gdal.GFT_Integer if f.type() in (QVariant.Int, QVariant.LongLong) else gdal.GFT_Real) elif f.name().upper() == 'COUNT': fields[f.name()] = RATField( f.name(), gdal.GFU_PixelCount, gdal.GFT_Integer) elif f.name().upper() == 'VALUE': fields[f.name()] = RATField( f.name(), gdal.GFU_MinMax, gdal.GFT_Integer if f.type() in (QVariant.Int, QVariant.LongLong) else gdal.GFT_Real) else: if f.type() in (QVariant.Int, QVariant.LongLong): type = gdal.GFT_Integer elif f.type() == QVariant.Double: type = gdal.GFT_Real else: type = gdal.GFT_String fields[f.name()] = RATField( f.name(), gdal.GFU_Generic, type) for header in headers: values[header] = [] for f in rat_layer.getFeatures(): for header in headers: values[header].append(f.attribute(header)) is_sidecar = True break # Colors if headers: red = None green = None blue = None alpha = None is_integer = False for name, f in fields.items(): if f.usage == gdal.GFU_Red: red = name is_integer = f.type == gdal.GFT_Integer continue if f.usage == gdal.GFU_Green: green = name continue if f.usage == gdal.GFU_Blue: blue = name continue if f.usage == gdal.GFU_Alpha: alpha = name continue if red and green and blue: headers.append(RAT_COLOR_HEADER_NAME) values[RAT_COLOR_HEADER_NAME] = [] for i in range(len(values[red])): func = 'fromRgb' if is_integer else 'fromRgbF' if alpha: values[RAT_COLOR_HEADER_NAME].append( getattr(QColor, func)(values[red][i], values[green][i], values[blue][i], values[alpha][i])) else: values[RAT_COLOR_HEADER_NAME].append( getattr(QColor, func)(values[red][i], values[green][i], values[blue][i])) return RAT(values, is_sidecar, fields, path)
dst_ds = gdal.GetDriverByName('GTiff').Create( os.path.join(dest, '2x2_2_BANDS_INT16.tif'), ny, nx, 2, gdal.GDT_Int16) dst_ds.SetGeoTransform(geotransform) # specify coords srs = osr.SpatialReference() # establish encoding srs.ImportFromEPSG(3857) # WGS84 lat/long dst_ds.SetProjection(srs.ExportToWkt()) # export coords to file dst_ds.GetRasterBand(1).WriteArray(r_pixels) # write r-band to the raster dst_ds.GetRasterBand(2).WriteArray(g_pixels) # write g-band to the raster dst_ds.FlushCache() # write to disk dst_ds = None # Create RAT fields = [] fields.append(RATField('Value', gdal.GFU_MinMax, gdal.GFT_Integer)) fields.append(RATField('Count', gdal.GFU_PixelCount, gdal.GFT_Integer)) fields.append(RATField('Class', gdal.GFU_Name, gdal.GFT_String)) fields.append(RATField('Class2', gdal.GFU_Name, gdal.GFT_String)) fields.append(RATField('Class3', gdal.GFU_Generic, gdal.GFT_String)) fields.append(RATField('Red', gdal.GFU_Red, gdal.GFT_Integer)) fields.append(RATField('Green', gdal.GFU_Green, gdal.GFT_Integer)) fields.append(RATField('Blue', gdal.GFU_Blue, gdal.GFT_Integer)) fields = {field.name: field for field in fields} data = { 'Value': [0, 2, 4], 'Count': [1, 1, 2], 'Class': ['zero', 'one', 'two'], 'Class2': ['zero2', 'one2', 'two2'],