def addDHMasWMS(self): crs = self.gh.getGetMapCrs(self.iface).authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' dhmUrl = "url=https://geoservices.informatievlaanderen.be/raadpleegdiensten/DHMV/wms&layers=DHMVII_DTM_1m&&format=image/png&styles=default&crs="+ crs try: rlayer = QgsRasterLayer(dhmUrl, 'Hoogtemodel', 'wms') if rlayer.isValid(): rlayer.renderer().setOpacity(0.8) QgsProject.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisElevationDialog", "Kan WMS niet laden"), level=Qgis.Critical, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=Qgis.Critical, duration=10) return
def display_one_band(layer, keyword, iface): index_group = TerreImageConstant().index_group logger.debug("keyword " + str(keyword)) corres = {'red': "_bande_rouge", 'green': "_bande_verte", 'blue': "_bande_bleue", 'pir': "_bande_pir", 'mir': "_bande_mir", "nat": "_couleurs_naturelles"} raster_layer = QgsRasterLayer(layer.get_source(), layer.name() + corres[keyword]) if keyword == 'nat': logger.debug("display on natural colors") band_red = layer.bands['red'] band_green = layer.bands['green'] band_blue = layer.bands['blue'] renderer = raster_layer.renderer() # raster_layer.setDrawingStyle("MultiBandColor") renderer.setRedBand(band_red) renderer.setGreenBand(band_green) renderer.setBlueBand(band_blue) # raster_layer.setRenderer( renderer ) # contrastForRasters( raster_layer, 0, 0, [pir, red, green] ) histogram_stretching(raster_layer, iface.mapCanvas()) QgsMapLayerRegistry.instance().addMapLayer(raster_layer) TerreImageConstant().legendInterface.moveLayer(raster_layer, index_group) return raster_layer else: band = layer.bands[keyword] if band: logger.debug("band num: " + str(band)) raster_layer.setDrawingStyle("MultiBandSingleBandGray") renderer = raster_layer.renderer() logger.debug(renderer) renderer.setGrayBand(band) # contrastForRasters( raster_layer, 0, 0 ) histogram_stretching(raster_layer, iface.mapCanvas()) QgsMapLayerRegistry.instance().addMapLayer(raster_layer) TerreImageConstant().legendInterface.moveLayer(raster_layer, index_group) return raster_layer
def test_setRenderer(self): myPath = os.path.join(unitTestDataPath("raster"), "band1_float32_noct_epsg4326.tif") myFileInfo = QFileInfo(myPath) myBaseName = myFileInfo.baseName() layer = QgsRasterLayer(myPath, myBaseName) self.rendererChanged = False layer.rendererChanged.connect(self.onRendererChanged) rShader = QgsRasterShader() r = QgsSingleBandPseudoColorRenderer(layer.dataProvider(), 1, rShader) layer.setRenderer(r) assert self.rendererChanged assert layer.renderer() == r
def testClone(self): myPath = os.path.join(unitTestDataPath('raster'), 'band1_float32_noct_epsg4326.tif') myFileInfo = QFileInfo(myPath) myBaseName = myFileInfo.baseName() layer = QgsRasterLayer(myPath, myBaseName) renderer = layer.renderer().clone() renderer.setOpacity(33.3) layer.setRenderer(renderer) # clone layer clone = layer.clone() # generate xml from layer layer_doc = QDomDocument("doc") layer_elem = layer_doc.createElement("maplayer") layer.writeLayerXml(layer_elem, layer_doc, QgsReadWriteContext()) # generate xml from clone clone_doc = QDomDocument("doc") clone_elem = clone_doc.createElement("maplayer") clone.writeLayerXml(clone_elem, clone_doc, QgsReadWriteContext()) # replace id within xml of clone clone_id_elem = clone_elem.firstChildElement("id") clone_id_elem_patch = clone_doc.createElement("id") clone_id_elem_patch_value = clone_doc.createTextNode(layer.id()) clone_id_elem_patch.appendChild(clone_id_elem_patch_value) clone_elem.replaceChild(clone_id_elem_patch, clone_id_elem) # update doc clone_doc.appendChild(clone_elem) layer_doc.appendChild(layer_elem) # compare xml documents self.assertEqual(layer_doc.toString(), clone_doc.toString())
def fix_style(layer: QgsRasterLayer) -> None: ''' Sets a sensible default style for loaded raster layers and fix up other issues. By default QGIS uses MultiBandColor renderer if there are multiple bands. (See https://github.com/qgis/QGIS/blob/final-3_0_1/src/core/raster/qgsrasterlayer.cpp#L729). This function picks the right renderers, either a palette or gray-band renderer. Also, fake categories/classes that exist due to GDAL limitations are removed. Search for UNUSED_CATEGORY_LABEL to find details. ''' provider = layer.dataProvider() # type: QgsRasterDataProvider color_interp = provider.colorInterpretation(1) is_palette = color_interp == QgsRaster.PaletteIndex # See the link below on how to create default-type renderers. # https://github.com/qgis/QGIS/blob/final-3_0_1/src/core/raster/qgsrasterrendererregistry.cpp#L128-L137 renderer = layer.renderer() # type: QgsRasterRenderer new_renderer = None if is_palette: # For paletted layers we always re-create the renderer even if it is already a # paletted renderer. This is because we need to remove the UNUSED categories. color_table = provider.colorTable(1) classes = QgsPalettedRasterRenderer.colorTableToClassData(color_table) if not any(c.label == gis4wrf.core.UNUSED_CATEGORY_LABEL for c in classes): return new_classes = filter( lambda c: c.label != gis4wrf.core.UNUSED_CATEGORY_LABEL, classes) new_renderer = QgsPalettedRasterRenderer(renderer.input(), 1, new_classes) layer.setRenderer(new_renderer) else: if not isinstance(renderer, QgsSingleBandGrayRenderer): new_renderer = QgsSingleBandGrayRenderer(renderer.input(), 1) layer.setRenderer(new_renderer) layer.setDefaultContrastEnhancement( ) # must be *after* setting the renderer
def test_homogenize_colors(self): """Test color homogenize""" tmp_dir = QTemporaryDir() shutil.copy( os.path.join(os.path.dirname(__file__), 'data', 'ExistingVegetationTypes_sample.img'), tmp_dir.path()) shutil.copy( os.path.join(os.path.dirname(__file__), 'data', 'ExistingVegetationTypes_sample.img.vat.dbf'), tmp_dir.path()) raster_layer = QgsRasterLayer( os.path.join(tmp_dir.path(), 'ExistingVegetationTypes_sample.img'), 'rat_test', 'gdal') rat = get_rat(raster_layer, 1) self.assertTrue(rat.isValid()) unique_labels = rat_classify(raster_layer, 1, rat, 'EVT_NAME') if Qgis.QGIS_VERSION_INT >= 31800: self.assertEqual(unique_labels, list(range(1, 60))) else: self.assertEqual(unique_labels, list(range(0, 59))) # Get color map color_map = {} for klass in raster_layer.renderer().classes(): color_map[klass.value] = klass.color.name() # Two different colors for EVT_NAME self.assertEqual(color_map[11.0], '#0000ff') self.assertEqual(color_map[12.0], '#9fa1f0') # Reclass unique_labels = rat_classify(raster_layer, 1, rat, 'NVCSCLASS') if Qgis.QGIS_VERSION_INT >= 31800: self.assertEqual(unique_labels, [1, 3, 5, 6, 7, 22, 31, 41, 44]) else: self.assertEqual(unique_labels, [0, 2, 4, 5, 6, 21, 30, 40, 43]) color_map = {} for klass in raster_layer.renderer().classes(): color_map[klass.value] = klass.color.name() # Same colors for NVCSCLASS self.assertEqual(color_map[11.0], '#0000ff') self.assertEqual(color_map[12.0], '#0000ff') # Manually change one color classes = raster_layer.renderer().classes() classes[0].color = QColor(10, 20, 30) renderer = QgsPalettedRasterRenderer(raster_layer.dataProvider(), 1, classes) raster_layer.setRenderer(renderer) color_map = {} for klass in raster_layer.renderer().classes(): color_map[klass.value] = klass.color.name() # Manually changed colors for NVCSCLASS self.assertEqual(color_map[11.0], '#0a141e') self.assertEqual(color_map[12.0], '#0000ff') self.assertTrue(homogenize_colors(raster_layer)) color_map = {} for klass in raster_layer.renderer().classes(): color_map[klass.value] = klass.color.name() # Same colors for NVCSCLASS self.assertEqual(color_map[11.0], '#0a141e') self.assertEqual(color_map[12.0], '#0a141e')
pcolor = [] pcolor.append(QgsColorRampShader.ColorRampItem(1, QColor("#d2ca97"))) pcolor.append(QgsColorRampShader.ColorRampItem(2, QColor("#f7f7f7"))) pcolor.append(QgsColorRampShader.ColorRampItem(3, QColor("#a1d99b"))) pcolor.append(QgsColorRampShader.ColorRampItem(4, QColor("#41ab5d"))) pcolor.append(QgsColorRampShader.ColorRampItem(5, QColor("#006d2c"))) pcolor.append(QgsColorRampShader.ColorRampItem(6, QColor("#00441b"))) renderer = QgsPalettedRasterRenderer(layer.dataProvider(), 1, QgsPalettedRasterRenderer.colorTableToClassData(pcolor)) layer.setRenderer(renderer) extent = layer.extent() width, height = layer.width(), layer.height() renderer = layer.renderer() provider = layer.dataProvider() crs = layer.crs().toWkt() pipe = QgsRasterPipe() pipe.set(provider.clone()) pipe.set(renderer.clone()) file_writer = QgsRasterFileWriter("C:/temp/naip/classified_res.tif") file_writer.writeRaster(pipe, width, height, extent, layer.crs()) print("Done!")
class TestQgsRasterRendererCreateSld(unittest.TestCase): """ This class tests the creation of SLD from QGis raster layers """ @classmethod def setUpClass(self): pass def setUp(self): pass def tearDown(self): pass def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'landsat.tif') rasterFileInfo = QFileInfo(myPath) self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) def testSingleBandPseudoColorRenderer_Interpolated(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics( bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Interpolated) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append( QgsColorRampShader.ColorRampItem( index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer( self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'ramp') colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) def testSingleBandPseudoColorRenderer_Discrete(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics( bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Discrete) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append( QgsColorRampShader.ColorRampItem( index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer( self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'intervals') colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) def testSingleBandPseudoColorRenderer_Exact(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics( bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append( QgsColorRampShader.ColorRampItem( index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer( self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'values') self.assertFalse(colorMap.hasAttribute('extendend')) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) # add check that is set ColoMap extended="true" if colormap is bigger that 255 entries # !NOTE! can't reuse previous shader => segmentation fault shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(255): items.append( QgsColorRampShader.ColorRampItem( index, QColor.fromHsv(index, 255, 255, 255), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer( self.raster_layer.dataProvider(), bandNo, shader) # self.raster_layer.setRenderer(rasterRenderer) # dom, root = self.rendererToSld(self.raster_layer.renderer()) # self.assertTrue( colorMap.hasAttribute( 'extendend' ) ) # self.assertEqual( colorMap.attribute( 'extendend' ), 'true' ) def testPalettedRasterRenderer(self): # create 10 color classes #classesString = '122 0 0 0 255 122\n123 1 1 1 255 123\n124 2 2 2 255 124\n125 3 3 3 255 125\n126 4 4 4 255 126\n127 5 5 5 255 127\n128 6 6 6 255 128\n129 7 7 7 255 129\n130 8 8 8 255 130' classesString = '' for index in range(10): classesString += '{0} {0} {0} {0} 255 {0}\n'.format(index) classes = QgsPalettedRasterRenderer.classDataFromString(classesString) rasterRenderer = QgsPalettedRasterRenderer( self.raster_layer.dataProvider(), 3, classes) self.raster_layer.setRenderer(rasterRenderer) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'values') self.assertFalse(colorMap.hasAttribute('extendend')) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) # add check that is set ColoMap extended="true" if colormap is bigger that 255 entries classesString = '' values = range(255) for index in range(255): classesString += '{0} {1} {1} {1} 255 {0}\n'.format( index, random.choice(values)) classes = QgsPalettedRasterRenderer.classDataFromString(classesString) rasterRenderer = QgsPalettedRasterRenderer( self.raster_layer.dataProvider(), 3, classes) self.raster_layer.setRenderer(rasterRenderer) dom, root = self.rendererToSld(self.raster_layer.renderer()) colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertTrue(colorMap.hasAttribute('extended')) self.assertEqual(colorMap.attribute('extended'), 'true') def testMultiBandColorRenderer(self): rasterRenderer = QgsMultiBandColorRenderer( self.raster_layer.dataProvider(), 3, 1, 2) self.raster_layer.setRenderer(rasterRenderer) self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.StretchToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:RedChannel', '3') self.assertChannelBand(root, 'sld:GreenChannel', '1') self.assertChannelBand(root, 'sld:BlueChannel', '2') def testSingleBandGrayRenderer(self): # check with StretchToMinimumMaximum rasterRenderer = QgsSingleBandGrayRenderer( self.raster_layer.dataProvider(), 3) self.raster_layer.setRenderer(rasterRenderer) self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.StretchToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) maximum = self.raster_layer.renderer().contrastEnhancement( ).maximumValue() minmum = self.raster_layer.renderer().contrastEnhancement( ).minimumValue() self.assertEqual(minmum, 51) self.assertEqual(maximum, 172) # check default values dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') elements = root.elementsByTagName('sld:ContrastEnhancement') self.assertEqual(len(elements), 1) enhancement = elements.at(0).toElement() self.assertFalse(enhancement.isNull()) normalize = enhancement.firstChildElement('sld:Normalize') self.assertFalse(normalize.isNull()) self.assertVendorOption(normalize, 'algorithm', 'StretchToMinimumMaximum') self.assertVendorOption(normalize, 'minValue', '51') self.assertVendorOption(normalize, 'maxValue', '172') elements = root.elementsByTagName('sld:ColorMap') self.assertEqual(len(elements), 1) colorMap = elements.at(0).toElement() self.assertFalse(colorMap.isNull()) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(len(colorMapEntries), 2) clorMap1 = colorMapEntries.at(0) self.assertEqual(clorMap1.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual( clorMap1.attributes().namedItem('quantity').nodeValue(), '0') clorMap2 = colorMapEntries.at(1) self.assertEqual(clorMap2.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual( clorMap2.attributes().namedItem('quantity').nodeValue(), '255') # check when StretchAndClipToMinimumMaximum # then min/max have always to be the real one and not that set in the contrastEnhancement self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.StretchAndClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) minmum = self.raster_layer.renderer().contrastEnhancement( ).setMinimumValue(100) maximum = self.raster_layer.renderer().contrastEnhancement( ).maximumValue() minmum = self.raster_layer.renderer().contrastEnhancement( ).minimumValue() self.assertEqual(minmum, 100) self.assertEqual(maximum, 172) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') elements = root.elementsByTagName('sld:ContrastEnhancement') self.assertEqual(len(elements), 1) enhancement = elements.at(0).toElement() self.assertFalse(enhancement.isNull()) normalize = enhancement.firstChildElement('sld:Normalize') self.assertFalse(normalize.isNull()) self.assertVendorOption(normalize, 'minValue', '51') self.assertVendorOption(normalize, 'maxValue', '172') elements = root.elementsByTagName('sld:ColorMap') self.assertEqual(len(elements), 1) colorMap = elements.at(0).toElement() self.assertFalse(colorMap.isNull()) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(len(colorMapEntries), 4) clorMap1 = colorMapEntries.at(0) self.assertEqual(clorMap1.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual( clorMap1.attributes().namedItem('quantity').nodeValue(), '100') self.assertEqual( clorMap1.attributes().namedItem('opacity').nodeValue(), '0') clorMap2 = colorMapEntries.at(1) self.assertEqual(clorMap2.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual( clorMap2.attributes().namedItem('quantity').nodeValue(), '100') clorMap3 = colorMapEntries.at(2) self.assertEqual(clorMap3.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual( clorMap3.attributes().namedItem('quantity').nodeValue(), '172') clorMap4 = colorMapEntries.at(3) self.assertEqual(clorMap4.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual( clorMap4.attributes().namedItem('quantity').nodeValue(), '172') self.assertEqual( clorMap4.attributes().namedItem('opacity').nodeValue(), '0') # check when ClipToMinimumMaximum # then min/max have always to be the real one and not that set in the contrastEnhancement self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.ClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) minmum = self.raster_layer.renderer().contrastEnhancement( ).setMinimumValue(100) maximum = self.raster_layer.renderer().contrastEnhancement( ).maximumValue() minmum = self.raster_layer.renderer().contrastEnhancement( ).minimumValue() self.assertEqual(minmum, 100) self.assertEqual(maximum, 172) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') elements = root.elementsByTagName('sld:ContrastEnhancement') self.assertEqual(len(elements), 1) enhancement = elements.at(0).toElement() self.assertFalse(enhancement.isNull()) normalize = enhancement.firstChildElement('sld:Normalize') self.assertFalse(normalize.isNull()) self.assertVendorOption(normalize, 'minValue', '51') self.assertVendorOption(normalize, 'maxValue', '172') elements = root.elementsByTagName('sld:ColorMap') self.assertEqual(len(elements), 1) colorMap = elements.at(0).toElement() self.assertFalse(colorMap.isNull()) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(len(colorMapEntries), 4) clorMap1 = colorMapEntries.at(0) self.assertEqual(clorMap1.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual( clorMap1.attributes().namedItem('quantity').nodeValue(), '100') self.assertEqual( clorMap1.attributes().namedItem('opacity').nodeValue(), '0') clorMap2 = colorMapEntries.at(1) self.assertEqual(clorMap2.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual( clorMap2.attributes().namedItem('quantity').nodeValue(), '100') clorMap3 = colorMapEntries.at(2) self.assertEqual(clorMap3.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual( clorMap3.attributes().namedItem('quantity').nodeValue(), '172') clorMap4 = colorMapEntries.at(3) self.assertEqual(clorMap4.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual( clorMap4.attributes().namedItem('quantity').nodeValue(), '172') self.assertEqual( clorMap4.attributes().namedItem('opacity').nodeValue(), '0') def testRasterRenderer(self): class fakerenderer(QgsRasterRenderer): def __init__(self, interface): QgsRasterRenderer.__init__(self, interface, '') rasterRenderer = fakerenderer(self.raster_layer.dataProvider()) self.raster_layer.setRenderer(rasterRenderer) # check opacity default value is not exported dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) # check if opacity is not the default value rasterRenderer.setOpacity(1.1) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertOpacity(root, '1.1') # check gamma properties from [-100:0] streched to [0:1] # and (0:100] stretche dto (1:100] # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-100'}) # self.assertGamma(root, '0') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-50'}) # self.assertGamma(root, '0.5') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '0'}) # self.assertGamma(root, '1') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '1'}) # self.assertGamma(root, '1') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '100'}) # self.assertGamma(root, '100') # # input contrast are always integer, btw the value is managed also if it's double # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '1.1'}) # self.assertGamma(root, '1.1') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '1.6'}) # self.assertGamma(root, '1.6') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-50.5'}) # self.assertGamma(root, '0.495') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-0.1'}) # self.assertGamma(root, '0.999') def testStretchingAlgorithm(self): rasterRenderer = QgsMultiBandColorRenderer( self.raster_layer.dataProvider(), 3, 1, 2) self.raster_layer.setRenderer(rasterRenderer) # check StretchToMinimumMaximum stretching alg self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.StretchToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel', 'StretchToMinimumMaximum', '51', '172') self.assertContrastEnhancement(root, 'sld:GreenChannel', 'StretchToMinimumMaximum', '122', '130') self.assertContrastEnhancement(root, 'sld:BlueChannel', 'StretchToMinimumMaximum', '133', '148') # check StretchAndClipToMinimumMaximum stretching alg self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.StretchAndClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel', 'ClipToZero', '51', '172') self.assertContrastEnhancement(root, 'sld:GreenChannel', 'ClipToZero', '122', '130') self.assertContrastEnhancement(root, 'sld:BlueChannel', 'ClipToZero', '133', '148') # check ClipToMinimumMaximum stretching alg self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.ClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel', 'ClipToMinimumMaximum', '51', '172') self.assertContrastEnhancement(root, 'sld:GreenChannel', 'ClipToMinimumMaximum', '122', '130') self.assertContrastEnhancement(root, 'sld:BlueChannel', 'ClipToMinimumMaximum', '133', '148') # check NoEnhancement stretching alg self.raster_layer.setContrastEnhancement( algorithm=QgsContrastEnhancement.NoEnhancement) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel') self.assertContrastEnhancement(root, 'sld:GreenChannel') self.assertContrastEnhancement(root, 'sld:BlueChannel') def assertVendorOption(self, root, name, expectedValue): """Set expectedValue=None to check that the vendor option is not present.""" vendorOptions = root.elementsByTagName('sld:VendorOption') found = False for vendorOptionIndex in range(vendorOptions.count()): vendorOption = vendorOptions.at(vendorOptionIndex) self.assertEqual('sld:VendorOption', vendorOption.nodeName()) if (vendorOption.attributes().namedItem('name').nodeValue() == name ): found = True self.assertEqual(vendorOption.firstChild().nodeValue(), expectedValue) if (expectedValue is None) and found: self.fail( "found VendorOption: {} where supposed not present".format( name)) if expectedValue and not found: self.fail("Not found VendorOption: {}".format(name)) def assertGamma(self, root, expectedValue, index=0): enhancement = root.elementsByTagName('sld:ContrastEnhancement').item( index) gamma = enhancement.firstChildElement('sld:GammaValue') self.assertEqual(expectedValue, gamma.firstChild().nodeValue()) def assertOpacity(self, root, expectedValue, index=0): opacity = root.elementsByTagName('sld:Opacity').item(index) self.assertEqual(expectedValue, opacity.firstChild().nodeValue()) def assertNoOpacity(self, root): opacities = root.elementsByTagName('sld:Opacity') self.assertEqual(opacities.size(), 0) def assertContrastEnhancement(self, root, bandTag, expectedAlg=None, expectedMin=None, expectedMax=None, index=0): channelSelection = root.elementsByTagName('sld:ChannelSelection').item( index) self.assertIsNotNone(channelSelection) band = channelSelection.firstChildElement(bandTag) # check if no enhancement alg is iset if (not expectedAlg): contrastEnhancementName = band.firstChildElement( 'sld:ContrastEnhancement') self.assertEqual('', contrastEnhancementName.firstChild().nodeName()) return # check if enhancement alg is set contrastEnhancementName = band.firstChildElement( 'sld:ContrastEnhancement') self.assertEqual('sld:Normalize', contrastEnhancementName.firstChild().nodeName()) normalize = contrastEnhancementName.firstChildElement('sld:Normalize') vendorOptions = normalize.elementsByTagName('VendorOption') for vendorOptionIndex in range(vendorOptions.count()): vendorOption = vendorOptions.at(vendorOptionIndex) self.assertEqual('VendorOption', vendorOption.nodeName()) if (vendorOption.attributes().namedItem('name').nodeValue() == 'algorithm'): self.assertEqual(expectedAlg, vendorOption.firstChild().nodeValue()) elif (vendorOption.attributes().namedItem('name').nodeValue() == 'minValue'): self.assertEqual(expectedMin, vendorOption.firstChild().nodeValue()) elif (vendorOption.attributes().namedItem('name').nodeValue() == 'maxValue'): self.assertEqual(expectedMax, vendorOption.firstChild().nodeValue()) else: self.fail('Unrecognised vendorOption name {}'.format( vendorOption.attributes().namedItem('name').nodeValue())) def assertChannelBand(self, root, bandTag, expectedValue, index=0): channelSelection = root.elementsByTagName('sld:ChannelSelection').item( index) self.assertIsNotNone(channelSelection) band = channelSelection.firstChildElement(bandTag) sourceChannelName = band.firstChildElement('sld:SourceChannelName') self.assertEqual(expectedValue, sourceChannelName.firstChild().nodeValue()) def rendererToSld(self, renderer, properties={}): dom = QDomDocument() root = dom.createElement("FakeRoot") dom.appendChild(root) renderer.toSld(dom, root, properties) return dom, root
class TestRATClasses(TestCase): @classmethod def setUpClass(cls): cls.qgs = QgsApplication([], False) cls.qgs.initQgis() @classmethod def tearDownClass(cls): cls.qgs.exitQgis() def tearDown(self): del (self.raster_layer) del (self.raster_layer_dbf) del (self.raster_layer_athematic) def setUp(self): self.tmp_dir = QTemporaryDir() self.tmp_path = os.path.join(self.tmp_dir.path(), 'data') shutil.copytree(os.path.join(os.path.dirname(__file__), 'data'), self.tmp_path) self.raster_layer = QgsRasterLayer( os.path.join( self.tmp_path, 'NBS_US5PSMBE_20200923_0_generalized_p.source_information.tiff' ), 'rat_test', 'gdal') self.assertTrue(self.raster_layer.isValid()) self.raster_layer_dbf = QgsRasterLayer( os.path.join(self.tmp_path, 'ExistingVegetationTypes_sample.img'), 'rat_test', 'gdal') self.assertTrue(self.raster_layer_dbf.isValid()) self.raster_layer_athematic = QgsRasterLayer( os.path.join(self.tmp_path, '2x2_1_BAND_FLOAT.tif'), 'rat_test', 'gdal') self.assertTrue(self.raster_layer_athematic.isValid()) def test_field_usages(self): rat = get_rat(self.raster_layer, 1) self.assertTrue(rat.isValid()) self.assertEqual(rat.field_usages, {0, 1, 5}) 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 test_remove_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)) # Invalid removals self.assertFalse(rat.remove_column('Value')[0]) self.assertFalse(rat.remove_column('Count')[0]) self.assertFalse(rat.remove_column('not found')[0]) # Valid removals self.assertTrue(rat.remove_column('data_assessment')[0]) self.assertEqual(len(rat.keys), 15) self.assertEqual(len(rat.keys), len(rat.fields)) 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_remove_column_dbf(self): rat = get_rat(self.raster_layer_dbf, 1) self.assertTrue(rat.isValid()) self.assertEqual(len(rat.keys), 17) self.assertEqual(len(rat.keys), len(rat.fields) + 1) # Invalid removals self.assertFalse(rat.remove_column('VALUE')[0]) self.assertFalse(rat.remove_column('COUNT')[0]) self.assertFalse(rat.remove_column('not found')[0]) # Valid removals self.assertTrue(rat.remove_column('SYSTMGRPNA')[0]) self.assertEqual(len(rat.keys), 16) self.assertEqual(len(rat.keys), len(rat.fields) + 1) def test_qgis_features(self): rat = get_rat(self.raster_layer_dbf, 1) features = rat.qgis_features() self.assertEqual(len(features), 59) rat = get_rat(self.raster_layer, 1) features = rat.qgis_features() self.assertEqual(len(features), 27) def test_get_set_color(self): rat = get_rat(self.raster_layer_dbf, 1) color = rat.get_color(0) self.assertTrue(color.isValid()) # Invalid self.assertFalse(rat.get_color(-1).isValid()) self.assertFalse(rat.get_color(100).isValid()) # Setter self.assertTrue(rat.set_color(1, QColor(10, 20, 30, 120))) self.assertEqual(rat.get_color(1), QColor(10, 20, 30, 120)) def test_field_name(self): rat = get_rat(self.raster_layer_dbf, 1) usages = [] for field in rat.fields.values(): if field.usage not in usages: usages.append(field.usage) self.assertEqual(rat.field_name(field.usage), field.name) self.assertEqual(rat.field_name(gdal.GFU_AlphaMax), '') self.assertEqual(rat.field_name(gdal.GFU_RedMax), '') self.assertEqual(rat.field_name(gdal.GFU_RedMin), '') def test_update_color_from_raster(self): rat = get_rat(self.raster_layer_dbf, 1) self.assertTrue(rat.has_color) rat_classify(self.raster_layer_dbf, 1, rat, 'EVT_NAME') color_map = { klass.value: klass.color for klass in self.raster_layer_dbf.renderer().classes() } # Remove color self.assertTrue(rat.remove_color_fields()) self.assertFalse(rat.has_color) # Add color result, error_message = rat.insert_color_fields(len(rat.keys) - 1) self.assertTrue(result, error_message) self.assertTrue(rat.has_color) for color in rat.data[RAT_COLOR_HEADER_NAME]: self.assertEqual(color, QColor(Qt.black)) # Update color from raster self.assertTrue(rat.update_colors_from_raster(self.raster_layer_dbf)) value_column = rat.value_columns[0] self.assertEqual(value_column, rat.field_name(gdal.GFU_MinMax)) for row_index in range(len(rat.data[RAT_COLOR_HEADER_NAME])): self.assertEqual(rat.data[RAT_COLOR_HEADER_NAME][row_index], color_map[rat.data[value_column][row_index]]) def test_update_color_from_raster_athematic(self): rat = get_rat(self.raster_layer_athematic, 1) self.assertTrue(rat.has_color) rat_classify(self.raster_layer_athematic, 1, rat, 'Class') shader = self.raster_layer_athematic.renderer().shader() colorRampShaderFcn = shader.rasterShaderFunction() classes = classes = colorRampShaderFcn.colorRampItemList() color_map = {klass.value: klass.color for klass in classes} # Remove color self.assertTrue(rat.remove_color_fields()) self.assertFalse(rat.has_color) # Add color result, error_message = rat.insert_color_fields(len(rat.keys) - 1) self.assertTrue(result, error_message) self.assertTrue(rat.has_color) for color in rat.data[RAT_COLOR_HEADER_NAME]: self.assertEqual(color, QColor(Qt.black)) # Update color from raster self.assertTrue( rat.update_colors_from_raster(self.raster_layer_athematic)) value_column = rat.value_columns[1] self.assertEqual(value_column, rat.field_name(gdal.GFU_Max)) for row_index in range(len(rat.data[RAT_COLOR_HEADER_NAME])): self.assertEqual(rat.data[RAT_COLOR_HEADER_NAME][row_index], color_map[rat.data[value_column][row_index]]) def test_add_remove_row(self): def _test(raster_layer): rat = get_rat(raster_layer, 1) value_column = rat.value_columns[0] self.assertEqual(value_column, rat.field_name(gdal.GFU_MinMax)) self.assertNotEqual(rat.data[value_column][-1], 0) row_count = len(rat.data[value_column]) result, error_message = rat.insert_row(0) self.assertTrue(result) self.assertEqual(len(rat.data[value_column]), row_count + 1) self.assertEqual(rat.data[value_column][0], 0) result, error_message = rat.remove_row(0) self.assertTrue(result) self.assertEqual(len(rat.data[value_column]), row_count) self.assertNotEqual(rat.data[value_column][0], 0) last = len(rat.data[value_column]) result, error_message = rat.insert_row(last) self.assertTrue(result) self.assertEqual(len(rat.data[value_column]), row_count + 1) self.assertEqual(rat.data[value_column][last], 0) result, error_message = rat.remove_row(last) self.assertTrue(result) self.assertEqual(len(rat.data[value_column]), row_count) self.assertNotEqual(rat.data[value_column][last - 1], 0) # Invalid ranges last = len(rat.data[value_column]) self.assertFalse(rat.insert_row(-1)[0]) self.assertFalse(rat.insert_row(last + 1)[0]) self.assertFalse(rat.remove_row(-1)[0]) self.assertFalse(rat.remove_row(last)[0]) _test(self.raster_layer_dbf) _test(self.raster_layer) def test_edit_rat(self): raster_layer = QgsRasterLayer( os.path.join(self.tmp_path, '2x2_2_BANDS_INT16.tif'), 'rat_test', 'gdal') self.assertTrue(raster_layer.isValid()) band = 1 rat = get_rat(raster_layer, band) self.assertTrue(rat.isValid()) self.assertEqual(rat.data['Red'], [0, 100, 200]) rat.data['Red'] = [111, 222, 123] rat.save(band) rat = get_rat(raster_layer, band) self.assertTrue(rat.isValid()) self.assertEqual(rat.data['Red'], [111, 222, 123]) def test_athematic_dbf_roundtrip(self): """Test that saving as athematic and reloading does not loose type""" rat = get_rat(self.raster_layer_athematic, 1) self.assertTrue(rat.has_color) self.assertTrue(rat.isValid()) self.assertEqual(rat.thematic_type, gdal.GRTT_ATHEMATIC) # Delete the layer and the PAM file raster_source = self.raster_layer_athematic.source() pam_path = raster_source + '.aux.xml' del (self.raster_layer_athematic) os.unlink(pam_path) rat.save_as_dbf(raster_source) self.assertTrue(os.path.exists(raster_source + '.vat.dbf')) self.raster_layer_athematic = QgsRasterLayer(raster_source, 'rat_test', 'gdal') rat_dbf = get_rat(self.raster_layer_athematic, 1) self.assertTrue(rat_dbf.has_color) self.assertTrue(rat_dbf.isValid()) self.assertEqual(rat_dbf.thematic_type, gdal.GRTT_ATHEMATIC) self.assertEqual( rat_dbf.field_usages, { gdal.GFU_Generic, gdal.GFU_Red, gdal.GFU_Green, gdal.GFU_Blue, gdal.GFU_Min, gdal.GFU_Max, }) def test_charset(self): """Test that we can save/load non-ASCII chars""" rat = get_rat(self.raster_layer, 1) self.assertTrue(rat.isValid()) # Delete the layer and the PAM file raster_source = self.raster_layer.source() pam_path = raster_source + '.aux.xml' dbf_path = raster_source + '.vat.dbf' del (self.raster_layer) os.unlink(pam_path) rat.data['License_Name'][0] = 'Some accented chars èé 😁' rat.save_as_dbf(raster_source) self.assertTrue(os.path.exists(dbf_path)) self.raster_layer = QgsRasterLayer(raster_source, 'rat_test', 'gdal') rat_dbf = get_rat(self.raster_layer, 1) self.assertTrue(rat_dbf.isValid()) self.assertEqual(rat_dbf.data['License_Na'][0], 'Some accented chars èé 😁') # Save as XML rat.save_as_xml(raster_source, 1) self.assertTrue(os.path.exists(pam_path)) del (self.raster_layer) os.unlink(dbf_path) self.raster_layer = QgsRasterLayer(raster_source, 'rat_test', 'gdal') rat_xml = get_rat(self.raster_layer, 1) self.assertTrue(rat_xml.isValid()) self.assertEqual(rat_xml.data['License_Name'][0], 'Some accented chars èé 😁')
class ExploreMapWindow(QMainWindow): """This class offers a canvas and tools to preview and explore data provided by Geocubes. Preview raster layers are fetched from the Geocubes cached WMTS server. The user can simply view the data or get legend info on a single point.""" # the window is initiated with the Geocubes url base defined on the main plugin # this means that the base doesn't have to be manually changed here if it changes def __init__(self, url_base): QMainWindow.__init__(self) # creating map canvas, which draws the maplayers # setting up features like canvas color self.canvas = QgsMapCanvas() self.canvas.setMinimumSize(550, 700) self.canvas.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.canvas.setCanvasColor(Qt.white) #self.canvas.enableAntiAliasing(True) self.url_base = url_base # Qmainwindow requires a central widget. Canvas is placed self.setCentralWidget(self.canvas) """'tile widths' refer to the Map proxy WMTS server's settings for displaying data of different resolutions. If I understood correctly, these values (got by examining properties of a layer from that server in QGIS) are the thresholds at which a different resolution is loaded on the GRIDI-FIN tileset. The values represent the tile size in map units (meters). Each tile widths is tied to the corresponding resolution, which is used to get the correct resolution legend info. The method is only an estimation, but ought to produce good enough results for this purpose. Smallest resolutions (1, 2, 5) are omitted since only some layers have them. """ self.tile_widths = { 2560: 10, 5120: 20, 12800: 50, 25600: 100, 51200: 200, 128000: 500, 256000: 1000 } # get all keys i.e. widths self.all_widths = [i for i in self.tile_widths] # creating background layer box and housing it with the hardcoded options self.bg_layer_box = QComboBox() # if ortokuva ever updates to newer versions, just change the year here bg_layers = ['Taustakartta', 'Ortokuva_2018', 'No reference layer'] # set 'No reference layer' as the default option self.bg_layer_box.addItems(layer for layer in bg_layers) self.bg_layer_box.setCurrentIndex(2) self.bg_layer_box.currentIndexChanged.connect(self.addBackgroundLayer) # initialize the slider that will control BG layer opacity/transparency self.opacity_slider = QSlider(Qt.Horizontal) self.opacity_slider.setMinimum(0) self.opacity_slider.setMaximum(100) self.opacity_slider.setSingleStep(1) self.opacity_slider.setMaximumWidth(100) self.opacity_slider.valueChanged.connect(self.setBackgroundMapOpacity) self.legend_checkbox = QCheckBox("Get attribute info on all layers") # explanatory texts for the different widgets are stored as label widgets bg_layer_label = QLabel(" Background: ") bg_opacity_label = QLabel(" BG opacity: ") data_label = QLabel("Data: ") spacing = QLabel(" ") # all of the data layers are housed in this combobox self.layer_box = QComboBox() self.layer_box.currentIndexChanged.connect(self.addLayer) # creating each desired action self.actionPan = QAction("Pan tool", self) self.actionLegend = QAction("Attribute info tool", self) self.actionCancel = QAction("Close window", self) self.actionZoom = QAction("Zoom to full extent", self) # these two work as on/off. the rest are clickable self.actionPan.setCheckable(True) self.actionLegend.setCheckable(True) # when actions are clicked, do corresponding function self.actionPan.triggered.connect(self.pan) self.actionLegend.triggered.connect(self.info) self.actionCancel.triggered.connect(self.cancel) self.actionZoom.triggered.connect(self.zoomToExtent) # defining two toolbars: first one houses layer and opacity selection # the other has all the tools and functions self.layers_toolbar = self.addToolBar("Select layers") self.layers_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) self.layers_toolbar.setMovable(False) self.addToolBarBreak() self.tools_toolbar = self.addToolBar("Tools") self.tools_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) self.tools_toolbar.setMovable(False) # change order here to change their placement on window # starting with the layer widgets and the corresponding label texts self.layers_toolbar.addWidget(data_label) self.layers_toolbar.addWidget(self.layer_box) self.layers_toolbar.addWidget(bg_layer_label) self.layers_toolbar.addWidget(self.bg_layer_box) self.layers_toolbar.addWidget(bg_opacity_label) self.layers_toolbar.addWidget(self.opacity_slider) self.layers_toolbar.addWidget(spacing) self.layers_toolbar.addWidget(self.legend_checkbox) # then setting all the canvas tools on the second toolbar self.tools_toolbar.addAction(self.actionLegend) self.tools_toolbar.addAction(self.actionPan) self.tools_toolbar.addAction(self.actionZoom) self.tools_toolbar.addAction(self.actionCancel) # a large text box that will house the legend info self.text_browser = QTextEdit("Legend will be shown here") self.text_browser.setReadOnly(True) # a dock widget is required for the text browser. Docked to main window dock_widget = QDockWidget() dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures) dock_widget.setWindowTitle("Legend") dock_widget.setWidget(self.text_browser) self.addDockWidget(Qt.RightDockWidgetArea, dock_widget) # link actions to premade map tools self.toolPan = QgsMapToolPan(self.canvas) self.toolPan.setAction(self.actionPan) self.toolClick = QgsMapToolEmitPoint(self.canvas) self.toolClick.canvasClicked.connect(self.getLegendInfo) # this is to ensure that the map isn't zoomed out everytime the layer changes self.first_start = True # this boolean is true while there is no active background layer # needed to ensure that e.g. opacity isn't attempted to be set on a nonexisting layer self.no_bg_layer_flag = True # set pantool as default self.pan() def pan(self): """Simply activates the tool and deactivates the other tool if active""" self.canvas.setMapTool(self.toolPan) # make sure the other button isn't checked to avoid confusion self.actionLegend.setChecked(False) def info(self): self.canvas.setMapTool(self.toolClick) self.actionLegend.setChecked(True) self.actionPan.setChecked(False) def zoomToExtent(self): """zooms out/in so that the raster layer is centered""" self.canvas.setExtent(self.layer.extent()) self.canvas.refresh() def setBackgroundMapOpacity(self): if self.no_bg_layer_flag: return else: self.bg_layer.renderer().setOpacity(self.getBackgroundMapOpacity()) self.canvas.refresh() def getBackgroundMapOpacity(self): """Returns the current BG layer opacity as a double [0, 1]. Slider only accepts integers, therefore the initial value is divided by hundred.""" return (self.opacity_slider.value() / 100) def showCanvas(self, all_datasets): """Called to activate the the window. Input is all of the datasets on the Geocubes server as a dictionary (see main plugin py-file). First a default layer (background map, which is on the WMTS server but not on Geocubes files) is inserted to the combobox. Then the keys of the dictionary (which are in format layer_name;year) are inserted.""" # empty box on restart self.layer_box.clear() self.all_datasets = all_datasets self.no_bg_layer_flag = True for key in self.all_datasets: self.layer_box.addItem(key) # zoom to the full extent of the current map self.zoomToExtent() # default values set self.text_browser.setText("Legend will be shown here") self.bg_layer_box.setCurrentIndex(2) self.opacity_slider.setValue(50) self.show() def getLegendInfo(self, point): """Activated when the canvas is clicked. The click returns a point, which is parsed to a string of X and Y coordinates separated by a comma. An url to get legend info on this point is formed and used. If the request is succesful, the response string is decoded and passed to be inserted to the text browser.""" formatted_point = str(int(point.x())) + "," + str(int(point.y())) url = self.formLegendUrl(formatted_point) if not url: return response = requests.get(url, timeout=6) # 200 = succesful request # the field won't be updated in case of a failed request if response.status_code == 200: legend_string = response.content.decode("utf-8") self.setTextToBrowser(legend_string) def setTextToBrowser(self, string): """Formats and inserts legend text to the browser. Input is string of raw text data. This is split at semicolons if there are multiple features.""" # empty on multiple clicks self.text_browser.clear() strings = string.split(';') # no need for a loop if there's only one line if len(strings) == 1: self.text_browser.setText(string) else: for text_string in strings: # appending allows to insert multi-line texts self.text_browser.append(text_string) def formLegendUrl(self, formatted_point): """Forms an url for querying legend data on a specific coordinate point. Data is queried either from the currently selected layer or, if selected by the user, from all available layers.""" key = self.layer_box.currentText() resolution = self.getResolutionFromExtent() if not key: return if not resolution: resolution = 100 if self.legend_checkbox.isChecked(): layer_name = "all" year = "2015" else: value = self.all_datasets[key] layer_name, year = value[0], value[3] url = (self.url_base + "/legend/" + str(resolution) + "/" + layer_name + "/" + formatted_point + "/" + year) return url def getResolutionFromExtent(self): """Estimates the resolution of the imagery currently viewed by user based on the width of the canvas. Returns said resolution. Used by the legend tool to get info of the correct dataset.""" # extent as a QgsRectangle canvas_extent = self.canvas.extent() # width (in meters, since CRS is EPSG:3067) of the current canvas view width = canvas_extent.xMaximum() - canvas_extent.xMinimum() # find the width threshold closest to the current one try: closest_width = min(self.all_widths, key=lambda x: abs(x - width)) except Exception: return # use the width key to get the corrensponding resolution closest_resolution = self.tile_widths[closest_width] return closest_resolution def addLayer(self): """Adds a new layer on the map canvas based on the selection on the combobox. Everything else is hardcoded, but the layer name of course changes. Layers are identified by name and year (i.e. km2_2018). These type of strings are formed first, then the whole url""" # often a layer already exists. If so, remove try: QgsProject.instance().removeMapLayer(self.layer) except Exception: pass key = self.layer_box.currentText() if not key: return # background map doesn't have a specific year attached to it if key == "Taustakartta": layer_name = key else: # the desired parameters are housed in the dictionary. Luckily the # combobox houses the keys to it. gets a tuple with four values value = self.all_datasets[key] # name is first value, year last. separated with an underscore layer_name = value[0] + "_" + value[3] url = ("https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" + "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/" + "png&layers=" + layer_name + "&styles=default&tileMatrixSet=GRIDI-FIN") self.layer = QgsRasterLayer("url=" + url, 'GEOCUBES DATALAYER - TEMPORARY', 'wms') if self.layer.isValid(): QgsProject.instance().addMapLayer(self.layer, False) # if layer is valid and added to the instance, insert it to the canvas self.setMapLayers() # zoom to the full extent of the map if canvas is started for the first time if self.first_start: self.zoomToExtent() self.first_start = False def addBackgroundLayer(self): """Adds a background layer to help user locating what they want. This layer will be either background map (taustakarta), ortographic imagery or nothing at all. BG layer has an opacity value that set by the user. Function is called when user selects a layer on the combobox.""" layer_name = self.bg_layer_box.currentText() # remove the old background layer, if one exists try: QgsProject.instance().removeMapLayer(self.bg_layer) except Exception: pass # if user wants no background layer, return without setting a new layer if not layer_name or layer_name == 'No reference layer': self.no_bg_layer_flag = True self.canvas.refresh() return else: self.bg_layer = QgsRasterLayer( "url=https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" + "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/" + "png&layers=" + layer_name.lower() + "&styles=default&tileMatrixSet=GRIDI-FIN", 'GEOCUBES BG-LAYER - TEMPORARY', 'wms') if self.bg_layer.isValid(): self.no_bg_layer_flag = False QgsProject.instance().addMapLayer(self.bg_layer, False) self.bg_layer.renderer().setOpacity( self.getBackgroundMapOpacity()) self.setMapLayers() def setMapLayers(self): """Called anytime a new layer is added to the project instance. Setting layers to canvas decides what's shown to the user and in which order. If there's a background layer, it must be set before the data layer.""" if self.no_bg_layer_flag: self.canvas.setLayers([self.layer]) else: self.canvas.setLayers([self.bg_layer, self.layer]) self.canvas.refresh() def cancel(self): self.close() def closeEvent(self, event): """Activated anytime Mapwindow is closed either by buttons given or if the user finds some other way to close the window. Deletes scrap maplayers.""" try: QgsProject.instance().removeMapLayer(self.layer) QgsProject.instance().removeMapLayer(self.bg_layer) except Exception: pass QMainWindow.closeEvent(self, event)
def readRasterLayer(i, allBandData): # pylint: disable=too-many-locals fileInfo = QFileInfo(shared.rasterFileName[i]) fileBaseName = fileInfo.baseName() layer = QgsRasterLayer(shared.rasterFileName[i], fileBaseName) if not layer.isValid(): shared.fpOut.write("Raster layer '" + shared.rasterFileName[i] + "'failed to load") return -1 # Store the title as the first list item allBandData.append(shared.rasterFileTitle[i]) # Get more info xSize = layer.width() ySize = layer.height() cellWidth = layer.rasterUnitsPerPixelX() cellHeight = layer.rasterUnitsPerPixelY() provider = layer.dataProvider() extnt = provider.extent() dpi = provider.dpi() shared.fpOut.write("Raster layer '" + shared.rasterFileTitle[i] + "' loaded with X, Y resolution = " + str(cellWidth) + ", " + str(cellHeight) + " m\n") #shared.fpOut.write(layer.metadata()) #shared.fpOut.write(layer.rasterType()) # Store the above as the second list item allBandData.append( [provider, xSize, ySize, cellWidth, cellHeight, extnt, dpi]) # Now store the data for each band as a QgsRasterBlock nBands = layer.bandCount() for band in range(nBands): #shared.fpOut.write(layer.bandName(i)) bandData = provider.block(band, extnt, xSize, ySize) # Store as a further list item allBandData.append(bandData) # Sort out style #shared.fpOut.write("Style file " + str(i) + " is " + shared.rasterFileStyle[i]) if not shared.rasterFileStyle[i]: # No style file specified, so try the default style for this layer shared.rasterFileStyle[i] = layer.styleURI() #shared.fpOut.write("Trying default style file " + shared.rasterFileStyle[i]) if not layer.loadDefaultStyle(): shared.fpOut.write("Could not load default style '" + shared.rasterFileStyle[i] + "' for raster layer '" + shared.rasterFileTitle[i] + "'") else: # A style file was specified, so try to load it #shared.fpOut.write("Trying style file " + shared.rasterFileStyle[i]) if not layer.loadNamedStyle(shared.rasterFileStyle[i]): shared.fpOut.write("Could not load style '" + shared.rasterFileStyle[i] + "' for raster layer '" + shared.rasterFileTitle[i] + "'") # Set opacity layer.renderer().setOpacity(shared.rasterFileOpacity[i]) # Add this layer to the app's registry QgsProject.instance().addMapLayer(layer) return layer
def select_HDF5_file(self): """Run method that performs all the real work""" # show the dialog #self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # close the dialog self.dlg.close() # See if OK was pressed if result: #### to open windows browser and search for HDF5 files only: myfileNames = QFileDialog.getOpenFileNames(self.dlg, self.tr("HDF5 File Selector"), "", self.tr("HDF5 (*.hdf5 *.h5)")) #### to loop through all selected HDF5 files, just handling one for now for i in myfileNames: #### bind the HDF5 file myfile = QgsRasterLayer(i) #### to deal with multiple datasets within file if (len(myfile.subLayers()) > 1): #### to open dataset with desired data and name it Reflectance #mydset = QgsRasterLayer(myfile.subLayers()[0], 'Reflectance') fileName = myfile.subLayers()[0] fileInfo = QFileInfo(fileName) baseName = fileInfo.baseName() mydset = QgsRasterLayer(fileName, baseName) ##################################### Trials of setting proper extent begin here print 'Extent before: ' print mydset.extent().toString() ########################## test almost worked ### loaded layer in right place but without data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #mydset.setExtent(rect) #################################### ######################## test almost worked ### loaded layer in right place but without data #size = QSizeF(1,1) #point = QPointF(326380.0, 4103390.0) #rect = QRectF(point, size) #rect = QgsRectangle(rect) #mydset.setExtent(rect) ################################### ########################## did not change extent #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #context = QgsRenderContext() #context.setExtent(rect) #mydset.draw(context) #################################### #################################### did not change extent #size = QSizeF(1,1) #point = QPointF(326380.0, 4103390.0) #rect = QRectF(point, size) #rect = QgsRectangle(rect) #context = QgsRenderContext() #context.setExtent(rect) #mydset.draw(context) ##################################### ###################################### did not change extent #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #context = QgsRenderContext() #context.setExtent(rect) #print 'Context extent: ' #print context.extent().toString() #this printed correct extent #mydset.createMapRenderer(context) ####################################### ######################## test almost worked ### loaded layer in right place but without data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #provider = mydset.dataProvider().clone() #mydset.setExtent(rect) #provider.reloadData() ###################################### ######################################## # did not change extent, loaded data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #context = QgsRenderContext() #context.setExtent(rect) #renderer = mydset.createMapRenderer(context) #renderer.render() ######################################### ########################################### # did not change extent, loaded data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #mydset.dataProvider().block(52, rect, 1332, 2853) ########################################## ############################################# # did not change extent, loaded data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #mydset.dataProvider().block(426, rect, 1332, 2853) ############################################### ################################################# # changed extent but overwrote to no data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #mydset.setExtent(rect) #mydset.dataProvider().reload() #################################################### #################################################### # changed extent but overwrote to no data #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #mydset.setExtent(rect) #mydset.dataProvider().block(52, rect, 1332, 2853) #mydset.dataProvider().reload() ##################################################### ############################################# # not setting new cloned data provider correctly # method wants QString object but gets meaningless string #rect = QgsRectangle(326380.0,4103390.0-2853,326380.0+1332,4103390.0) #provider = mydset.dataProvider() #print 'Provider: ' #print provider #clone = provider.clone() #print 'Provider clone: ' #print clone #clone.block(1, rect, 1332, 2853) #mydset.setDataProvider(str(clone)) #print 'New provider: ' #print mydset.dataProvider() ############################################## ######## printing result of extent changes print 'Extent after: ' print mydset.extent().toString() ################################### ####################################### Trials of setting proper extent end here #### to set proper Coordinate Reference System (crs) info crs = mydset.crs() crs.createFromId(32611) mydset.setCrs(crs) ### this was josh's recommendation #mycrs = QgsCoordinateReferenceSystem(32611) #self.iface.mapCanvas().mapRenderer().setDestinationCrs(mycrs) #### to set raster bands to load as RGB mydset.renderer().setGreenBand(34) mydset.renderer().setRedBand(52) mydset.renderer().setBlueBand(18) ####### Tristan changes #myrenderer = mydset.renderer() #print 'Renderer Type: ' #print mydset.renderer().type() # print 'Renderer block before: ' #print myrenderer.block(426, rect, 1, 1) #myrenderer.block(426, rect, 1, 1) #print 'Renderer block after: ' #print myrenderer.block() not enough arguments #if hasattr(mydset, "setCacheImage"): #mydset.setCacheImage(None) #mydset.triggerRepaint() #mydset.dataProvider().reloadData() #mydset.triggerRepaint() #self.iface.legendInterface().refreshLayerSymbology(mydset) #mydset.reload() #mydset.reloadData() #mydset.triggerRepaint() #mydset.draw() #print 'Extent: ' #print mydset.extent().toString() # ident = rlayer.dataProvider().identify(QgsPoint(15.30, 40.98), \ # QgsRaster.IdentifyFormatValue) # if ident.isValid(): # print ident.results() ####### End Tristan changes #### to add selected dataset/raster to map canvas QgsMapLayerRegistry.instance().addMapLayer(mydset) #canvas=QgsMapCanvas() #canvas.show() #canvas.setExtent(mydset.extent()) #canvas.setLayerSet([QgsMapCanvasLayer(mydset)]) ####################################### Script for getting all raster values # takes 11 minutes to run on (426,1332,2583) #list = [] #for x in range(mydset.width()): #x_coord = x #print 'Doing ' +str(x) + 'x right now...' #for y in range(mydset.height()): #y_coord = y #ident = mydset.dataProvider().identify(QgsPoint(x_coord, y_coord), \ #QgsRaster.IdentifyFormatValue) #list.append(ident.results().values()) #print 'Length of list is: ' #print len(list) #print 'Length of list[0] is: ' #print len(list[0]) ############################################ #if ident.isValid(): #print ident.results()
class ProjectModule(Module): def __init__(self, plugin): super(ProjectModule, self).__init__(plugin) # Project settings # self.projectLayerView = None # QgsLayerTreeView() self.metadata = None # Metadata() self.projectGroupIndex = -1 self.drawingsGroupIndex = -1 self.drawingsGroupName = '' self.geoLayer = None # QgsRasterLayer() self.plan = None # Collection() self.section = None # Collection() self.grid = None # Collection() self.site = None # Collection() self._collections = {} # {Collection()} # Tools self.identifyMapTool = None # MapToolIndentifyItems() # Internal variables self._mapAction = MapAction.MoveMap self._filterAction = FilterAction.ExclusiveHighlightFilter self._drawingAction = DrawingAction.NoDrawingAction self._layerSnappingAction = None # LayerSnappingAction() self._itemLogPath = '' # Create the gui when the plugin is first created def initGui(self): dock = ProjectDock(self._plugin.iface.mainWindow()) self._initDockGui(dock, Qt.LeftDockWidgetArea, self._plugin.pluginAction) # self.projectLayerView = QgsLayerTreeView() # self._layerSnappingAction = LayerSnappingAction(self._plugin.iface, self.projectLayerView) # self._plugin.iface.legendInterface().addLegendLayerAction( # self._layerSnappingAction, '', 'arksnap', QgsMapLayer.VectorLayer, True) """ # Create the Layer Model and View # TODO Should only show our subgroup but crashes! # self.projectLayerModel # = QgsLayerTreeModel(QgsProject.instance().layerTreeRoot().findGroup(Config.projectGroupName), self); self.projectLayerModel = QgsLayerTreeModel(QgsProject.instance().layerTreeRoot(), self) self.projectLayerModel.setFlag(QgsLayerTreeModel.ShowLegend) self.projectLayerModel.setFlag(QgsLayerTreeModel.ShowLegendAsTree) self.projectLayerModel.setFlag(QgsLayerTreeModel.AllowNodeReorder, True) self.projectLayerModel.setFlag(QgsLayerTreeModel.AllowNodeRename, False) self.projectLayerModel.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility) self.projectLayerModel.setFlag(QgsLayerTreeModel.AllowLegendChangeState) self.projectLayerModel.setFlag(QgsLayerTreeModel.AllowSymbologyChangeState) self.projectLayerModel.setAutoCollapseLegendNodes(-1) self.projectLayerView.setModel(self.projectLayerModel) menuProvider = LayerTreeMenu(self, self.projectLayerView) self.projectLayerView.setMenuProvider(menuProvider) self.projectLayerView.setCurrentLayer(self._plugin.iface.activeLayer()) self.projectLayerView.doubleClicked.connect(self._plugin.iface.actionOpenTable().trigger) self.projectLayerView.currentLayerChanged.connect(self._plugin.mapCanvas().setCurrentLayer) self.projectLayerView.currentLayerChanged.connect(self._plugin.iface.setActiveLayer) self._plugin.iface.currentLayerChanged.connect(self.projectLayerView.setCurrentLayer) self.layerViewAction = self.addDockAction( ':/plugins/ark/tree.svg', self.tr(u'Toggle Layer View'), callback=self._toggleLayerView, checkable=True) self.layerViewAction.setChecked(True) """ # Add Settings to the toolbar TODO move to end of dock? self.addDockSeparator() self.addDockAction(':/plugins/ark/settings.svg', self._plugin.tr(u'Project Settings'), self._triggerSettingsDialog) # Init the identify tool and add to the toolbar self.identifyAction = self.addDockAction( ':/plugins/ark/filter/identify.png', self._plugin.tr(u'Identify Items'), callback=self.triggerIdentifyAction, checkable=True ) self.identifyMapTool = MapToolIndentifyItems(self._plugin) self.identifyMapTool.setAction(self.identifyAction) # Init the Load Item tool and add to the toolbar self.showItemAction = self.addDockAction( ':/plugins/ark/filter/showContext.png', self._plugin.tr(u'Show Item'), callback=self._showItem ) # If the project or layers or legend indexes change make sure we stay updated self._plugin.legendInterface().groupIndexChanged.connect(self._groupIndexChanged) # Load the project settings when project is loaded def loadProject(self): if Settings.isProjectConfigured(): self.projectGroupIndex = layers.createLayerGroup(self._plugin.iface, Config.projectGroupName) # Load the layer collections self._addCollection('grid') self._addCollection('plan') self._addCollection('section') self._addCollection('site') self.drawingsGroupName = Config.drawings['context']['layersGroupName'] if (self.collection('grid').loadCollection() and self.collection('plan').loadCollection() and self.collection('section').loadCollection() and self.collection('site').loadCollection()): self._dock.loadProject(self._plugin) if self.collection('plan').isLogged(): self._itemLogPath = os.path.join(self.collection('plan').projectPath, self.collection('plan').settings.collectionPath, 'log/itemLog.csv') if not QFile.exists(self._itemLogPath): fd = open(self._itemLogPath, 'a') fd.write('timestamp,action,siteCode,classCode,itemId\n') fd.close() # TODO Think of a better way... # self.metadata = Metadata(self._dock.widget.sourceWidget) # self.metadata.metadataChanged.connect(self.updateMapToolAttributes) self._initialised = True return True return False # Close the project def closeProject(self): if self.identifyMapTool.action() and self.identifyMapTool.action().isChecked(): self._plugin.iface.actionPan().trigger() # TODO Unload the drawing tools! self._dock.closeProject() # self.metadata.metadataChanged.disconnect(self.updateMapToolAttributes) # Unload the layers for collection in self._collections: self._collections[collection].unload() del self._collections self._collections = {} self._plugin.iface.legendInterface().removeLegendLayerAction(self._layerSnappingAction) self._initialised = False # Project def crs(self): return self._plugin.iface.mapCanvas().mapSettings().destinationCrs() def crsId(self): return self.crs().authid() def projectFolder(self): proj = Project.dir() proj.cdUp() return proj.absolutePath() def configure(self): wizard = ProjectWizard(self._plugin.iface.mainWindow()) ok = wizard.exec_() if ok: if wizard.newProject(): if Project.exists(): Project.write() Project.clear() projectFolderPath = os.path.join(wizard.projectFolder(), 'project') if not QDir(projectFolderPath).mkpath('.'): return False projectFilePath = os.path.join(projectFolderPath, wizard.projectFilename() + '.qgs') Project.setFileName(projectFilePath) Settings.setProjectCode(wizard.project().projectCode()) Settings.setProjectName(wizard.project().projectName()) Settings.setSiteCode(wizard.project().siteCode()) if not Project.title(): Project.setTitle(wizard.project().projectCode() + ' - ' + wizard.project().projectName()) self._initialised = Project.write() if self._initialised: # We always want the site collection self._addCollection('site') self.collection('site').loadCollection() # Add the Site Location if entered location = wizard.projectLocation() if not location.isEmpty(): siteCode = wizard.project().siteCode() if wizard.project().siteCode() else wizard.project().projectCode() item = Item(siteCode, 'site', siteCode) source = Source('other', item) audit = Audit(Settings.userFullName(), utils.timestamp()) itemFeature = ItemFeature(item, 'loc', None, source, 'New Project Wizard', audit) layer = self.collection('site').layer('points') feature = QgsFeature(layer.pendingFields(), 0) feature.setGeometry(QgsGeometry(location)) attributes = itemFeature.toFeature(feature) layers.addFeatures([feature], layer) # Temp load of other collection, later do on demand self._addCollection('plan') self.collection('plan').loadCollection() self._addCollection('section') self.collection('section').loadCollection() self._addCollection('grid') self.collection('grid').loadCollection() # self._configureDrawing('context') # self._configureDrawing('plan') # self._configureDrawing('section') Settings.setProjectConfigured() return ok def _triggerSettingsDialog(self): if Settings.isProjectConfigured(): self.showSettingsDialog() else: self.configure() def _configureDrawing(self, drawing): Settings.drawingDir(drawing).mkpath('.') Settings.georefDrawingDir(drawing).mkpath('.') def _setDrawing(self, pmd): self.metadata.setSiteCode(pmd.siteCode) self.metadata.setClassCode(pmd.sourceClass) if pmd.sourceId > 0: self.metadata.setItemId(pmd.sourceId) self.metadata.setSourceId(pmd.sourceId) self.metadata.setSourceCode('drawing') self.metadata.setSourceClass(pmd.sourceClass) self.metadata.setSourceFile(pmd.filename) self.metadata.setEditor(Settings.userFullName()) def _groupIndexChanged(self, oldIndex, newIndex): if (oldIndex == self.projectGroupIndex): self.projectGroupIndex = newIndex def loadGeoLayer(self, geoFile, zoomToLayer=True): # TODO Check if already loaded, remove old one? self.geoLayer = QgsRasterLayer(geoFile.absoluteFilePath(), geoFile.completeBaseName()) self.geoLayer.renderer().setOpacity(Settings.drawingTransparency() / 100.0) QgsMapLayerRegistry.instance().addMapLayer(self.geoLayer) if (self.drawingsGroupIndex < 0): self.drawingsGroupIndex = layers.createLayerGroup( self._plugin.iface, self.drawingsGroupName, Config.projectGroupName) self._plugin.legendInterface().moveLayer(self.geoLayer, self.drawingsGroupIndex) if zoomToLayer: self._plugin.mapCanvas().setExtent(self.geoLayer.extent()) def clearDrawings(self): if (self.drawingsGroupIndex >= 0): self.drawingsLayerTreeGroup().removeAllChildren() def drawingsLayerTreeGroup(self): if (self.drawingsGroupIndex >= 0): return QgsProject.instance().layerTreeRoot().findGroup(self.drawingsGroupName) else: return None def isArkGroup(self, name): for collection in self._collections: if self._collections[collection].isCollectionGroup(name): return True return name == Config.projectGroupName or name == self.drawingsGroupName def isArkLayer(self, layerId): for collection in self._collections: if self._collections[collection].isCollectionLayer(layerId): return True return False def _pluginStylesPath(self): return os.path.join(self._plugin.pluginPath, 'ark', 'styles') def _styleFile(self, layerPath, layerName): # First see if the layer itself has a default style saved filePath = layerPath + '/' + layerName + '.qml' if QFile.exists(filePath): return filePath # Next see if the layer name has a style in the styles folder (which may # be a special folder, the site folder or the plugin folder) filePath = Settings.stylePath() + '/' + layerName + '.qml' if QFile.exists(filePath): return filePath # Finally, check the plugin folder for the default style filePath = self._pluginStylesPath() + '/' + layerName + '.qml' if QFile.exists(filePath): return filePath # If we didn't find that then something is wrong! return '' def _addCollection(self, collection): config = Config.collections[collection] path = config['path'] bufferPath = path + '/buffer' logPath = path + '/log' config['collection'] = collection config['crs'] = self.crs() config['parentGroupName'] = Config.projectGroupName for layer in config['layers']: name = layer['name'] layer['fields'] = config['fields'] layer['multi'] = config['multi'] layer['crs'] = self.crs() layer['path'] = layers.shapeFilePath(path, name) layer['stylePath'] = layers.styleFilePath(self._pluginStylesPath(), name) layer['buffer'] = config['buffer'] if config['buffer']: bufferName = name + Config.bufferSuffix layer['bufferName'] = bufferName layer['bufferName'] = bufferName layer['bufferPath'] = layers.shapeFilePath(bufferPath, bufferName) else: layer['bufferName'] = '' layer['bufferPath'] = '' layer['log'] = config['log'] if config['log']: logName = name + Config.logSuffix layer['logName'] = logName layer['logPath'] = layers.shapeFilePath(logPath, logName) else: layer['logName'] = '' layer['logPath'] = '' settings = CollectionSettings.fromArray(config) if config['item']: self._collections[collection] = ItemCollection(self._plugin.iface, self.projectFolder(), settings) else: self._collections[collection] = Collection(self._plugin.iface, self.projectFolder(), settings) def addDockSeparator(self): self._dock.toolbar.addSeparator() def addDockAction(self, iconPath, text, callback=None, enabled=True, checkable=False, tip=None, whatsThis=None): action = QAction(QIcon(iconPath), text, self._dock) if callback is not None: action.triggered.connect(callback) action.setEnabled(enabled) action.setCheckable(checkable) if tip is not None: action.setStatusTip(tip) if whatsThis is not None: action.setWhatsThis(whatsThis) self._dock.toolbar.addAction(action) # self.actions.append(action) return action def collection(self, collection): if collection in self._collections: return self._collections[collection] return None # Identify Tool def triggerIdentifyAction(self, checked): if checked: self._plugin.mapCanvas().setMapTool(self.identifyMapTool) else: self._plugin.mapCanvas().unsetMapTool(self.identifyMapTool) # Show Items Tool def _showItem(self): classCodes = sorted(set(self.collection('plan').uniqueValues('class'))) dialog = SelectItemDialog(Settings.siteCodes(), Settings.siteCode(), classCodes, self._plugin.iface.mainWindow()) if dialog.exec_(): self.drawingModule.showItem(dialog.item(), dialog.loadDrawings(), dialog.zoomToItem()) # Plan Tools def loadDrawing(self, item, zoomToDrawing=True): if not Config.classCodes[item.classCode()]['drawing']: return drawingDir = Settings.georefDrawingDir(item.classCode()) drawingDir.setFilter(QDir.Files | QDir.NoDotAndDotDot) name = item.name() nameList = [] nameList.append(name + '.png') nameList.append(name + '.tif') nameList.append(name + '.tiff') nameList.append(name + '_*.png') nameList.append(name + '_*.tif') nameList.append(name + '_*.tiff') drawingDir.setNameFilters(nameList) drawings = drawingDir.entryInfoList() for drawing in drawings: self._setDrawing(Drawing(drawing)) self._plugin.loadGeoLayer(drawing, zoomToDrawing) def loadSourceDrawings(self, item, clearDrawings=False): if item.isInvalid(): return sourceKeys = set() sourceKeys.add(item) itemRequest = item.featureRequest() for feature in self.collection('plan').layer('polygons').getFeatures(itemRequest): source = Source(feature) if source.item().isValid(): sourceKeys.add(source.item()) for feature in self.collection('plan').layer('lines').getFeatures(itemRequest): source = Source(feature) if source.item.isValid(): sourceKeys.add(source.item()) for feature in self.collection('plan').layer('points').getFeatures(itemRequest): source = Source(feature) if source.item().isValid(): sourceKeys.add(source.item()) if clearDrawings and len(sourceKeys) > 0: self.clearDrawings() for sourceKey in sorted(sourceKeys): self.loadDrawing(sourceKey) # Layer Methods def mergeBuffers(self): self._mergeBuffers(self.collection('plan')) self._mergeBuffers(self.collection('section')) self._mergeBuffers(self.collection('site')) def _mergeBuffers(self, collection): # Check the layers are writable name = collection.settings.collectionGroupName if not collection.isWritable(): self._plugin.showCriticalMessage( name + ' layers are not writable! Please correct the permissions and log out.', 0) return # Check the buffers contain valid data errors = self._preMergeBufferCheck(collection.buffer('points')) errors.extend(self._preMergeBufferCheck(collection.buffer('lines'))) errors.extend(self._preMergeBufferCheck(collection.buffer('polygons'))) if len(errors) > 0: dialog = ItemFeatureErrorDialog() dialog.loadErrors(errors) dialog.exec_() if not dialog.ignoreErrors(): return # Update the audit attributes timestamp = utils.timestamp() user = Settings.userFullName() self._preMergeBufferUpdate(collection.buffer('points'), timestamp, user) self._preMergeBufferUpdate(collection.buffer('lines'), timestamp, user) self._preMergeBufferUpdate(collection.buffer('polygons'), timestamp, user) # Finally actually merge the data if collection.mergeBuffers('Merge data', timestamp): self._plugin.showInfoMessage(name + ' data successfully merged.') # TODO pass current Item... self._logItemAction(Item(), 'Merge Buffers', timestamp) # TODO Signal out layers merged for schematic dock to catch # if self._editSchematic: # self._editSchematic = False # self._dock.activateSchematicCheck() # self._findContext() else: self._plugin.showCriticalMessage( name + ' data merge failed! Some data has not been saved, please check your data.', 5) def _preMergeBufferCheck(self, layer): errors = [] row = 0 for feature in layer.getFeatures(): # Set up the error template error = ItemFeatureError() error.layer = layer.name() error.row = row row = row + 1 error.fid = feature.id() error.feature.fromFeature(feature) # Feature must be valid if not feature.isValid(): error.field = 'feature' error.message = 'Invalid Feature' errors.append(copy.deepcopy(error)) # Geometry must be valid error.field = 'geometry' if feature.geometry() is None: error.message = 'No Geometry' errors.append(copy.deepcopy(error)) elif feature.geometry().isEmpty(): error.message = 'Empty Geometry' errors.append(copy.deepcopy(error)) else: error.message = 'Invalid Geometry' geomErrs = feature.geometry().validateGeometry() # Ignore the last error, it is just a total for err in geomErrs[:-1]: error.message = err.what() errors.append(copy.deepcopy(error)) # Key attributes that must always be populated if utils.isEmpty(feature.attribute('site')): error.field = 'site' error.message = 'Site Code is required' errors.append(copy.deepcopy(error)) if utils.isEmpty(feature.attribute('class')): error.field = 'class' error.message = 'Class Code is required' errors.append(copy.deepcopy(error)) if utils.isEmpty(feature.attribute('id')): error.field = 'id' error.message = 'ID is required' errors.append(copy.deepcopy(error)) if utils.isEmpty(feature.attribute('category')): error.field = 'category' error.message = 'Category is required' errors.append(copy.deepcopy(error)) if utils.isEmpty(feature.attribute('source_cd')): error.field = 'source_cd' error.message = 'Source Code is required' error.ignore = True # errors.append(copy.deepcopy(error)) # Source attributes required depend on the source type if feature.attribute('source_cd') == 'creator' or feature.attribute('source_cd') == 'other': if utils.isEmpty(feature.attribute('comment')): error.field = 'source_cd' error.message = 'Comment is required for Source type of Creator or Other' error.ignore = True # errors.append(copy.deepcopy(error)) elif feature.attribute('source_cd') == 'survey': if utils.isEmpty(feature.attribute('file')): error.field = 'source_cd' error.message = 'Filename is required for Source type of Survey' error.ignore = True # errors.append(copy.deepcopy(error)) else: # 'drw', 'unc', 'skt', 'cln', 'mod', 'inf' if ((feature.attribute('source_cd') == 'drawing' or feature.attribute('source_cd') == 'unchecked') and utils.isEmpty(feature.attribute('file'))): error.field = 'source_cd' error.message = 'Filename is required for Source type of Drawing' error.ignore = True # errors.append(copy.deepcopy(error)) if (utils.isEmpty(feature.attribute('source_cl')) or utils.isEmpty(feature.attribute('source_id'))): error.field = 'source_cd' error.message = 'Source Class and ID is required' error.ignore = True # errors.append(copy.deepcopy(error)) return errors def _preMergeBufferUpdate(self, layer, timestamp, user): createdIdx = layer.fieldNameIndex('created') creatorIdx = layer.fieldNameIndex('creator') modifiedIdx = layer.fieldNameIndex('modified') modifierIdx = layer.fieldNameIndex('modifier') for feature in layer.getFeatures(): if utils.isEmpty(feature.attribute('created')): layer.changeAttributeValue(feature.id(), createdIdx, timestamp) layer.changeAttributeValue(feature.id(), creatorIdx, user) else: layer.changeAttributeValue(feature.id(), modifiedIdx, timestamp) layer.changeAttributeValue(feature.id(), modifierIdx, user) def resetBuffers(self): self.collection('plan').resetBuffers('Clear Buffers') self.collection('section').resetBuffers('Clear Buffers') self.collection('site').resetBuffers('Clear Buffers') # TODO Signal out layers reset for schematic dock to catch # if self._editSchematic: # self._editSchematic = False # self._dock.activateSchematicCheck() def _confirmDelete(self, itemId, title='Confirm Delete Item', label=None): if not label: label = 'This action ***DELETES*** item ' + \ str(itemId) + ' from the saved data.\n\nPlease enter the item ID to confirm.' confirm, ok = QInputDialog.getText(None, title, label, text='') return ok and confirm == str(itemId) def _logItemAction(self, item, action, timestamp=None): if self.collection('plan').settings.log: if not timestamp: timestamp = utils.timestamp() fd = open(self._itemLogPath, 'a') fd.write(utils.doublequote(timestamp) + ',' + utils.doublequote(action) + ',' + item.toCsv() + '\n') fd.close() def editInBuffers(self, item): timestamp = utils.timestamp() if self.collection('plan').moveItemToBuffers(item, 'Edit Item', timestamp): self._logItemAction(item, 'Edit Item', timestamp) self._metadataFromBuffers(item) def deleteItem(self, item): if self._confirmDelete(item.itemId(), 'Confirm Delete Item'): timestamp = utils.timestamp() if self.collection('plan').deleteItem(item, 'Delete Item', timestamp): self._logItemAction(item, 'Delete Item', timestamp) def applyItemActions(self, item, mapAction=MapAction.NoMapAction, filterAction=FilterAction.NoFilterAction, drawingAction=DrawingAction.NoDrawingAction): if drawingAction != DrawingAction.NoDrawingAction: self.loadSourceDrawings(item, drawingAction == DrawingAction.LoadDrawings) if filterAction != FilterAction.NoFilterAction: self._plugin.filter().applyItemAction(item, filterAction) if mapAction == MapAction.ZoomMap: self._zoomToItem(item) elif mapAction == MapAction.PanMap: self._panToItem(item) elif mapAction == MapAction.MoveMap: self._moveToItem(item) self._plugin.mapCanvas().refresh() def showItem(self, item, loadDrawings=True, zoom=True): self._plugin.showMessage('Loading ' + item.itemLabel()) self._plugin.filter().filterItem(item) if loadDrawings: self.loadSourceDrawings(item, True) if zoom: self._zoomToItem(item) def panToItem(self, item, highlight=False): if highlight: self._plugin.filter().highlightItem(item) self._panToItem(item) self._plugin.mapCanvas().refresh() def zoomToItem(self, item, highlight=False): if highlight: self._plugin.filter().highlightItem(item) self._zoomToItem(item) self._plugin.mapCanvas().refresh() def moveToItem(self, item, highlight=False): ret = -1 if highlight: ret = self._plugin.filter().highlightItem(item) self._moveToItem(item) self._plugin.mapCanvas().refresh() return ret def _moveToItem(self, item): self._moveToExtent(self.itemExtent(item)) def _moveToExtent(self, extent): if extent is None or extent.isNull() or extent.isEmpty(): return mapExtent = self._plugin.mapCanvas().extent() if (extent.width() > mapExtent.width() or extent.height() > mapExtent.height() or extent.width() * extent.height() > mapExtent.width() * mapExtent.height()): self._zoomToExtent(extent) else: self._panToExtent(extent) def _panToItem(self, item): self._panToExtent(self.itemExtent(item)) def _panToExtent(self, extent): if extent is None or extent.isNull() or extent.isEmpty(): return self._plugin.mapCanvas().setCenter(extent.center()) def _zoomToItem(self, item): self._zoomToExtent(self.itemExtent(item)) def _zoomToExtent(self, extent): if extent is None or extent.isNull() or extent.isEmpty(): return extent.scale(1.05) self._plugin.mapCanvas().setExtent(extent) def filterItem(self, item): self._plugin.filter().filterItem(item) self._plugin.mapCanvas().refresh() def excludeFilterItem(self, item): self._plugin.filter().excludeItem(item) self._plugin.mapCanvas().refresh() def highlightItem(self, item): self._plugin.filter().highlightItem(item) self._plugin.mapCanvas().refresh() def addHighlightItem(self, item): self._plugin.filter().addHighlightItem(item) self._plugin.mapCanvas().refresh() def itemExtent(self, item): requestKey = self._plugin.data().nodesItem(item) request = requestKey.featureRequest() points = self._requestAsLayer(request, self.collection('plan').layer('points'), 'points') lines = self._requestAsLayer(request, self.collection('plan').layer('lines'), 'lines') polygons = self._requestAsLayer(request, self.collection('plan').layer('polygons'), 'polygons') extent = None extent = self._combineExtentWith(extent, polygons) extent = self._combineExtentWith(extent, lines) extent = self._combineExtentWith(extent, points) return extent def _requestAsLayer(self, request, fromLayer, toName): toLayer = layers.cloneAsMemoryLayer(fromLayer, toName) layers.copyFeatureRequest(request, fromLayer, toLayer) toLayer.updateExtents() return toLayer def _combineExtentWith(self, extent, layer): if (layer is not None and layer.isValid() and layer.featureCount() > 0): layerExtent = layer.extent() if layerExtent.isNull() or layerExtent.isEmpty(): return extent if extent is None: extent = layerExtent else: extent.combineExtentWith(layerExtent) return extent def _sectionItemList(self, siteCode): # TODO in 2.14 use addOrderBy() request = utils.featureRequest(utils.eqClause('site', siteCode) + ' and ' + utils.eqClause('class', 'sec')) features = layers.getAllFeaturesRequest(request, self.collection('plan').layer('lines')) lst = [] for feature in features: lst.append(ItemFeature(feature)) lst.sort() return lst def _sectionLineGeometry(self, item): if item and item.isValid(): sln = ItemFeature(item, 'sln') request = sln.featureRequest() features = layers.getAllFeaturesRequest(request, self.collection('plan').layer('lines')) for feature in features: return QgsGeometry(feature.geometry()) return QgsGeometry() def _metadataFromBuffers(self, item): feature = self._getFeature(self.collection('plan').buffer('polygons'), item, 'sch') if feature: self.metadata.fromFeature(feature) return feature = self._getFeature(self.collection('plan').buffer('polygons'), item, 'scs') if feature: self.metadata.fromFeature(feature) return feature = self._getFeature(self.collection('plan').buffer('polygons'), item) if feature: self.metadata.fromFeature(feature) return feature = self._getFeature(self.collection('plan').buffer('lines'), item) if feature: self.metadata.fromFeature(feature) return feature = self._getFeature(self.collection('plan').buffer('points'), item) if feature: self.metadata.fromFeature(feature) def _getFeature(self, layer, item, category=''): req = None if category: sch = ItemFeature(item, 'sch') req = sch.featureRequest() else: req = item.featureRequest() try: return layer.getFeatures(req).next() except StopIteration: return None return None
class TestQgsRasterRendererCreateSld(unittest.TestCase): """ This class tests the creation of SLD from QGis raster layers """ @classmethod def setUpClass(self): pass def setUp(self): pass def tearDown(self): pass def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'landsat.tif') rasterFileInfo = QFileInfo(myPath) self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) def testSingleBandPseudoColorRenderer_Interpolated(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics(bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Interpolated) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append(QgsColorRampShader.ColorRampItem(index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'ramp') colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) def testSingleBandPseudoColorRenderer_Discrete(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics(bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Discrete) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append(QgsColorRampShader.ColorRampItem(index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'intervals') colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) def testSingleBandPseudoColorRenderer_Exact(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics(bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append(QgsColorRampShader.ColorRampItem(index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'values') self.assertFalse(colorMap.hasAttribute('extendend')) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) # add check that is set ColoMap extended="true" if colormap is bigger that 255 entries # !NOTE! can't reuse previous shader => segmentation fault shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(255): items.append(QgsColorRampShader.ColorRampItem(index, QColor.fromHsv(index, 255, 255, 255), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader) # self.raster_layer.setRenderer(rasterRenderer) # dom, root = self.rendererToSld(self.raster_layer.renderer()) # self.assertTrue( colorMap.hasAttribute( 'extendend' ) ) # self.assertEqual( colorMap.attribute( 'extendend' ), 'true' ) def testPalettedRasterRenderer(self): # create 10 color classes #classesString = '122 0 0 0 255 122\n123 1 1 1 255 123\n124 2 2 2 255 124\n125 3 3 3 255 125\n126 4 4 4 255 126\n127 5 5 5 255 127\n128 6 6 6 255 128\n129 7 7 7 255 129\n130 8 8 8 255 130' classesString = '' for index in range(10): classesString += '{0} {0} {0} {0} 255 {0}\n'.format(index) classes = QgsPalettedRasterRenderer.classDataFromString(classesString) rasterRenderer = QgsPalettedRasterRenderer( self.raster_layer.dataProvider(), 3, classes) self.raster_layer.setRenderer(rasterRenderer) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'values') self.assertFalse(colorMap.hasAttribute('extendend')) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) # add check that is set ColoMap extended="true" if colormap is bigger that 255 entries classesString = '' values = range(255) for index in range(255): classesString += '{0} {1} {1} {1} 255 {0}\n'.format(index, random.choice(values)) classes = QgsPalettedRasterRenderer.classDataFromString(classesString) rasterRenderer = QgsPalettedRasterRenderer( self.raster_layer.dataProvider(), 3, classes) self.raster_layer.setRenderer(rasterRenderer) dom, root = self.rendererToSld(self.raster_layer.renderer()) colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertTrue(colorMap.hasAttribute('extended')) self.assertEqual(colorMap.attribute('extended'), 'true') def testMultiBandColorRenderer(self): rasterRenderer = QgsMultiBandColorRenderer( self.raster_layer.dataProvider(), 3, 1, 2) self.raster_layer.setRenderer(rasterRenderer) self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.StretchToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:RedChannel', '3') self.assertChannelBand(root, 'sld:GreenChannel', '1') self.assertChannelBand(root, 'sld:BlueChannel', '2') def testSingleBandGrayRenderer(self): # check with StretchToMinimumMaximum rasterRenderer = QgsSingleBandGrayRenderer(self.raster_layer.dataProvider(), 3) self.raster_layer.setRenderer(rasterRenderer) self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.StretchToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) maximum = self.raster_layer.renderer().contrastEnhancement().maximumValue() minmum = self.raster_layer.renderer().contrastEnhancement().minimumValue() self.assertEqual(minmum, 51) self.assertEqual(maximum, 172) # check default values dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') elements = root.elementsByTagName('sld:ContrastEnhancement') self.assertEqual(len(elements), 1) enhancement = elements.at(0).toElement() self.assertFalse(enhancement.isNull()) normalize = enhancement.firstChildElement('sld:Normalize') self.assertFalse(normalize.isNull()) self.assertVendorOption(normalize, 'algorithm', 'StretchToMinimumMaximum') self.assertVendorOption(normalize, 'minValue', '51') self.assertVendorOption(normalize, 'maxValue', '172') elements = root.elementsByTagName('sld:ColorMap') self.assertEqual(len(elements), 1) colorMap = elements.at(0).toElement() self.assertFalse(colorMap.isNull()) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(len(colorMapEntries), 2) clorMap1 = colorMapEntries.at(0) self.assertEqual(clorMap1.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual(clorMap1.attributes().namedItem('quantity').nodeValue(), '0') clorMap2 = colorMapEntries.at(1) self.assertEqual(clorMap2.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual(clorMap2.attributes().namedItem('quantity').nodeValue(), '255') # check when StretchAndClipToMinimumMaximum # then min/max have always to be the real one and not that set in the contrastEnhancement self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.StretchAndClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) minmum = self.raster_layer.renderer().contrastEnhancement().setMinimumValue(100) maximum = self.raster_layer.renderer().contrastEnhancement().maximumValue() minmum = self.raster_layer.renderer().contrastEnhancement().minimumValue() self.assertEqual(minmum, 100) self.assertEqual(maximum, 172) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') elements = root.elementsByTagName('sld:ContrastEnhancement') self.assertEqual(len(elements), 1) enhancement = elements.at(0).toElement() self.assertFalse(enhancement.isNull()) normalize = enhancement.firstChildElement('sld:Normalize') self.assertFalse(normalize.isNull()) self.assertVendorOption(normalize, 'minValue', '51') self.assertVendorOption(normalize, 'maxValue', '172') elements = root.elementsByTagName('sld:ColorMap') self.assertEqual(len(elements), 1) colorMap = elements.at(0).toElement() self.assertFalse(colorMap.isNull()) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(len(colorMapEntries), 4) clorMap1 = colorMapEntries.at(0) self.assertEqual(clorMap1.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual(clorMap1.attributes().namedItem('quantity').nodeValue(), '100') self.assertEqual(clorMap1.attributes().namedItem('opacity').nodeValue(), '0') clorMap2 = colorMapEntries.at(1) self.assertEqual(clorMap2.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual(clorMap2.attributes().namedItem('quantity').nodeValue(), '100') clorMap3 = colorMapEntries.at(2) self.assertEqual(clorMap3.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual(clorMap3.attributes().namedItem('quantity').nodeValue(), '172') clorMap4 = colorMapEntries.at(3) self.assertEqual(clorMap4.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual(clorMap4.attributes().namedItem('quantity').nodeValue(), '172') self.assertEqual(clorMap4.attributes().namedItem('opacity').nodeValue(), '0') # check when ClipToMinimumMaximum # then min/max have always to be the real one and not that set in the contrastEnhancement self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.ClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) minmum = self.raster_layer.renderer().contrastEnhancement().setMinimumValue(100) maximum = self.raster_layer.renderer().contrastEnhancement().maximumValue() minmum = self.raster_layer.renderer().contrastEnhancement().minimumValue() self.assertEqual(minmum, 100) self.assertEqual(maximum, 172) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '3') elements = root.elementsByTagName('sld:ContrastEnhancement') self.assertEqual(len(elements), 1) enhancement = elements.at(0).toElement() self.assertFalse(enhancement.isNull()) normalize = enhancement.firstChildElement('sld:Normalize') self.assertFalse(normalize.isNull()) self.assertVendorOption(normalize, 'minValue', '51') self.assertVendorOption(normalize, 'maxValue', '172') elements = root.elementsByTagName('sld:ColorMap') self.assertEqual(len(elements), 1) colorMap = elements.at(0).toElement() self.assertFalse(colorMap.isNull()) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(len(colorMapEntries), 4) clorMap1 = colorMapEntries.at(0) self.assertEqual(clorMap1.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual(clorMap1.attributes().namedItem('quantity').nodeValue(), '100') self.assertEqual(clorMap1.attributes().namedItem('opacity').nodeValue(), '0') clorMap2 = colorMapEntries.at(1) self.assertEqual(clorMap2.attributes().namedItem('color').nodeValue(), '#000000') self.assertEqual(clorMap2.attributes().namedItem('quantity').nodeValue(), '100') clorMap3 = colorMapEntries.at(2) self.assertEqual(clorMap3.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual(clorMap3.attributes().namedItem('quantity').nodeValue(), '172') clorMap4 = colorMapEntries.at(3) self.assertEqual(clorMap4.attributes().namedItem('color').nodeValue(), '#ffffff') self.assertEqual(clorMap4.attributes().namedItem('quantity').nodeValue(), '172') self.assertEqual(clorMap4.attributes().namedItem('opacity').nodeValue(), '0') def testRasterRenderer(self): class fakerenderer(QgsRasterRenderer): def __init__(self, interface): QgsRasterRenderer.__init__(self, interface, '') rasterRenderer = fakerenderer(self.raster_layer.dataProvider()) self.raster_layer.setRenderer(rasterRenderer) # check opacity default value is not exported dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) # check if opacity is not the default value rasterRenderer.setOpacity(1.1) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertOpacity(root, '1.1') # check gamma properties from [-100:0] stretched to [0:1] # and (0:100] stretche dto (1:100] # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-100'}) # self.assertGamma(root, '0') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-50'}) # self.assertGamma(root, '0.5') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '0'}) # self.assertGamma(root, '1') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '1'}) # self.assertGamma(root, '1') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '100'}) # self.assertGamma(root, '100') # # input contrast are always integer, btw the value is managed also if it's double # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '1.1'}) # self.assertGamma(root, '1.1') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '1.6'}) # self.assertGamma(root, '1.6') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-50.5'}) # self.assertGamma(root, '0.495') # dom, root = self.rendererToSld(rasterRenderer, {'contrast': '-0.1'}) # self.assertGamma(root, '0.999') def testStretchingAlgorithm(self): rasterRenderer = QgsMultiBandColorRenderer( self.raster_layer.dataProvider(), 3, 1, 2) self.raster_layer.setRenderer(rasterRenderer) # check StretchToMinimumMaximum stretching alg self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.StretchToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel', 'StretchToMinimumMaximum', '51', '172') self.assertContrastEnhancement(root, 'sld:GreenChannel', 'StretchToMinimumMaximum', '122', '130') self.assertContrastEnhancement(root, 'sld:BlueChannel', 'StretchToMinimumMaximum', '133', '148') # check StretchAndClipToMinimumMaximum stretching alg self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.StretchAndClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel', 'ClipToZero', '51', '172') self.assertContrastEnhancement(root, 'sld:GreenChannel', 'ClipToZero', '122', '130') self.assertContrastEnhancement(root, 'sld:BlueChannel', 'ClipToZero', '133', '148') # check ClipToMinimumMaximum stretching alg self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.ClipToMinimumMaximum, limits=QgsRasterMinMaxOrigin.MinMax) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel', 'ClipToMinimumMaximum', '51', '172') self.assertContrastEnhancement(root, 'sld:GreenChannel', 'ClipToMinimumMaximum', '122', '130') self.assertContrastEnhancement(root, 'sld:BlueChannel', 'ClipToMinimumMaximum', '133', '148') # check NoEnhancement stretching alg self.raster_layer.setContrastEnhancement(algorithm=QgsContrastEnhancement.NoEnhancement) dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertContrastEnhancement(root, 'sld:RedChannel') self.assertContrastEnhancement(root, 'sld:GreenChannel') self.assertContrastEnhancement(root, 'sld:BlueChannel') def assertVendorOption(self, root, name, expectedValue): """Set expectedValue=None to check that the vendor option is not present.""" vendorOptions = root.elementsByTagName('sld:VendorOption') found = False for vendorOptionIndex in range(vendorOptions.count()): vendorOption = vendorOptions.at(vendorOptionIndex) self.assertEqual('sld:VendorOption', vendorOption.nodeName()) if (vendorOption.attributes().namedItem('name').nodeValue() == name): found = True self.assertEqual(vendorOption.firstChild().nodeValue(), expectedValue) if (expectedValue is None) and found: self.fail("found VendorOption: {} where supposed not present".format(name)) if expectedValue and not found: self.fail("Not found VendorOption: {}".format(name)) def assertGamma(self, root, expectedValue, index=0): enhancement = root.elementsByTagName('sld:ContrastEnhancement').item(index) gamma = enhancement.firstChildElement('sld:GammaValue') self.assertEqual(expectedValue, gamma.firstChild().nodeValue()) def assertOpacity(self, root, expectedValue, index=0): opacity = root.elementsByTagName('sld:Opacity').item(index) self.assertEqual(expectedValue, opacity.firstChild().nodeValue()) def assertNoOpacity(self, root): opacities = root.elementsByTagName('sld:Opacity') self.assertEqual(opacities.size(), 0) def assertContrastEnhancement(self, root, bandTag, expectedAlg=None, expectedMin=None, expectedMax=None, index=0): channelSelection = root.elementsByTagName('sld:ChannelSelection').item(index) self.assertIsNotNone(channelSelection) band = channelSelection.firstChildElement(bandTag) # check if no enhancement alg is iset if (not expectedAlg): contrastEnhancementName = band.firstChildElement('sld:ContrastEnhancement') self.assertEqual('', contrastEnhancementName.firstChild().nodeName()) return # check if enhancement alg is set contrastEnhancementName = band.firstChildElement('sld:ContrastEnhancement') self.assertEqual('sld:Normalize', contrastEnhancementName.firstChild().nodeName()) normalize = contrastEnhancementName.firstChildElement('sld:Normalize') vendorOptions = normalize.elementsByTagName('VendorOption') for vendorOptionIndex in range(vendorOptions.count()): vendorOption = vendorOptions.at(vendorOptionIndex) self.assertEqual('VendorOption', vendorOption.nodeName()) if (vendorOption.attributes().namedItem('name').nodeValue() == 'algorithm'): self.assertEqual(expectedAlg, vendorOption.firstChild().nodeValue()) elif (vendorOption.attributes().namedItem('name').nodeValue() == 'minValue'): self.assertEqual(expectedMin, vendorOption.firstChild().nodeValue()) elif (vendorOption.attributes().namedItem('name').nodeValue() == 'maxValue'): self.assertEqual(expectedMax, vendorOption.firstChild().nodeValue()) else: self.fail('Unrecognised vendorOption name {}'.format(vendorOption.attributes().namedItem('name').nodeValue())) def assertChannelBand(self, root, bandTag, expectedValue, index=0): channelSelection = root.elementsByTagName('sld:ChannelSelection').item(index) self.assertIsNotNone(channelSelection) band = channelSelection.firstChildElement(bandTag) sourceChannelName = band.firstChildElement('sld:SourceChannelName') self.assertEqual(expectedValue, sourceChannelName.firstChild().nodeValue()) def rendererToSld(self, renderer, properties={}): dom = QDomDocument() root = dom.createElement("FakeRoot") dom.appendChild(root) renderer.toSld(dom, root, properties) return dom, root
def testTransparency(self): myPath = os.path.join(unitTestDataPath('raster'), 'band1_float32_noct_epsg4326.tif') myFileInfo = QFileInfo(myPath) myBaseName = myFileInfo.baseName() myRasterLayer = QgsRasterLayer(myPath, myBaseName) myMessage = 'Raster not loaded: %s' % myPath assert myRasterLayer.isValid(), myMessage renderer = QgsSingleBandGrayRenderer(myRasterLayer.dataProvider(), 1) myRasterLayer.setRenderer(renderer) myRasterLayer.setContrastEnhancementAlgorithm( QgsContrastEnhancement.StretchToMinimumMaximum, QgsRasterLayer.ContrastEnhancementMinMax) myContrastEnhancement = myRasterLayer.renderer().contrastEnhancement() #print ("myContrastEnhancement.minimumValue = %.17g" % # myContrastEnhancement.minimumValue()) #print ("myContrastEnhancement.maximumValue = %.17g" % # myContrastEnhancement.maximumValue()) # Unfortunately the minimum/maximum values calculated in C++ and Python # are slightly different (e.g. 3.3999999521443642e+38 x # 3.3999999521444001e+38) # It is not clear where the precision is lost. # We set the same values as C++. myContrastEnhancement.setMinimumValue(-3.3319999287625854e+38) myContrastEnhancement.setMaximumValue(3.3999999521443642e+38) #myType = myRasterLayer.dataProvider().dataType(1); #myEnhancement = QgsContrastEnhancement(myType); myTransparentSingleValuePixelList = [] rasterTransparency = QgsRasterTransparency() myTransparentPixel1 = \ QgsRasterTransparency.TransparentSingleValuePixel() myTransparentPixel1.min = -2.5840000772112106e+38 myTransparentPixel1.max = -1.0879999684602689e+38 myTransparentPixel1.percentTransparent = 50 myTransparentSingleValuePixelList.append(myTransparentPixel1) myTransparentPixel2 = \ QgsRasterTransparency.TransparentSingleValuePixel() myTransparentPixel2.min = 1.359999960575336e+37 myTransparentPixel2.max = 9.520000231087593e+37 myTransparentPixel2.percentTransparent = 70 myTransparentSingleValuePixelList.append(myTransparentPixel2) rasterTransparency.setTransparentSingleValuePixelList( myTransparentSingleValuePixelList) rasterRenderer = myRasterLayer.renderer() assert rasterRenderer rasterRenderer.setRasterTransparency(rasterTransparency) QgsMapLayerRegistry.instance().addMapLayers([ myRasterLayer, ]) myMapRenderer = QgsMapRenderer() myLayers = QStringList() myLayers.append(myRasterLayer.id()) myMapRenderer.setLayerSet(myLayers) myMapRenderer.setExtent(myRasterLayer.extent()) myChecker = QgsRenderChecker() myChecker.setControlName("expected_raster_transparency") myChecker.setMapRenderer(myMapRenderer) myResultFlag = myChecker.runTest("raster_transparency_python"); assert myResultFlag, "Raster transparency rendering test failed"
def create_raster_layer(matrix): driver = gdal.GetDriverByName("GTiff") filename = tempfile.mktemp(prefix="hmtk", suffix=".tif") # sort the data by lon, lat gridded_data = numpy.array( sorted(matrix, key=lambda row: (90 + row[1]) * 180 + (180 + row[0]))) # extract it into separate vars lons, lats, vals = ( gridded_data[:, 0], gridded_data[:, 1], gridded_data[:, 3]) ncols = lons[lons == lons[0]].size nrows = lats[lats == lats[0]].size # put values in a grid gridded_vals = vals.reshape((ncols, nrows)).T dataset = driver.Create(filename, ncols, nrows, 1, gdal.GDT_Float32) dataset.SetGeoTransform(( min(lons), (max(lons) - min(lons)) / ncols, 0, max(lats), 0, -(max(lats) - min(lats)) / nrows)) out_srs = osr.SpatialReference() out_srs.ImportFromEPSG(4326) dataset.SetProjection(out_srs.ExportToWkt()) out_band = dataset.GetRasterBand(1) out_band.WriteArray(gridded_vals) out_band.SetNoDataValue(0) out_band.FlushCache() out_band = None dataset = None fileInfo = QFileInfo(filename) baseName = fileInfo.baseName() layer = QgsRasterLayer(filename, baseName) stat = layer.dataProvider().bandStatistics(1) minVal = stat.minimumValue maxVal = stat.maximumValue entries_nr = 20 colorRamp = QgsStyleV2().defaultStyle().colorRamp("Spectral") currentValue = float(minVal) intervalDiff = float(maxVal - minVal) / float(entries_nr - 1) colorRampItems = [] for i in reversed(xrange(entries_nr)): item = QgsColorRampShader.ColorRampItem() item.value = currentValue item.label = unicode(currentValue) currentValue += intervalDiff item.color = colorRamp.color(float(i) / float(entries_nr)) item.color.setAlphaF(0.75) colorRampItems.append(item) rasterShader = QgsRasterShader() colorRampShader = QgsColorRampShader() colorRampShader.setColorRampItemList(colorRampItems) colorRampShader.setColorRampType(QgsColorRampShader.INTERPOLATED) rasterShader.setRasterShaderFunction(colorRampShader) layer.setDrawingStyle('SingleBandPseudoColor') layer.renderer().setShader(rasterShader) QgsMapLayerRegistry.instance().addMapLayer(layer) return layer
def testTransparency(self): myPath = os.path.join(unitTestDataPath('raster'), 'band1_float32_noct_epsg4326.tif') myFileInfo = QFileInfo(myPath) myBaseName = myFileInfo.baseName() myRasterLayer = QgsRasterLayer(myPath, myBaseName) myMessage = 'Raster not loaded: %s' % myPath assert myRasterLayer.isValid(), myMessage renderer = QgsSingleBandGrayRenderer(myRasterLayer.dataProvider(), 1) myRasterLayer.setRenderer(renderer) myRasterLayer.setContrastEnhancement( QgsContrastEnhancement.StretchToMinimumMaximum, QgsRasterMinMaxOrigin.MinMax) myContrastEnhancement = myRasterLayer.renderer().contrastEnhancement() # print ("myContrastEnhancement.minimumValue = %.17g" % # myContrastEnhancement.minimumValue()) # print ("myContrastEnhancement.maximumValue = %.17g" % # myContrastEnhancement.maximumValue()) # Unfortunately the minimum/maximum values calculated in C++ and Python # are slightly different (e.g. 3.3999999521443642e+38 x # 3.3999999521444001e+38) # It is not clear where the precision is lost. # We set the same values as C++. myContrastEnhancement.setMinimumValue(-3.3319999287625854e+38) myContrastEnhancement.setMaximumValue(3.3999999521443642e+38) #myType = myRasterLayer.dataProvider().dataType(1); #myEnhancement = QgsContrastEnhancement(myType); myTransparentSingleValuePixelList = [] rasterTransparency = QgsRasterTransparency() myTransparentPixel1 = \ QgsRasterTransparency.TransparentSingleValuePixel() myTransparentPixel1.min = -2.5840000772112106e+38 myTransparentPixel1.max = -1.0879999684602689e+38 myTransparentPixel1.percentTransparent = 50 myTransparentSingleValuePixelList.append(myTransparentPixel1) myTransparentPixel2 = \ QgsRasterTransparency.TransparentSingleValuePixel() myTransparentPixel2.min = 1.359999960575336e+37 myTransparentPixel2.max = 9.520000231087593e+37 myTransparentPixel2.percentTransparent = 70 myTransparentSingleValuePixelList.append(myTransparentPixel2) rasterTransparency.setTransparentSingleValuePixelList( myTransparentSingleValuePixelList) rasterRenderer = myRasterLayer.renderer() assert rasterRenderer rasterRenderer.setRasterTransparency(rasterTransparency) QgsProject.instance().addMapLayers([myRasterLayer, ]) myMapSettings = QgsMapSettings() myMapSettings.setLayers([myRasterLayer]) myMapSettings.setExtent(myRasterLayer.extent()) myChecker = QgsRenderChecker() myChecker.setControlName("expected_raster_transparency") myChecker.setMapSettings(myMapSettings) myResultFlag = myChecker.runTest("raster_transparency_python") assert myResultFlag, "Raster transparency rendering test failed"
def add_layer_to_map(item: QStandardItem): """ Add a layer to the map :param layer: :return: """ # No multiselect so there is only ever one item pt_data: ProjectTreeData = item.data(Qt.UserRole) project = pt_data.project map_layer: QRaveMapLayer = pt_data.data settings = Settings() # Loop over all the parent group layers for this raster # ensuring they are in the tree in correct, nested order ancestry = [] if map_layer.exists is True: parent = item.parent() while parent is not None and len(ancestry) < 50: ancestry.append((parent.text(), parent.row())) parent = parent.parent() else: # Layer does not exist. do not try to put it on the map return ancestry.reverse() parentGroup = None for agroup in ancestry: parentGroup = QRaveMapLayer._addgrouptomap(agroup[0], agroup[1], parentGroup) assert parentGroup, "All rasters should be nested and so parentGroup should be instantiated by now" # Loop over all the parent group layers for this raster # ensuring they are in the tree in correct, nested order # Only add the layer if it's not already in the registry exists = False existing_layers = QgsProject.instance().mapLayersByName( map_layer.label) layers_ancestry = [ QRaveMapLayer.get_layer_ancestry(lyr) for lyr in existing_layers ] # Now we compare the ancestry group labels to the business logic ancestry branch names # to see if this layer is already in the map for lyr in layers_ancestry: if len(lyr) == len(ancestry) \ and all(iter([ancestry[x][0] == lyr[x] for x in range(len(ancestry))])): exists = True break if not exists: layer_uri = map_layer.layer_uri rOutput = None # This might be a basemap if map_layer.layer_type == QRaveMapLayer.LayerTypes.WEBTILE: rOutput = QgsRasterLayer(layer_uri, map_layer.label, 'wms') elif map_layer.layer_type in [ QRaveMapLayer.LayerTypes.LINE, QRaveMapLayer.LayerTypes.POLYGON, QRaveMapLayer.LayerTypes.POINT ]: if map_layer.layer_name is not None: layer_uri += "|layername={}".format(map_layer.layer_name) rOutput = QgsVectorLayer(layer_uri, map_layer.label, "ogr") elif map_layer.layer_type == QRaveMapLayer.LayerTypes.RASTER: # Raster rOutput = QgsRasterLayer(layer_uri, map_layer.label) if rOutput is not None: ########################################## # Symbology ########################################## symbology = map_layer.bl_attr[ 'symbology'] if map_layer.bl_attr is not None and 'symbology' in map_layer.bl_attr else None # If the business logic has symbology defined if symbology is not None: qml_fname = '{}.qml'.format(symbology) os.path.abspath( os.path.join(project.project_dir, qml_fname)) # Here are the search paths for QML files in order of precedence hierarchy = [ os.path.abspath( os.path.join(project.project_dir, qml_fname)), # This is the default one os.path.abspath( os.path.join(SYMBOLOGY_DIR, project.project_type, qml_fname)), os.path.abspath( os.path.join(SYMBOLOGY_DIR, 'Shared', qml_fname)) ] # Find the first match try: chosen_qml = next( iter([ candidate for candidate in hierarchy if os.path.isfile(candidate) ])) # Report to the terminal if we couldn't find a qml file to use if chosen_qml is None: settings.msg_bar( "Missing Symbology", "Could not find a valid .qml symbology file for layer {}. Search paths: [{}]" .format(layer_uri, ', '.join(hierarchy)), level=Qgis.Warning) # Apply the QML file else: rOutput.loadNamedStyle(chosen_qml) except StopIteration: settings.log( 'Could not find valid symbology for layer at any of the following search paths: [ {} ]' .format(', '.join(hierarchy)), Qgis.Warning) ############################################################ # Transparency. A few notes: # - QML transparency will prevail for rasters before 3.18 # - We set this here so that QML layer transparency will be # overruled ############################################################ transparency = 0 try: if 'transparency' in map_layer.bl_attr: transparency = int(map_layer.bl_attr['transparency']) except Exception as e: settings.log( 'Error deriving transparency from layer: {}'.format(e)) try: if transparency > 0: if rOutput.__class__ is QgsVectorLayer: rOutput.setLayerTransparency(transparency) # rOutput.triggerRepaint() elif rOutput.__class__ is QgsRasterLayer: renderer = rOutput.renderer() renderer.setOpacity((100 - transparency) / 100.0) # rOutput.triggerRepaint() except Exception as e: settings.log( 'Error deriving transparency from layer: {}'.format(e)) QgsProject.instance().addMapLayer(rOutput, False) parentGroup.insertLayer(item.row(), rOutput) # if the layer already exists trigger a refresh else: QgsProject.instance().mapLayersByName( map_layer.label)[0].triggerRepaint()