def testAbbreviateLayoutUnits(self): """Test abbreviating layout units""" units = [ QgsUnitTypes.LayoutMillimeters, QgsUnitTypes.LayoutCentimeters, QgsUnitTypes.LayoutMeters, QgsUnitTypes.LayoutInches, QgsUnitTypes.LayoutFeet, QgsUnitTypes.LayoutPoints, QgsUnitTypes.LayoutPicas, QgsUnitTypes.LayoutPixels ] used = set() for u in units: self.assertTrue(QgsUnitTypes.toString(u)) self.assertTrue(QgsUnitTypes.toAbbreviatedString(u)) self.assertFalse(QgsUnitTypes.toAbbreviatedString(u) in used) used.add(QgsUnitTypes.toAbbreviatedString(u))
def testAbbreviateRenderUnits(self): """Test abbreviating render units""" units = [ QgsUnitTypes.RenderMillimeters, QgsUnitTypes.RenderMapUnits, QgsUnitTypes.RenderPixels, QgsUnitTypes.RenderPercentage, QgsUnitTypes.RenderPoints, QgsUnitTypes.RenderInches, QgsUnitTypes.RenderUnknownUnit, QgsUnitTypes.RenderMetersInMapUnits ] used = set() for u in units: self.assertTrue(QgsUnitTypes.toString(u)) self.assertTrue(QgsUnitTypes.toAbbreviatedString(u)) self.assertFalse(QgsUnitTypes.toAbbreviatedString(u) in used) used.add(QgsUnitTypes.toAbbreviatedString(u))
def testAbbreviateLayoutUnits(self): """Test abbreviating layout units""" units = [QgsUnitTypes.LayoutMillimeters, QgsUnitTypes.LayoutCentimeters, QgsUnitTypes.LayoutMeters, QgsUnitTypes.LayoutInches, QgsUnitTypes.LayoutFeet, QgsUnitTypes.LayoutPoints, QgsUnitTypes.LayoutPicas, QgsUnitTypes.LayoutPixels] used = set() for u in units: self.assertTrue(QgsUnitTypes.toString(u)) self.assertTrue(QgsUnitTypes.toAbbreviatedString(u)) self.assertFalse(QgsUnitTypes.toAbbreviatedString(u) in used) used.add(QgsUnitTypes.toAbbreviatedString(u))
def testAbbreviateRenderUnits(self): """Test abbreviating render units""" units = [QgsUnitTypes.RenderMillimeters, QgsUnitTypes.RenderMapUnits, QgsUnitTypes.RenderPixels, QgsUnitTypes.RenderPercentage, QgsUnitTypes.RenderPoints, QgsUnitTypes.RenderInches, QgsUnitTypes.RenderUnknownUnit, QgsUnitTypes.RenderMetersInMapUnits] used = set() for u in units: self.assertTrue(QgsUnitTypes.toString(u)) self.assertTrue(QgsUnitTypes.toAbbreviatedString(u)) self.assertFalse(QgsUnitTypes.toAbbreviatedString(u) in used) used.add(QgsUnitTypes.toAbbreviatedString(u))
def __init__(self): QDialog.__init__(self) self.setupUi(self) # dialog buttons box self.DialogButtons.rejected.connect(self.closing) self.DialogButtons.button( QDialogButtonBox.Save).setText("Export to CSV") self.DialogButtons.button(QDialogButtonBox.Save).clicked.connect( self.export_to_csv) from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa sampling_layer = AcATaMa.dockwidget.QCBox_SamplingFile_AA.currentLayer( ) thematic_layer = AcATaMa.dockwidget.QCBox_ThematicRaster.currentLayer() # get AccuracyAssessment or init new instance if sampling_layer: # sampling file valid if sampling_layer in Classification.instances: # classification exists for this file classification = Classification.instances[sampling_layer] if classification.accuracy_assessment: self.accuracy_assessment = classification.accuracy_assessment # restore config to dialog self.z_score.setValue(self.accuracy_assessment.z_score) self.CSV_separator.setText( self.accuracy_assessment.csv_separator) self.CSV_decimal_sep.setText( self.accuracy_assessment.csv_decimal) else: self.accuracy_assessment = AccuracyAssessment( classification) classification.accuracy_assessment = self.accuracy_assessment # fill the area units self.area_unit.clear() layer_dist_unit = thematic_layer.crs().mapUnits() for area_unit in AREA_UNITS: self.area_unit.addItem("{} ({})".format( QgsUnitTypes.toString(area_unit), QgsUnitTypes.toAbbreviatedString(area_unit))) # set the area unit saved or based on the sampling file by default if self.accuracy_assessment.area_unit is not None: self.area_unit.setCurrentIndex(self.accuracy_assessment.area_unit) else: self.accuracy_assessment.area_unit = QgsUnitTypes.distanceToAreaUnit( layer_dist_unit) self.area_unit.setCurrentIndex(self.accuracy_assessment.area_unit) self.area_unit.currentIndexChanged.connect( lambda: self.reload(msg_bar=False)) self.z_score.valueChanged.connect(lambda: self.reload(msg_bar=False)) self.CSV_separator.textChanged.connect(lambda value: setattr( self.accuracy_assessment, "csv_separator", value)) self.CSV_decimal_sep.textChanged.connect(lambda value: setattr( self.accuracy_assessment, "csv_decimal", value)) self.reloadButton.clicked.connect(lambda: self.reload(msg_bar=True))
def get_pixel_size(layer): """Get the pixel size and pixel size and units from raster layer. """ if layer is None or layer.type() != QgsMapLayer.RasterLayer: return None, None, None if layer.crs().isGeographic(): ft = 'f' # this will convert 1.99348e-05 to 0.000020 else: ft = 'g' # this will convert 2.0 to 2 or 0.5, '0.5' pixel_units = QgsUnitTypes.toAbbreviatedString(layer.crs().mapUnits()) # Adjust for Aust/UK spelling #pixel_units = pixel_units.replace('meters', 'metres') pixel_size = format(layer.rasterUnitsPerPixelX(), ft) #Convert to float or int #pixel_size = int(float(pixel_size)) if int(float(pixel_size)) == float(pixel_size) else float(pixel_size) return pixel_size, pixel_units, ft
def compute(self): from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa AcATaMa.dockwidget.QPBtn_ComputeTheAccurasyAssessment.setText( "Processing, please wait ...") QApplication.processEvents() # get labels from classification buttons labels = {} for button_config in self.classification.buttons_config.values(): labels[button_config["thematic_class"]] = button_config["name"] # get the classified and thematic map values thematic_map_values = [] classified_values = [] samples_outside_the_thematic = [] classification_points = [ point for point in self.classification.points if point.is_classified ] points_ordered = sorted(classification_points, key=lambda p: p.shape_id) for point in points_ordered: # classification from the pixel values in the thematic map thematic_map_value = self.ThematicR.get_pixel_value_from_pnt( point.QgsPnt) if not thematic_map_value: samples_outside_the_thematic.append(point) continue thematic_map_values.append(int(thematic_map_value)) # classified value made/checked by user with classification buttons classified_value = self.classification.buttons_config[ point.classif_id]["thematic_class"] classified_values.append(int(classified_value)) # all unique and sorted values values = sorted(set(thematic_map_values + classified_values)) # Construct a value->index dictionary indices = dict((val, i) for (i, val) in enumerate(values)) # calculate the error/confusion matrix # https://github.com/nltk/nltk/blob/develop/nltk/metrics/confusionmatrix.py # # classified # t | | L1 | L2 | L3 | L4 | # h | L1 | | | | | # e | L2 | | | | | # m | L3 | | | | | # a | L4 | | | | | # error_matrix = [[0 for column in values] for row in values] for thematic, classified in zip(thematic_map_values, classified_values): error_matrix[indices[thematic]][indices[classified]] += 1 # calculate the total number of pixel in the thematic raster # by each thematic raster class used in the classification buttons for thematic_map_value in values: if thematic_map_value not in self.thematic_pixels_count: self.thematic_pixels_count[ thematic_map_value] = self.ThematicR.get_total_pixels_by_value( thematic_map_value) # values necessary for results self.values = values self.labels = labels self.error_matrix = error_matrix self.samples_outside_the_thematic = samples_outside_the_thematic # set area by pixel self.pixel_area_base = self.ThematicR.qgs_layer.rasterUnitsPerPixelX( ) * self.ThematicR.qgs_layer.rasterUnitsPerPixelY() self.pixel_area_value = self.pixel_area_base * QgsUnitTypes.fromUnitToUnitFactor( self.base_area_unit, self.area_unit) self.pixel_area_unit = QgsUnitTypes.toAbbreviatedString(self.area_unit)
def select_thematic_raster(self, layer): def clear_and_unset_the_thematic_raster(): with block_signals_to(self.QCBox_ThematicRaster): self.QCBox_ThematicRaster.setCurrentIndex(-1) self.QCBox_band_ThematicRaster.clear() self.nodata_ThematicRaster.setValue(-1) # SimpRS self.minDistance_SimpRS.setSuffix("") self.minDistance_SimpRS.setToolTip("") self.minDistance_SimpRS.setValue(0) # StraRS self.minDistance_StraRS.setSuffix("") self.minDistance_StraRS.setToolTip("") self.minDistance_StraRS.setValue(0) # disable sampling tab self.scrollAreaWidgetContents_S.setDisabled(True) # unset the thematic classes in classification instance sampling_layer = self.QCBox_SamplingFile.currentLayer() if sampling_layer and sampling_layer in Classification.instances: Classification.instances[ sampling_layer].with_thematic_classes = False # updated state of sampling file selected for accuracy assessment tab self.set_sampling_file_accuracy_assessment() # first check if not layer or not valid_file_selected_in(self.QCBox_ThematicRaster, "thematic raster"): clear_and_unset_the_thematic_raster() return # check if thematic raster data type is integer or byte if layer.dataProvider().dataType(1) not in [1, 2, 3, 4, 5]: clear_and_unset_the_thematic_raster() iface.messageBar().pushMessage( "AcATaMa", "Error, thematic raster must be byte or integer as data type.", level=Qgis.Warning) return # set band count self.QCBox_band_ThematicRaster.clear() self.QCBox_band_ThematicRaster.addItems( [str(x) for x in range(1, layer.bandCount() + 1)]) # set nodata value of thematic raster in nodata field self.nodata_ThematicRaster.setValue(get_nodata_value(layer)) # set/update the units in minimum distance items in sampling tab layer_dist_unit = layer.crs().mapUnits() str_unit = QgsUnitTypes.toString(layer_dist_unit) abbr_unit = QgsUnitTypes.toAbbreviatedString(layer_dist_unit) # Set the properties of the QdoubleSpinBox based on the QgsUnitTypes of the thematic raster # https://qgis.org/api/classQgsUnitTypes.html # SimpRS self.minDistance_SimpRS.setSuffix(" {}".format(abbr_unit)) self.minDistance_SimpRS.setToolTip( "Minimum distance in {} (units based on thematic raster selected)". format(str_unit)) self.minDistance_SimpRS.setRange( 0, 360 if layer_dist_unit == QgsUnitTypes.DistanceDegrees else 10e6) self.minDistance_SimpRS.setDecimals(4 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.minDistance_SimpRS.setSingleStep(0.0001 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.minDistance_SimpRS.setValue(0) # StraRS self.minDistance_StraRS.setSuffix(" {}".format(abbr_unit)) self.minDistance_StraRS.setToolTip( "Minimum distance in {} (units based on thematic raster selected)". format(str_unit)) self.minDistance_StraRS.setRange( 0, 360 if layer_dist_unit == QgsUnitTypes.DistanceDegrees else 10e6) self.minDistance_StraRS.setDecimals(4 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.minDistance_StraRS.setSingleStep(0.0001 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.minDistance_StraRS.setValue(0) # enable sampling tab self.scrollAreaWidgetContents_S.setEnabled(True)
def setup_gui(self): self.NavTiles_widgetFile.setHidden(True) self.NavTiles_widgetAOI.setHidden(True) self.SliderNavigationBlock.setEnabled(False) self.QCBox_BuildNavType.currentIndexChanged[str].connect( self.set_navigation_type_tool) # set properties to QgsMapLayerComboBox self.QCBox_VectorFile.setCurrentIndex(-1) self.QCBox_VectorFile.setFilters(QgsMapLayerProxyModel.VectorLayer) # handle connect layer selection with render canvas self.QCBox_VectorFile.currentIndexChanged.connect( lambda: self.render_over_thematic(self.QCBox_VectorFile. currentLayer())) # call to browse the render file self.QPBtn_BrowseVectorFile.clicked.connect( lambda: self.browser_dialog_to_load_file( self.QCBox_VectorFile, dialog_title=self.tr("Select the vector file"), file_filters=self.tr( "Vector files (*.gpkg *.shp);;All files (*.*)"))) # buttons connections self.QPBtn_BuildNavigationTools.clicked.connect(self.build_tools) self.QPBtn_BuildNavigation.clicked.connect( self.call_to_build_navigation) self.TilesColor.clicked.connect(self.change_tiles_color) self.CleanNavigation.clicked.connect(self.clean_navigation) self.AOI_Picker.clicked.connect(self.activate_deactivate_AOI_picker) self.DeleteAllAOI.clicked.connect(self.clean_all_aoi_drawn) self.ZoomToTiles.clicked.connect(self.zoom_to_tiles) # slider and spinbox connection self.SliderNavigation.valueChanged.connect(self.highlight) self.SliderNavigation.sliderReleased.connect( lambda: self.change_tile_from_slider(self.SliderNavigation.value() )) self.currentTile.valueChanged.connect(self.change_tile_from_spinbox) # #### setup units in tile size # set/update the units in tileSize item layer_unit = self.layer_to_edit.qgs_layer.crs().mapUnits() if layer_unit == QgsUnitTypes.DistanceUnknownUnit: layer_unit = QgsUnitTypes.DistanceMeters str_unit = QgsUnitTypes.toString(layer_unit) + \ "\nWARNING: the layer does not have a valid map unit considering meters as the base unit!" else: str_unit = QgsUnitTypes.toString(layer_unit) abbr_unit = QgsUnitTypes.toAbbreviatedString(layer_unit) # Set the properties of the QdoubleSpinBox based on the QgsUnitTypes of the thematic layer # https://qgis.org/api/classQgsUnitTypes.html self.tileSize.setSuffix(" {}".format(abbr_unit)) self.tileSize.setToolTip( "The height/width of the tile to build the navigation, in {}\n" "(units based on the current thematic layer to edit)\n" "(rebuild the navigation to make the changes)".format(str_unit)) self.tileSize.setRange( 0, 360 if layer_unit == QgsUnitTypes.DistanceDegrees else 10e10) self.tileSize.setDecimals(4 if layer_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.tileSize.setSingleStep(0.0001 if layer_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) default_tile_size = { QgsUnitTypes.DistanceMeters: 15000, QgsUnitTypes.DistanceKilometers: 15, QgsUnitTypes.DistanceFeet: 49125, QgsUnitTypes.DistanceNauticalMiles: 8.125, QgsUnitTypes.DistanceYards: 16500, QgsUnitTypes.DistanceMiles: 9.375, QgsUnitTypes.DistanceDegrees: 0.1375, QgsUnitTypes.DistanceCentimeters: 1500000, QgsUnitTypes.DistanceMillimeters: 15000000 } self.tileSize.setValue(default_tile_size[layer_unit])
def __init__(self, sampling_layer, columns, rows): QDialog.__init__(self) self.sampling_layer = sampling_layer self.setupUi(self) ClassificationDialog.instance = self # flags self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) # get classification or init new instance if sampling_layer in Classification.instances: self.classification = Classification.instances[sampling_layer] else: self.classification = Classification(sampling_layer) self.classification.grid_columns = columns self.classification.grid_rows = rows #### settings the classification dialog # set dialog title self.setWindowTitle("Classification of samples for " + sampling_layer.name()) # resize the classification dialog if self.classification.dialog_size: self.resize(*self.classification.dialog_size) # disable enter action self.QPBtn_SetClassification.setAutoDefault(False) self.QPBtn_unclassifySampleButton.setAutoDefault(False) # go to sample ID action self.GoTo_ID_Button.clicked.connect(self.go_to_sample_id) self.GoTo_ID.returnPressed.connect(self.go_to_sample_id) self.GoTo_ID.textChanged.connect( lambda: self.GoTo_ID.setStyleSheet("")) # open in Google Earth self.QPBtn_OpenInGE.clicked.connect( self.open_current_point_in_google_engine) # set properties and default value for the fit to sample spinBox based on sampling file layer_dist_unit = self.sampling_layer.crs().mapUnits() str_unit = QgsUnitTypes.toString(layer_dist_unit) abbr_unit = QgsUnitTypes.toAbbreviatedString(layer_dist_unit) self.radiusFitToSample.setSuffix(" {}".format(abbr_unit)) self.radiusFitToSample.setToolTip( "Units in {} for set the zoom radius to the current sample\n" "(units based on sampling file selected)".format(str_unit)) self.radiusFitToSample.setRange( 0, 360 if layer_dist_unit == QgsUnitTypes.DistanceDegrees else 10e6) self.radiusFitToSample.setDecimals(4 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.radiusFitToSample.setSingleStep(0.0001 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.radiusFitToSample.setValue(self.classification.fit_to_sample) # set total samples self.QPBar_SamplesNavigation.setMaximum(len( self.classification.points)) # actions for fit and go to current sample self.radiusFitToSample.valueChanged.connect( lambda: self.show_and_go_to_current_sample(highlight=False)) self.currentSample.clicked.connect( lambda: self.show_and_go_to_current_sample(highlight=True)) # move through samples self.nextSample.clicked.connect(self.next_sample) self.nextSampleNotClassified.clicked.connect( self.next_sample_not_classified) self.previousSample.clicked.connect(self.previous_sample) self.previousSampleNotClassified.clicked.connect( self.previous_sample_not_classified) # dialog buttons box self.closeButton.rejected.connect(self.closing) # disable enter action self.closeButton.button(QDialogButtonBox.Close).setAutoDefault(False) # init current point self.current_sample_idx = self.classification.current_sample_idx self.current_sample = None self.set_current_sample() # set classification buttons self.classification_btns_config = ClassificationButtonsConfig( self.classification.buttons_config) self.create_classification_buttons( buttons_config=self.classification.buttons_config) self.QPBtn_SetClassification.clicked.connect( self.open_set_classification_dialog) self.QPBtn_unclassifySampleButton.clicked.connect( self.unclassify_sample) # create dynamic size of the view render widgets windows # inside the grid with columns x rows divide by splitters h_splitters = [] view_widgets = [] for row in range(self.classification.grid_rows): splitter = QSplitter(Qt.Horizontal) for column in range(self.classification.grid_columns): new_view_widget = ClassificationViewWidget() splitter.addWidget(new_view_widget) h_splitters.append(splitter) view_widgets.append(new_view_widget) v_splitter = QSplitter(Qt.Vertical) for splitter in h_splitters: v_splitter.addWidget(splitter) # add to classification dialog self.widget_view_windows.layout().addWidget(v_splitter) # save instances ClassificationDialog.view_widgets = view_widgets # setup view widget [ view_widget.setup_view_widget(sampling_layer) for view_widget in ClassificationDialog.view_widgets ] for idx, view_widget in enumerate(ClassificationDialog.view_widgets): view_widget.id = idx # set the label names for each view for num_view, view_widget in enumerate( ClassificationDialog.view_widgets): view_widget.QLabel_ViewID.setText("View {}:".format(num_view + 1)) # restore view widgets status for config_id, view_config in self.classification.view_widgets_config.items( ): for view_widget in ClassificationDialog.view_widgets: if config_id == view_widget.id: view_widget = ClassificationDialog.view_widgets[config_id] # select the file for this view widget if exists and is loaded in Qgis layer_name = view_config["layer_name"] if not layer_name and view_config["render_file_path"]: layer_name = os.path.splitext( os.path.basename( view_config["render_file_path"]))[0] file_index = view_widget.QCBox_RenderFile.findText( layer_name, Qt.MatchFixedString) if file_index != -1: # select layer if exists in Qgis with block_signals_to(view_widget.QCBox_RenderFile): view_widget.QCBox_RenderFile.setCurrentIndex( file_index) elif view_config["render_file_path"] and os.path.isfile( view_config["render_file_path"]): # load file and select in view if this exists and not load in Qgis load_and_select_filepath_in( view_widget.QCBox_RenderFile, view_config["render_file_path"], layer_name=layer_name) elif view_config["render_file_path"] and not os.path.isfile( view_config["render_file_path"]): self.MsgBar.pushMessage( "Could not to load the layer '{}' in the view {}: no such file {}" .format( layer_name, "'{}'".format(view_config["view_name"]) if view_config["view_name"] else view_widget.id + 1, view_config["render_file_path"]), level=Qgis.Warning, duration=-1) else: self.MsgBar.pushMessage( "Could not to load the layer '{}' in the view {} (for network layers use save/load a Qgis project)" .format( layer_name, "'{}'".format(view_config["view_name"]) if view_config["view_name"] else view_widget.id + 1), level=Qgis.Warning, duration=-1) # TODO: restore size by view widget # view_widget.resize(*view_config["view_size"]) view_widget.QLabel_ViewName.setText( view_config["view_name"]) view_widget.scaleFactor.setValue( view_config["scale_factor"]) # active render layer in canvas view_widget.set_render_layer( view_widget.QCBox_RenderFile.currentLayer())