class DigitizingToolsChooseRemaining(QtWidgets.QDialog, FORM_CLASS): def __init__(self, iface, editLayer, pkValues, featDict, title): QtWidgets.QDialog.__init__(self) self.setupUi(self) self.iface = iface self.editLayer = editLayer self.pkValues = pkValues self.featDict = featDict self.chooseId.addItems(list(self.pkValues.keys())) self.setWindowTitle(title) self.label.setText(QtWidgets.QApplication.translate( "digitizingtools", "Choose which already existing feature should remain")) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) @QtCore.pyqtSlot(int) def on_chooseId_currentIndexChanged(self, thisIndex): aPkValue = self.chooseId.currentText() aGeom = self.featDict[self.pkValues[aPkValue]].geometry() hlColor, hlFillColor, hlBuffer, hlMinWidth = dtGetHighlightSettings() self.hl = QgsHighlight(self.iface.mapCanvas(), aGeom, self.editLayer) self.hl.setColor(hlColor) self.hl.setFillColor(hlFillColor) self.hl.setBuffer(hlBuffer) self.hl.setWidth(hlMinWidth) @QtCore.pyqtSlot() def reject(self): self.done(0) @QtCore.pyqtSlot() def accept(self): self.pkValueToKeep = self.chooseId.currentText() self.done(1)
def runTestForLayer(self, layer, testname): tempdir = tempfile.mkdtemp() layer = QgsVectorLayer(layer, 'Layer', 'ogr') QgsProject.instance().addMapLayer(layer) self.iface.mapCanvas().setExtent(layer.extent()) geom = next(layer.getFeatures()).geometry() highlight = QgsHighlight(self.iface.mapCanvas(), geom, layer) color = QColor(Qt.red) highlight.setColor(color) highlight.setWidth(2) color.setAlpha(50) highlight.setFillColor(color) highlight.show() image = QImage(QSize(400, 400), QImage.Format_ARGB32) image.fill(Qt.white) painter = QPainter() painter.begin(image) self.iface.mapCanvas().render(painter) painter.end() control_image = os.path.join(tempdir, 'highlight_{}.png'.format(testname)) image.save(control_image) checker = QgsRenderChecker() checker.setControlPathPrefix("highlight") checker.setControlName("expected_highlight_{}".format(testname)) checker.setRenderedImage(control_image) self.assertTrue(checker.compareImages("highlight_{}".format(testname))) shutil.rmtree(tempdir)
def onResultTableSelChanged(self): cur_row_index = self.dockWidget.tableResult.currentRow() if cur_row_index > -1: self.clearHighlight(self.intersected_h_list) self.clearHighlight(self.intersection_h_list) f_geometry = QgsGeometry() f_geometry = QgsGeometry.fromWkt( self.dockWidget.tableResult.item(cur_row_index, 1).text()) h = QgsHighlight(self.iface.mapCanvas(), f_geometry, self.inters_layer) h.setColor(QColor(26, 200, 0, 220)) h.setWidth(6) h.setFillColor(QColor(26, 200, 0, 150)) self.intersected_h_list.append(h) if_geometry = QgsGeometry() if_geometry = QgsGeometry.fromWkt( self.dockWidget.tableResult.item(cur_row_index, 2).text()) ih = QgsHighlight(self.iface.mapCanvas(), if_geometry, self.inters_layer) ih.setColor(QColor(230, 0, 0, 220)) ih.setWidth(6) ih.setFillColor(QColor(230, 0, 0, 150)) self.intersection_h_list.append(ih)
def highlightByGeometry(self, geometry, color=QColor(255, 0, 0, 128)): highlight = QgsHighlight(self.mTheCanvas, geometry, self.mTheLayer) highlight.setColor(color) highlight.setFillColor(color) highlight.setBuffer(0.5) highlight.setMinWidth(6) highlight.setWidth(6) highlight.show() self.highlightList.append(highlight) return
def highlightFeature(self, theFeature): highlight = QgsHighlight(self.mTheCanvas, theFeature.geometry(), self.mTheLayer) highlight.setColor(QColor(255,0,0,128)) highlight.setFillColor(QColor(255,0,0,128)) highlight.setBuffer(0.5) highlight.setMinWidth(6) highlight.setWidth(6) highlight.show() self.highlightList.append(highlight) return
class DigitizingToolsChooseRemaining(QtWidgets.QDialog, FORM_CLASS): def __init__(self, iface, editLayer, pkValues, featDict, title): QtWidgets.QDialog.__init__(self) self.setupUi(self) self.iface = iface self.editLayer = editLayer self.pkValues = pkValues self.featDict = featDict self.chooseId.addItems(list(self.pkValues.keys())) self.setWindowTitle(title) self.label.setText( QtWidgets.QApplication.translate( "digitizingtools", "Choose which already existing feature should remain")) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) def clearHighlight(self): try: self.hl.hide() except: pass @QtCore.pyqtSlot(int) def on_chooseId_currentIndexChanged(self, thisIndex): self.clearHighlight() aPkValue = self.chooseId.currentText() aGeom = self.featDict[self.pkValues[aPkValue]].geometry() hlColor, hlFillColor, hlBuffer, hlMinWidth = dtGetHighlightSettings() self.hl = QgsHighlight(self.iface.mapCanvas(), aGeom, self.editLayer) self.hl.setColor(hlColor) self.hl.setFillColor(hlFillColor) self.hl.setBuffer(hlBuffer) self.hl.setWidth(hlMinWidth) self.hl.show() @QtCore.pyqtSlot() def reject(self): self.clearHighlight() self.done(0) @QtCore.pyqtSlot() def accept(self): self.clearHighlight() self.pkValueToKeep = self.chooseId.currentText() self.done(1)
class LrsErrorVisualizer(object): def __init__(self, mapCanvas): self.errorHighlight = None self.mapCanvas = mapCanvas def __del__(self): if self.errorHighlight: del self.errorHighlight def clearHighlight(self): if self.errorHighlight: del self.errorHighlight self.errorHighlight = None def highlight(self, error, crs): self.clearHighlight() if not error: return # QgsHighlight does reprojection from layer CRS layer = QgsVectorLayer('Point?crs=' + crsString(crs), 'LRS error highlight', 'memory') self.errorHighlight = QgsHighlight(self.mapCanvas, error.geo, layer) # highlight point size is hardcoded in QgsHighlight self.errorHighlight.setWidth(2) self.errorHighlight.setColor(Qt.yellow) self.errorHighlight.show() def zoom(self, error, crs): if not error: return geo = error.geo mapSettings = self.mapCanvas.mapSettings() if isProjectCrsEnabled() and getProjectCrs() != crs: geo = QgsGeometry(error.geo) transform = QgsCoordinateTransform(crs, QgsProject().instance().crs()) geo.transform(transform) if geo.type() == QgsWkbTypes.PointGeometry: p = geo.asPoint() bufferCrs = getProjectCrs() if isProjectCrsEnabled() else crs b = 2000 if not bufferCrs.isGeographic() else 2000 / 100000 # buffer extent = QgsRectangle(p.x() - b, p.y() - b, p.x() + b, p.y() + b) else: # line extent = geo.boundingBox() extent.scale(2) self.mapCanvas.setExtent(extent) self.mapCanvas.refresh();
def onMapClickedTableSelChanged(self): cur_row_index = self.map_clicked_dlg.tableClickedWays.currentRow() if cur_row_index > -1: self.clearAllHighlights() f_geometry = QgsGeometry() f_geometry = QgsGeometry.fromWkt( self.map_clicked_dlg.tableClickedWays.item(cur_row_index, 1).text()) h = QgsHighlight(self.iface.mapCanvas(), f_geometry, self.current_layer) h.setColor(QColor(0, 15, 183, 220)) h.setWidth(6) h.setFillColor(QColor(0, 15, 183, 150)) self.mapclicked_h_list.append(h) self.setButtonOkStatus()
def pointPicked(self, event): for b in self.rubberBands: del b self.rubberBands[:] = [] fieldX = self.cmbXField.currentField() fieldY = self.cmbYField.currentField() artist = event.artist indices = event.ind for i in indices: x = self.xData[artist.name][i] y = self.yData[artist.name][i] if isinstance(x, int): expr = '"{}" = {} AND '.format(fieldX, x) elif isinstance(x, float): expr = 'abs("{}" - {}) <= 0.0000001 AND '.format(fieldX, x) elif isinstance(x, (str, unicode)): expr = """"{}" = '{}' AND """.format(fieldX, x) else: expr = """"{}" = '{}' AND """.format(fieldX, x.toString('yyyy-MM-dd')) if isinstance(y, float): expr += 'abs("{}" - {}) <= 0.0000001'.format(fieldY, y) elif isinstance(y, (str, unicode)): expr += """"{}" = '{}'""".format(fieldY, y) else: expr += '"{}" = {}'.format(fieldY, y) layer = self.cmbLayer.currentLayer() expression = QgsExpression(expr) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) context.appendScope(QgsExpressionContextUtils.mapSettingsScope(self.canvas.mapSettings())) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) request = QgsFeatureRequest(expression, context) for f in layer.getFeatures(request): hl = QgsHighlight(self.canvas, f.geometry(), layer) hl.setColor(QColor(255, 0, 0)) hl.setWidth(2) self.rubberBands.append(hl)
def handle(self, message): if message.username != self.settings.get_user_name_prefix(): return for highlight in self.highlightFeatures: self.iface.mapCanvas().scene().removeItem(highlight) self.highlightFeatures = [] if len(message.identifiedFeatureMrids) == 0: return layer = None if message.featureType == self.settings.get_types_route_segment(): layer = QgsProject.instance().mapLayersByName( self.settings.get_layers_route_segment_name())[0] elif message.featureType == self.settings.get_types_route_node(): layer = QgsProject.instance().mapLayersByName( self.settings.get_layers_route_node_name())[0] filterExpression = "" for i in range(len(message.identifiedFeatureMrids)): mrid = message.identifiedFeatureMrids[i] if i == len(message.identifiedFeatureMrids) - 1: filterExpression += f'"mrid" = \'{mrid}\'' else: filterExpression += f'"mrid" = \'{mrid}\' OR ' features = layer.getFeatures( QgsFeatureRequest().setFilterExpression(filterExpression)) color = QColor(64, 224, 208) for feature in features: identifyHighlight = QgsHighlight(self.iface.mapCanvas(), feature.geometry(), layer) identifyHighlight.setWidth(5) identifyHighlight.setColor(color) self.highlightFeatures.append(identifyHighlight) for highlight in self.highlightFeatures: highlight.show() layer.triggerRepaint()
class FeatureSelectorWidget(QWidget): feature_identified = pyqtSignal(QgsFeature) def __init__(self, parent): QWidget.__init__(self, parent) edit_layout = QHBoxLayout() edit_layout.setContentsMargins(0, 0, 0, 0) edit_layout.setSpacing(2) self.setLayout(edit_layout) self.line_edit = QLineEdit(self) self.line_edit.setReadOnly(True) edit_layout.addWidget(self.line_edit) self.highlight_feature_button = QToolButton(self) self.highlight_feature_button.setPopupMode(QToolButton.MenuButtonPopup) self.highlight_feature_action = QAction( QgsApplication.getThemeIcon("/mActionHighlightFeature.svg"), "Highlight feature", self) self.scale_highlight_feature_action = QAction( QgsApplication.getThemeIcon("/mActionScaleHighlightFeature.svg"), "Scale and highlight feature", self) self.pan_highlight_feature_action = QAction( QgsApplication.getThemeIcon("/mActionPanHighlightFeature.svg"), "Pan and highlight feature", self) self.highlight_feature_button.addAction(self.highlight_feature_action) self.highlight_feature_button.addAction( self.scale_highlight_feature_action) self.highlight_feature_button.addAction( self.pan_highlight_feature_action) self.highlight_feature_button.setDefaultAction( self.highlight_feature_action) edit_layout.addWidget(self.highlight_feature_button) self.map_identification_button = QToolButton(self) self.map_identification_button.setIcon( QgsApplication.getThemeIcon("/mActionMapIdentification.svg")) self.map_identification_button.setText("Select on map") self.map_identification_button.setCheckable(True) edit_layout.addWidget(self.map_identification_button) self.map_identification_button.clicked.connect(self.map_identification) self.highlight_feature_button.triggered.connect( self.highlight_action_triggered) self.layer = None self.map_tool = None self.canvas = None self.window_widget = None self.highlight = None self.feature = QgsFeature() def set_canvas(self, map_canvas): self.map_tool = QgsMapToolIdentifyFeature(map_canvas) self.map_tool.setButton(self.map_identification_button) self.canvas = map_canvas def set_layer(self, layer): self.layer = layer def set_feature(self, feature, canvas_extent=CanvasExtent.Fixed): self.line_edit.clear() self.feature = feature if self.feature is None or not self.feature.isValid( ) or self.layer is None: return expression = QgsExpression(self.layer.displayExpression()) context = QgsExpressionContext() scope = QgsExpressionContextScope() context.appendScope(scope) scope.setFeature(feature) feature_title = expression.evaluate(context) if feature_title == "": feature_title = feature.id() self.line_edit.setText(str(feature_title)) self.highlight_feature(canvas_extent) def clear(self): self.feature = QgsFeature() self.line_edit.clear() @pyqtSlot() def map_identification(self): if self.layer is None or self.map_tool is None or self.canvas is None: return self.map_tool.setLayer(self.layer) self.canvas.setMapTool(self.map_tool) self.window_widget = QWidget.window(self) self.canvas.window().raise_() self.canvas.activateWindow() self.canvas.setFocus() self.map_tool.featureIdentified.connect( self.map_tool_feature_identified) self.map_tool.deactivated.connect(self.map_tool_deactivated) def map_tool_feature_identified(self, feature): feature = QgsFeature(feature) self.feature_identified.emit(feature) self.unset_map_tool() self.set_feature(feature) def map_tool_deactivated(self): if self.window_widget is not None: self.window_widget.raise_() self.window_widget.activateWindow() def highlight_feature(self, canvas_extent=CanvasExtent.Fixed): if self.canvas is None or not self.feature.isValid(): return geom = self.feature.geometry() if geom is None: return if canvas_extent == CanvasExtent.Scale: feature_bounding_box = geom.boundingBox() feature_bounding_box = self.canvas.mapSettings( ).layerToMapCoordinates(self.layer, feature_bounding_box) extent = self.canvas.extent() if not extent.contains(feature_bounding_box): extent.combineExtentWith(feature_bounding_box) extent.scale(1.1) self.canvas.setExtent(extent) self.canvas.refresh() elif canvas_extent == CanvasExtent.Pan: centroid = geom.centroid() center = centroid.asPoint() center = self.canvas.mapSettings().layerToMapCoordinates( self.layer, center) self.canvas.zoomByFactor(1.0, center) # refresh is done in this method # highlight self.delete_highlight() self.highlight = QgsHighlight(self.canvas, geom, self.layer) settings = QSettings() color = QColor( settings.value("/Map/highlight/color", Qgis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int( settings.value("/Map/highlight/colorAlpha", Qgis.DEFAULT_HIGHLIGHT_COLOR.alpha())) buffer = 2 * float( settings.value("/Map/highlight/buffer", Qgis.DEFAULT_HIGHLIGHT_BUFFER_MM)) min_width = 2 * float( settings.value("/Map/highlight/min_width", Qgis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.highlight.setColor(color) # sets also fill with default alpha color.setAlpha(alpha) self.highlight.setFillColor(color) # sets fill with alpha self.highlight.setBuffer(buffer) self.highlight.setMinWidth(min_width) self.highlight.setWidth(4.0) self.highlight.show() self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.delete_highlight) self.timer.start(3000) def delete_highlight(self): if self.highlight is not None: self.highlight.hide() del self.highlight self.highlight = None def unset_map_tool(self): if self.canvas is not None and self.map_tool is not None: # this will call mapTool.deactivated self.canvas.unsetMapTool(self.map_tool) def highlight_action_triggered(self, action): self.highlight_feature_button.setDefaultAction(action) if action == self.highlight_feature_action: self.highlight_feature() elif action == self.scale_highlight_feature_action: self.highlight_feature(CanvasExtent.Scale) elif action == self.pan_highlight_feature_action: self.highlight_feature(CanvasExtent.Pan)
class TinTools: def __init__(self, iface): self.iface = iface self.toolbar = None self.highlight = None self.msgBar = iface.messageBar() self.plugin_dir = os.path.dirname(__file__) print('plugin init en ' + self.plugin_dir) def initGui(self): self.toolbar = self.iface.addToolBar(u'TIN Tools') self.toolbar.setObjectName(u'tintools') label = QLabel('Capa TIN:') self.toolbar.addWidget(label) self.tin_cb = QgsMapLayerComboBox() self.tin_cb.setMinimumWidth(200) self.tin_cb.setFilters(QgsMapLayerProxyModel.VectorLayer) self.tin_cb.layerChanged.connect(self.tinLayerChanged) self.toolbar.addWidget(self.tin_cb) # icon_path = self.plugin_dir +'/icon.png' # icon = QIcon(icon_path) #'\u0394 \u2B16 \u20E4 \u2350 \u21C8 \u2963 \u2B7F \u2b85 \u23C4 \u25e9' self.calc_plane_action = QAction('\u0394', self.iface.mainWindow()) self.calc_plane_action.triggered.connect(self.calcPlaneEquation) self.toolbar.addAction(self.calc_plane_action) self.calc_z = QAction('\u21C8', self.iface.mainWindow()) self.calc_z.triggered.connect(self.calcZ) self.toolbar.addAction(self.calc_z) self.adjust_to_tin = QAction('\u25e9', self.iface.mainWindow()) self.adjust_to_tin.triggered.connect(self.adjustToTin) self.toolbar.addAction(self.adjust_to_tin) self.tinLayerChanged(self.tin_cb.currentLayer()) def unload(self): self.planeCalc = None self.setHighlight(None, None) self.iface.removeToolBarIcon(self.calc_plane_action) self.iface.removeToolBarIcon(self.calc_z) self.iface.removeToolBarIcon(self.adjust_to_tin) del self.toolbar print('unload toolbar5') def msg(self, text): self.msgBar.pushWarning('TIN tool', text) def info(self, text): self.msgBar.pushInfo('TIN tool', text) def setHighlight(self, ly, vertex): if self.highlight: self.highlight.hide() self.highlight = None if vertex: color = QColor(255, 0, 100, 255) lv = vertex + [vertex[0]] g = QgsGeometry.fromPolyline(lv).convertToType(2) print(g.asWkt(3)) self.highlight = QgsHighlight(self.iface.mapCanvas(), g, ly) self.highlight.setColor(color) self.highlight.setWidth(5) color.setAlpha(50) self.highlight.setFillColor(color) self.highlight.show() def tinLayerChanged(self, layer): self.calc_plane_action.setEnabled(layer is not None) self.adjust_to_tin.setEnabled(layer is not None) self.calc_z.setEnabled(False) self.planeCalc = None self.setHighlight(None, None) def calcPlaneEquation(self): ly = self.tin_cb.currentLayer() self.setHighlight(None, None) if not ly: self.msg('No hay ninguna capa seleccionada') return ver = None feat = ly.selectedFeatures() geometry_type = ly.geometryType() if geometry_type == 0: # point if len(feat) == 3: ver = [f.geometry().vertexAt(0) for f in feat] else: self.msg('Hay que seleccionar 3 puntos') elif geometry_type == 2: #polygon if len(feat) == 1: geom = feat[0].geometry() ver = list(geom.vertices()) if len(ver) == 4: ver = ver[:3] else: self.msg('El polígono tiene que ser un triángulo') else: self.msg('Seleccionar solo un triángulo') else: self.msg('seleccionar tres puntos o un triángulo') if ver: self.planeCalc = PlaneCalc(ver) self.info( 'Selecciona los elementos de una capa para calcular su Z en el plano' ) self.calc_z.setEnabled(True) self.setHighlight(ly, ver) else: self.planeCalc = None self.calc_z.setEnabled(False) def calcZ(self): layer = self.iface.activeLayer() if not layer: QMessageBox.warning(None, 'TIN Tools', 'Selecciona elemenos de una capa') return if not layer.isEditable(): QMessageBox.information(None, 'TIN cal', 'La capa no está en modo edición') return for f in layer.getSelectedFeatures(): geom = f.geometry() n = 0 v = geom.vertexAt(0) while (v != QgsPoint(0, 0)): z = self.planeCalc.cal_z(v.x(), v.y()) v.setZ(z) geom.moveVertex(v, n) n += 1 v = geom.vertexAt(n) layer.changeGeometry(f.id(), geom) def adjustToTin(self): print('calc tin')
class SpatialPreview(QTabWidget, Ui_frmPropertyPreview): """ Widget for previewing spatial unit on either local map or web source. """ def __init__(self, parent=None, iface=None): QTabWidget.__init__(self, parent) self.setupUi(self) self._notif_bar = None self._ol_loaded = False self._overlay_layer = None self.sel_highlight = None self.memory_layer = None self._db_session = STDMDb.instance().session self.set_iface(iface) #Web config self._web_spatial_loader = WebSpatialLoader(self.spatial_web_view, self) #Connect signals self._web_spatial_loader.loadError.connect(self.on_spatial_browser_error) self._web_spatial_loader.loadProgress.connect(self.on_spatial_browser_loading) self._web_spatial_loader.loadFinished.connect(self.on_spatial_browser_finished) self._web_spatial_loader.zoomChanged.connect(self.on_map_zoom_level_changed) self.rbGMaps.toggled.connect(self.on_load_GMaps) self.rbOSM.toggled.connect(self.on_load_OSM) self.zoomSlider.sliderReleased.connect(self.on_zoom_changed) self.btnResetMap.clicked.connect(self.on_reset_web_map) self.btnSync.clicked.connect(self.on_sync_extents) QgsMapLayerRegistry.instance().layersWillBeRemoved.connect(self._on_overlay_to_be_removed) def set_iface(self, iface): self._iface = iface self.local_map.set_iface(iface) def set_notification_bar(self, notif_bar): """ Set notification widget. :param notif_bar: Notification widget. """ self._notif_bar = notif_bar def notification_bar(self): """ :return: Currently configured notification bar. """ return self._notif_bar def _insert_notification(self, msg, level, clear_first = True): if self._notif_bar is None: return if clear_first: self._notif_bar.clear() self._notif_bar.insertNotification(msg, level) def iface(self): return self._iface def _setDefaults(self): """ Set default settings """ self.set_canvas_background_color(self.canvasBgColor) def set_canvas_background_color(self,color): """ Set the background color of the map canvas """ self.localMap.setCanvasColor(color) self.canvasBgColor = color def refresh_canvas_layers(self, process_events=False): """ Reload map layers in the viewer canvas. """ self.local_map.refresh_layers() def load_web_map(self): """ Loads the web map into the view using canvas extents if there are existing layers in the map canvas. """ if not self._ol_loaded: self._web_spatial_loader.load() def _create_vector_layer(self, geom_type, prj_code): """ Creates/resets the internal vector layer that will be used to draw the spatial unit overlays. :param geom_type: Geometry type :type geom_type: str :param prj_code: EPSG code :type prj_code: int """ self._overlay_layer = QgsVectorLayer( u"{0}?crs=epsg:{1!s}&field=lbname:string(20)&index=yes".format(geom_type, prj_code), "view_str_spatial_unit", "memory") def draw_spatial_unit(self, spatial_unit, model): """ Draw geometry of the given model in the respective local and web views. :param model: Source model whose geometry will be drawn. :type model: object :param clear_existing: Clears any existing features prior to adding the new features. :type clear_existing: bool """ if model is None: msg = QApplication.translate("SpatialPreview", "Data model is empty, the spatial " "unit cannot be rendered.") QMessageBox.critical(self, QApplication.translate( "SpatialPreview", "Spatial Unit Preview"), msg) return table_name = spatial_unit.name if not pg_table_exists(table_name): msg = QApplication.translate("SpatialPreview", "The spatial unit data source could " "not be retrieved, the feature cannot " "be rendered.") QMessageBox.critical( self, QApplication.translate( "SpatialPreview", "Spatial Unit Preview"), msg ) return sp_unit_manager = SpatialUnitManagerDockWidget(self.iface()) spatial_cols = sp_unit_manager.geom_columns(spatial_unit) geom, geom_col = None, "" sc_obj = None for sc in spatial_cols: db_geom = getattr(model, sc.name) #Use the first non-empty geometry # value in the collection if not db_geom is None: sc_obj = sc geom_col = sc.name geom = db_geom QApplication.processEvents() lyr = sp_unit_manager.geom_col_layer_name( table_name, sc_obj ) sp_unit_manager.add_layer_by_name(lyr) if geom is not None: self.highlight_spatial_unit( spatial_unit, geom, self.local_map.canvas ) self._web_spatial_loader.add_overlay( model, geom_col ) def clear_sel_highlight(self): """ Removes sel_highlight from the canvas. :return: """ if self.sel_highlight is not None: self.sel_highlight = None def get_layer_source(self, layer): """ Get the layer table name if the source is from the database. :param layer: The layer for which the source is checked :type QGIS vectorlayer :return: String or None """ source = layer.source() vals = dict(re.findall('(\S+)="?(.*?)"? ', source)) try: table = vals['table'].split('.') table_name = table[1].strip('"') return table_name except KeyError: return None def spatial_unit_layer(self, spatial_unit, active_layer): """ Check whether the layer is parcel layer or not. :param active_layer: The layer to be checked :type QGIS vectorlayer :return: Boolean """ if self.active_layer_check(): layers = self.iface().legendInterface().layers() for layer in layers: layer_source = self.get_layer_source(layer) if layer_source == spatial_unit.name: self.iface().setActiveLayer(layer) return True not_sp_msg = QApplication.translate( 'SpatialPreview', 'You have selected a non-spatial_unit layer. ' 'Please select a spatial unit layer to preview.' ) QMessageBox.information( self._iface.mainWindow(), "Error", not_sp_msg ) def active_layer_check(self): """ Check if there is active layer and if not, displays a message box to select a parcel layer. :return: """ active_layer = self._iface.activeLayer() if active_layer is None: no_layer_msg = QApplication.translate( 'SpatialPreview', 'Please add a spatial unit layer ' 'to preview the spatial unit.' ) QMessageBox.critical( self._iface.mainWindow(), "Error", no_layer_msg ) return False else: return True def _add_geom_to_map(self, geom): if self._overlay_layer is None: return geom_func = geom.ST_AsText() geom_wkt = self._db_session.scalar(geom_func) dp = self._overlay_layer.dataProvider() feat = QgsFeature() qgis_geom = QgsGeometry.fromWkt(geom_wkt) feat.setGeometry(g) dp.addFeatures([feat]) self._overlay_layer.updateExtents() return qgis_geom.boundingBox() def highlight_spatial_unit( self, spatial_unit, geom, map_canvas ): layer = self._iface.activeLayer() map_canvas.setExtent(layer.extent()) map_canvas.refresh() if self.spatial_unit_layer(spatial_unit, layer): self.clear_sel_highlight() qgis_geom = qgsgeometry_from_wkbelement(geom) self.sel_highlight = QgsHighlight( map_canvas, qgis_geom, layer ) rgba = selection_color() self.sel_highlight.setFillColor(rgba) self.sel_highlight.setWidth(3) self.sel_highlight.show() extent = qgis_geom.boundingBox() extent.scale(1.5) map_canvas.setExtent(extent) map_canvas.refresh() else: return def remove_preview_layer(self, layer, name): """ Removes the preview layer from legend. :param layer: The preview polygon layer to be removed. :param name: The name of the layer to be removed. :return: None """ if layer is not None: for lyr in QgsMapLayerRegistry.instance().mapLayers().values(): if lyr.name() == name: id = lyr.id() QgsMapLayerRegistry.instance().removeMapLayer(id) def delete_local_features(self, feature_ids=[]): """ Removes features in the local map overlay. """ del_status = False if not self._overlay_layer is None: if len(feature_ids) == 0: feature_ids = self._overlay_layer.allFeatureIds() del_status = self._overlay_layer.dataProvider().deleteFeatures(feature_ids) return del_status def remove_layer(self): """ Removes both the local and web layers. """ if not self._overlay_layer is None: QgsProject.instance().layerTreeRoot().removeLayer(self._overlay_layer) #Clear web overlays self._web_spatial_loader.removeOverlay() self._overlay_layer = None def _on_overlay_to_be_removed(self, layers_ids): """ Resets the local layer variable and removes the web overlay. """ if not self._overlay_layer is None: if self._overlay_layer.id() in layers_ids: self.remove_layer() def on_spatial_browser_error(self, err): """ Slot raised when an error occurs when loading items in the property browser """ self._insert_notification(err, ERROR) def on_spatial_browser_loading(self, progress): """ Slot raised when the property browser is loading. Displays the progress of the page loading as a percentage. """ if progress <= 0 or progress >= 100: self.lblInfo.setText("") self.lblInfo.setVisible(False) else: self.lblInfo.setVisible(True) self.lblInfo.setText("Loading...%d%%)"%(progress)) def on_spatial_browser_finished(self, status): """ Slot raised when the property browser finishes loading the content """ if status: if len(self.local_map.canvas_layers()) > 0:# and not self._ol_loaded: self.on_sync_extents() self._ol_loaded = True #self._overlay_spatial_unit() else: msg = QApplication.translate("SpatialPreview", "Error: Spatial unit cannot be loaded.") self._insert_notification(msg, ERROR) def on_zoom_changed(self): """ Slot raised when the zoom value in the slider changes. This is only raised once the user releases the slider with the mouse. """ zoom = self.zoomSlider.value() self._web_spatial_loader.zoom_to_level(zoom) def on_load_GMaps(self, state): """ Slot raised when a user clicks to set Google Maps Satellite as the base layer """ if state: self._web_spatial_loader.setBaseLayer(GMAP_SATELLITE) def on_load_OSM(self, state): """ Slot raised when a user clicks to set OSM as the base layer """ if state: self._web_spatial_loader.setBaseLayer(OSM) def on_map_zoom_level_changed(self, level): """ Slot which is raised when the zoom level of the map changes. """ self.zoomSlider.setValue(level) def on_reset_web_map(self): """ Slot raised when the user clicks to reset the property location in the map. """ self._web_spatial_loader.zoom_to_extents() def on_sync_extents(self): """ Slot raised to synchronize the webview extents with those of the local map canvas. """ if len(self.local_map.canvas_layers()) > 0:# and self._ol_loaded: curr_extent = self.map_extents() self._web_spatial_loader.zoom_to_map_extents(curr_extent) def map_extents(self): """ :returns: Current extents of the local map. :rtype: QgsRectangle """ return self.local_map.extent() def canvas_zoom_to_extent(self, extent): self.local_map.canvas.setExtent(extent)
pr.addAttributes([ QgsField("name", QVariant.String), QgsField("age", QVariant.Int), QgsField("size", QVariant.Double) ]) vl.updateFields() # tell the vector layer to fetch changes from the provider infos = [[10, 10, "John", 24, 1.73], [40, -60, "Paul", 29, 1.86], [60, 5, "George", 34, 1.69], [0, 45, "Ringo", 73, 1.75]] # add features for i in infos: fet = QgsFeature() fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(i[0], i[1]))) fet.setAttributes(i[2:5]) pr.addFeatures([fet]) # update layer's extent when new features have been added # because change of extent in provider is not propagated to the layer vl.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(vl) highlight = QgsHighlight(iface.mapCanvas(), QgsGeometry.fromPoint(QgsPoint(0, 47)), vl) highlight.setBuffer(1.5) highlight.setColor(QColor('black')) highlight.setFillColor(QColor('blue')) highlight.setWidth(0.5) iface.mapCanvas().refresh()
class SpatialPreview(QTabWidget, Ui_frmPropertyPreview): """ Widget for previewing spatial unit on either local map or web source. """ def __init__(self, parent=None, iface=None): QTabWidget.__init__(self, parent) self.setupUi(self) self._notif_bar = None self._ol_loaded = False self._overlay_layer = None self.sel_highlight = None self.memory_layer = None self._db_session = STDMDb.instance().session self.set_iface(iface) #Web config self._web_spatial_loader = WebSpatialLoader(self.spatial_web_view, self) #Connect signals self._web_spatial_loader.loadError.connect( self.on_spatial_browser_error) self._web_spatial_loader.loadProgress.connect( self.on_spatial_browser_loading) self._web_spatial_loader.loadFinished.connect( self.on_spatial_browser_finished) self._web_spatial_loader.zoomChanged.connect( self.on_map_zoom_level_changed) self.rbGMaps.toggled.connect(self.on_load_GMaps) self.rbOSM.toggled.connect(self.on_load_OSM) self.zoomSlider.sliderReleased.connect(self.on_zoom_changed) self.btnResetMap.clicked.connect(self.on_reset_web_map) self.btnSync.clicked.connect(self.on_sync_extents) QgsMapLayerRegistry.instance().layersWillBeRemoved.connect( self._on_overlay_to_be_removed) def set_iface(self, iface): self._iface = iface self.local_map.set_iface(iface) def set_notification_bar(self, notif_bar): """ Set notification widget. :param notif_bar: Notification widget. """ self._notif_bar = notif_bar def notification_bar(self): """ :return: Currently configured notification bar. """ return self._notif_bar def _insert_notification(self, msg, level, clear_first=True): if self._notif_bar is None: return if clear_first: self._notif_bar.clear() self._notif_bar.insertNotification(msg, level) def iface(self): return self._iface def _setDefaults(self): """ Set default settings """ self.set_canvas_background_color(self.canvasBgColor) def set_canvas_background_color(self, color): """ Set the background color of the map canvas """ self.localMap.setCanvasColor(color) self.canvasBgColor = color def refresh_canvas_layers(self, process_events=False): """ Reload map layers in the viewer canvas. """ self.local_map.refresh_layers() def load_web_map(self): """ Loads the web map into the view using canvas extents if there are existing layers in the map canvas. """ if not self._ol_loaded: self._web_spatial_loader.load() def _create_vector_layer(self, geom_type, prj_code): """ Creates/resets the internal vector layer that will be used to draw the spatial unit overlays. :param geom_type: Geometry type :type geom_type: str :param prj_code: EPSG code :type prj_code: int """ self._overlay_layer = QgsVectorLayer( u"{0}?crs=epsg:{1!s}&field=lbname:string(20)&index=yes".format( geom_type, prj_code), "view_str_spatial_unit", "memory") def draw_spatial_unit(self, spatial_unit, model): """ Draw geometry of the given model in the respective local and web views. :param model: Source model whose geometry will be drawn. :type model: object :param clear_existing: Clears any existing features prior to adding the new features. :type clear_existing: bool """ if model is None: msg = QApplication.translate( "SpatialPreview", "Data model is empty, the spatial " "unit cannot be rendered.") QMessageBox.critical( self, QApplication.translate("SpatialPreview", "Spatial Unit Preview"), msg) return table_name = spatial_unit.name if not pg_table_exists(table_name): msg = QApplication.translate( "SpatialPreview", "The spatial unit data source could " "not be retrieved, the feature cannot " "be rendered.") QMessageBox.critical( self, QApplication.translate("SpatialPreview", "Spatial Unit Preview"), msg) return sp_unit_manager = SpatialUnitManagerDockWidget(self.iface()) spatial_cols = sp_unit_manager.geom_columns(spatial_unit) geom, geom_col = None, "" sc_obj = None for sc in spatial_cols: db_geom = getattr(model, sc.name) #Use the first non-empty geometry # value in the collection if not db_geom is None: sc_obj = sc geom_col = sc.name geom = db_geom QApplication.processEvents() lyr = sp_unit_manager.geom_col_layer_name(table_name, sc_obj) sp_unit_manager.add_layer_by_name(lyr) if geom is not None: self.highlight_spatial_unit(spatial_unit, geom, self.local_map.canvas) self._web_spatial_loader.add_overlay(model, geom_col) def clear_sel_highlight(self): """ Removes sel_highlight from the canvas. :return: """ if self.sel_highlight is not None: self.sel_highlight = None def get_layer_source(self, layer): """ Get the layer table name if the source is from the database. :param layer: The layer for which the source is checked :type QGIS vectorlayer :return: String or None """ source = layer.source() vals = dict(re.findall('(\S+)="?(.*?)"? ', source)) try: table = vals['table'].split('.') table_name = table[1].strip('"') return table_name except KeyError: return None def spatial_unit_layer(self, spatial_unit, active_layer): """ Check whether the layer is parcel layer or not. :param active_layer: The layer to be checked :type QGIS vectorlayer :return: Boolean """ if self.active_layer_check(): layers = self.iface().legendInterface().layers() for layer in layers: layer_source = self.get_layer_source(layer) if layer_source == spatial_unit.name: self.iface().setActiveLayer(layer) return True not_sp_msg = QApplication.translate( 'SpatialPreview', 'You have selected a non-spatial_unit layer. ' 'Please select a spatial unit layer to preview.') QMessageBox.information(self._iface.mainWindow(), "Error", not_sp_msg) def active_layer_check(self): """ Check if there is active layer and if not, displays a message box to select a parcel layer. :return: """ active_layer = self._iface.activeLayer() if active_layer is None: no_layer_msg = QApplication.translate( 'SpatialPreview', 'Please add a spatial unit layer ' 'to preview the spatial unit.') QMessageBox.critical(self._iface.mainWindow(), "Error", no_layer_msg) return False else: return True def _add_geom_to_map(self, geom): if self._overlay_layer is None: return geom_func = geom.ST_AsText() geom_wkt = self._db_session.scalar(geom_func) dp = self._overlay_layer.dataProvider() feat = QgsFeature() qgis_geom = QgsGeometry.fromWkt(geom_wkt) feat.setGeometry(g) dp.addFeatures([feat]) self._overlay_layer.updateExtents() return qgis_geom.boundingBox() def highlight_spatial_unit(self, spatial_unit, geom, map_canvas): layer = self._iface.activeLayer() map_canvas.setExtent(layer.extent()) map_canvas.refresh() if self.spatial_unit_layer(spatial_unit, layer): self.clear_sel_highlight() qgis_geom = qgsgeometry_from_wkbelement(geom) self.sel_highlight = QgsHighlight(map_canvas, qgis_geom, layer) rgba = selection_color() self.sel_highlight.setFillColor(rgba) self.sel_highlight.setWidth(3) self.sel_highlight.show() extent = qgis_geom.boundingBox() extent.scale(1.5) map_canvas.setExtent(extent) map_canvas.refresh() else: return def remove_preview_layer(self, layer, name): """ Removes the preview layer from legend. :param layer: The preview polygon layer to be removed. :param name: The name of the layer to be removed. :return: None """ if layer is not None: for lyr in QgsMapLayerRegistry.instance().mapLayers().values(): if lyr.name() == name: id = lyr.id() QgsMapLayerRegistry.instance().removeMapLayer(id) def delete_local_features(self, feature_ids=[]): """ Removes features in the local map overlay. """ del_status = False if not self._overlay_layer is None: if len(feature_ids) == 0: feature_ids = self._overlay_layer.allFeatureIds() del_status = self._overlay_layer.dataProvider().deleteFeatures( feature_ids) return del_status def remove_layer(self): """ Removes both the local and web layers. """ if not self._overlay_layer is None: QgsProject.instance().layerTreeRoot().removeLayer( self._overlay_layer) #Clear web overlays self._web_spatial_loader.removeOverlay() self._overlay_layer = None def _on_overlay_to_be_removed(self, layers_ids): """ Resets the local layer variable and removes the web overlay. """ if not self._overlay_layer is None: if self._overlay_layer.id() in layers_ids: self.remove_layer() def on_spatial_browser_error(self, err): """ Slot raised when an error occurs when loading items in the property browser """ self._insert_notification(err, ERROR) def on_spatial_browser_loading(self, progress): """ Slot raised when the property browser is loading. Displays the progress of the page loading as a percentage. """ if progress <= 0 or progress >= 100: self.lblInfo.setText("") self.lblInfo.setVisible(False) else: self.lblInfo.setVisible(True) self.lblInfo.setText("Loading...%d%%)" % (progress)) def on_spatial_browser_finished(self, status): """ Slot raised when the property browser finishes loading the content """ if status: if len(self.local_map.canvas_layers() ) > 0: # and not self._ol_loaded: self.on_sync_extents() self._ol_loaded = True #self._overlay_spatial_unit() else: msg = QApplication.translate( "SpatialPreview", "Error: Spatial unit cannot be loaded.") self._insert_notification(msg, ERROR) def on_zoom_changed(self): """ Slot raised when the zoom value in the slider changes. This is only raised once the user releases the slider with the mouse. """ zoom = self.zoomSlider.value() self._web_spatial_loader.zoom_to_level(zoom) def on_load_GMaps(self, state): """ Slot raised when a user clicks to set Google Maps Satellite as the base layer """ if state: self._web_spatial_loader.setBaseLayer(GMAP_SATELLITE) def on_load_OSM(self, state): """ Slot raised when a user clicks to set OSM as the base layer """ if state: self._web_spatial_loader.setBaseLayer(OSM) def on_map_zoom_level_changed(self, level): """ Slot which is raised when the zoom level of the map changes. """ self.zoomSlider.setValue(level) def on_reset_web_map(self): """ Slot raised when the user clicks to reset the property location in the map. """ self._web_spatial_loader.zoom_to_extents() def on_sync_extents(self): """ Slot raised to synchronize the webview extents with those of the local map canvas. """ if len(self.local_map.canvas_layers()) > 0: # and self._ol_loaded: curr_extent = self.map_extents() self._web_spatial_loader.zoom_to_map_extents(curr_extent) def map_extents(self): """ :returns: Current extents of the local map. :rtype: QgsRectangle """ return self.local_map.extent() def canvas_zoom_to_extent(self, extent): self.local_map.canvas.setExtent(extent)
class Start: def __init__(self, iface): self.name = "OPEN_FTTH" self.iface = iface self.autosave_enabled = False self.route_segment_layer = None self.route_node_layer = None self.websocket = BridgeWebsocket(self.iface) self.identifyHighlight = None self.last_identified_feature_mrid = None self.last_identified_feature_type = None self.event_handler = EventHandler(self.iface, self.websocket.websocket, self) self.websocket.messageReceived.connect(self.event_handler.handle) self.identifyNetworkElementHandler = IdentifyNetworkElementHandler( self.websocket) self.retrieve_selected_handler = RetrieveSelectedHandler( self.iface, self.websocket) self.application_settings = ApplicationSettings() self.layers_loaded = False def initGui(self): self.setupActions() self.iface.layerTreeView().currentLayerChanged.connect( self.layersLoaded) self.iface.layerTreeView().currentLayerChanged.connect( self.layerSelectionChange) def setupActions(self): self.actions = [] icon_auto_save = ":/plugins/open_ftth/auto_save.svg" self.autosave_action = QAction(QtGui.QIcon(icon_auto_save), "Autosave", self.iface.mainWindow()) self.autosave_action.setCheckable(True) self.autosave_action.triggered.connect(self.setupAutoSave) auto_identify = ":/plugins/open_ftth/auto_identify.svg" self.select_action = QAction(QtGui.QIcon(auto_identify), "Select", self.iface.mainWindow()) self.select_action.setCheckable(True) self.select_action.triggered.connect(self.setupSelectTool) web_browser = ":/plugins/open_ftth/browser_icon.svg" self.web_browser_action = QAction(QtGui.QIcon(web_browser), "Web-browser", self.iface.mainWindow()) self.web_browser_action.setCheckable(False) self.web_browser_action.triggered.connect(self.connectWebBrowser) paste_geometry = ":/plugins/open_ftth/paste_geometry.svg" self.paste_geometry_action = QAction(QtGui.QIcon(paste_geometry), "Paste geometry", self.iface.mainWindow()) self.paste_geometry_action.setCheckable(False) self.paste_geometry_action.triggered.connect(self.pasteGeometry) self.iface.addPluginToMenu("&OPEN FTTH", self.select_action) self.iface.addToolBarIcon(self.autosave_action) self.iface.addToolBarIcon(self.select_action) self.iface.addToolBarIcon(self.web_browser_action) self.iface.addToolBarIcon(self.paste_geometry_action) self.identify_tool = IdentifySelect(self.iface.mapCanvas()) self.identify_tool.identified.connect(self.onIdentified) self.identify_tool.identifiedNone.connect(self.onIdentifiedNone) self.buildActionListIdentifyTool() def buildActionListIdentifyTool(self): actionList = self.iface.mapNavToolToolBar().actions() # Add actions from QGIS attributes toolbar (handling QWidgetActions) tmpActionList = self.iface.attributesToolBar().actions() for action in tmpActionList: if isinstance(action, QWidgetAction): actionList.extend(action.defaultWidget().actions()) else: actionList.append(action) tmpActionList = self.iface.digitizeToolBar().actions() for action in tmpActionList: if isinstance(action, QWidgetAction): actionList.extend(action.defaultWidget().actions()) else: actionList.append(action) tmpActionList = self.iface.selectionToolBar().actions() for action in tmpActionList: if isinstance(action, QWidgetAction): actionList.extend(action.defaultWidget().actions()) else: actionList.append(action) # Build a group with actions from actionList and add your own action group = QActionGroup(self.iface.mainWindow()) group.setExclusive(True) for action in actionList: group.addAction(action) group.addAction(self.select_action) def unload(self): for action in self.actions: self.iface.removeToolBarIcon(action) self.autosave_action.setChecked(False) self.select_action.setChecked(False) self.autosave_enabled = False self.select_tool_enabled = False try: self.disconnectSelectedFeatures() self.disconnectSelectTool() self.websocket.close() self.route_segment_layer.beforeCommitChanges.disconnect( self.preCommitDeleteHandlerRouteSegment) self.route_node_layer.beforeCommitChanges.disconnect( self.preCommitDeleteHandlerRouteNode) self.route_node_layer.featuresDeleted.disconnect( self.checkFeaturesDeleted) self.route_segment_layer.featuresDeleted.disconnect( self.checkFeaturesDeleted) except Exception: pass def setupSelectTool(self): self.iface.mapCanvas().setMapTool(self.identify_tool) def sendSelectedFeatures(self): self.getSelectedFeaturesHandler.handle() def setupAutoSave(self): if self.autosave_enabled is False: self.connectAutosave() self.saveActiveLayerEdits() else: self.disconnectAutosave() def layersLoaded(self): if not self.hasCorrectLayers(): self.onIdentifiedNone() return if self.layers_loaded is False: self.websocket.start() self.layers_loaded = True self.route_segment_layer = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_segment_name())[0] self.route_node_layer = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_node_name())[0] try: self.route_node_layer.featuresDeleted.connect( self.checkFeaturesDeleted) self.route_segment_layer.featuresDeleted.connect( self.checkFeaturesDeleted) except TypeError: pass try: self.route_segment_layer.beforeCommitChanges.connect( self.preCommitDeleteHandlerRouteSegment) self.route_node_layer.beforeCommitChanges.connect( self.preCommitDeleteHandlerRouteNode) except TypeError: pass def layerSelectionChange(self): if not self.hasCorrectLayers(): self.onIdentifiedNone() return self.route_segment_layer = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_segment_name())[0] try: self.route_segment_layer.selectionChanged.disconnect( self.onSelectedSegment) except TypeError: pass self.route_segment_layer.selectionChanged.connect( self.onSelectedSegment) def hasCorrectLayers(self): route_segment_layers = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_segment_name()) route_node_layers = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_node_name()) return len(route_segment_layers) > 0 and len(route_node_layers) > 0 def connectAutosave(self): # We do this to avoid plugin crash in case that connects come in an invalid state. try: self.route_segment_layer = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_segment_name())[0] self.route_segment_layer.layerModified.connect( self.saveActiveLayerEdits) except TypeError: pass # We do this to avoid plugin crash in case that connects come in an invalid state. try: self.route_node_layer = QgsProject.instance().mapLayersByName( ApplicationSettings().get_layers_route_node_name())[0] self.route_node_layer.layerModified.connect( self.saveActiveLayerEdits) except TypeError: pass self.autosave_enabled = True def disconnectAutosave(self): # We do this to avoid plugin crash in case that connects come in an invalid state. try: self.route_segment_layer.layerModified.disconnect( self.saveActiveLayerEdits) except TypeError: pass # We do this to avoid plugin crash in case that connects come in an invalid state. try: self.route_node_layer.layerModified.disconnect( self.saveActiveLayerEdits) except TypeError: pass self.autosave_action.setChecked(False) self.autosave_enabled = False def connectWebBrowser(self): webbrowser.open(self.application_settings.get_website_url(), new=2) def saveActiveLayerEdits(self): self.iface.actionSaveActiveLayerEdits().trigger() def onSelectedSegment(self): message = type( 'Expando', (object, ), {'username': self.application_settings.get_user_name_prefix()})() self.retrieve_selected_handler.handle(message) def checkFeaturesDeleted(self, fids): if self.last_identified_feature_mrid is None: return features = None filterExpression = f'"mrid" = \'{self.last_identified_feature_mrid}\'' if self.last_identified_feature_type == self.application_settings.get_types_route_node( ): features = self.route_node_layer.getFeatures( QgsFeatureRequest().setFilterExpression(filterExpression)) elif self.last_identified_feature_type == self.application_settings.get_types_route_segment( ): features = self.route_segment_layer.getFeatures( QgsFeatureRequest().setFilterExpression(filterExpression)) else: return # hack because the deleted signal is triggered on both create and delete stillExists = False for feature in features: stillExists = True if not stillExists: self.onIdentifiedNone() self.identifyNetworkElementHandler.handle(None, None) # end of hack def onIdentified(self, selected_layer, selected_feature): selected_type = "" if self.application_settings.get_layers_route_node_name( ) == selected_layer.sourceName(): selected_type = self.application_settings.get_types_route_node() elif self.application_settings.get_layers_route_segment_name( ) == selected_layer.sourceName(): selected_type = self.application_settings.get_types_route_segment() else: self.identifyHighlight.hide() return if self.identifyHighlight is not None: self.identifyHighlight.hide() color = QColor(0, 255, 0) self.identifyHighlight = QgsHighlight(self.iface.mapCanvas(), selected_feature.geometry(), selected_layer) self.identifyHighlight.setWidth(3) self.identifyHighlight.setColor(color) self.identifyHighlight.show() mrid = selected_feature.attribute("mrid") self.last_identified_feature_mrid = mrid self.last_identified_feature_type = selected_type if selected_type != "": self.identifyNetworkElementHandler.handle(mrid, selected_type) def onIdentifiedNone(self): if self.identifyHighlight is not None: self.last_identified_feature_mrid = None self.last_identified_feature_type = None self.identifyHighlight.hide() def preCommitDeleteHandlerRouteSegment(self): self.undoDeleteSetMarkedToBeDeleted( ApplicationSettings().get_layers_route_segment_name()) def preCommitDeleteHandlerRouteNode(self): self.undoDeleteSetMarkedToBeDeleted( ApplicationSettings().get_layers_route_node_name()) def undoDeleteSetMarkedToBeDeleted(self, layerName): layers = QgsProject.instance().mapLayersByName(layerName) if len(layers) != 1: return layer = layers[0] deleted_features_ids = layer.editBuffer().deletedFeatureIds() for feature_id in deleted_features_ids: QgsVectorLayerUndoCommandDeleteFeature(layer.editBuffer(), feature_id).undo() marked_to_be_deleted_idx = layer.fields().indexOf( 'marked_to_be_deleted') user_name_idx = layer.fields().indexOf('user_name') user_name = self.application_settings.get_user_name() for feature in layer.dataProvider().getFeatures( QgsFeatureRequest().setFilterFids(deleted_features_ids)): layer.changeAttributeValue(feature.id(), marked_to_be_deleted_idx, True) layer.changeAttributeValue(feature.id(), user_name_idx, user_name) def pasteGeometry(self): layer = self.iface.activeLayer() route_segment_layer_name = self.application_settings.get_layers_route_segment_name( ) if layer.sourceName() != route_segment_layer_name: self.showBarMessage( "You can only paste a geometry when layer %s is selected." % route_segment_layer_name, Qgis.Warning) return if not layer.isEditable(): self.showBarMessage( "You need to be in edit mode to paste the geometry.", Qgis.Warning) return geoms = self.tryGetFeaturesGeomsFromClipBoard() if len(geoms) > 1: self.showBarMessage( "Can't paste geometry multiple features in clipboard.", Qgis.Warning) return if len(geoms) == 0: self.showBarMessage( "Can't paste geometry. No features in clipboard.", Qgis.Warning) return selected_features_iter = layer.getSelectedFeatures() selected_features = [] for selected_feature in selected_features_iter: selected_features.append(selected_feature) if len(selected_features) == 0: self.showBarMessage("Can't paste. No target feature to paste to.", Qgis.Warning) return # Paste is the comment we want to paste to paste_feature = selected_features[0] paste_geom = paste_feature.geometry() # Copy is the geom from the clipboard that we want "paste" to resemble. copy_geom = geoms[0] if paste_geom.type() != copy_geom.type(): self.showBarMessage( "Not the same geometry type. From %s to %s" % (copy_geom.type(), paste_geom.type()), Qgis.Warning) return paste_geom_start = paste_geom.asPolyline()[0] paste_geom_end = paste_geom.asPolyline()[len(paste_geom.asPolyline()) - 1] copy_geom_start = copy_geom.asPolyline()[0] copy_geom_end = copy_geom.asPolyline()[len(copy_geom.asPolyline()) - 1] start_to_start_distance = paste_geom_start.distance(copy_geom_start) start_to_end_distance = paste_geom_start.distance(copy_geom_end) new_copy_polyline = copy_geom.asPolyline() if start_to_start_distance > start_to_end_distance: QgsMessageLog.logMessage( "The geometries are flipped, we reverse them for the copy.", self.name, Qgis.Info) new_copy_polyline.reverse() # Its important that we do this after, in case that the geometry is reversed. new_copy_geom_start = new_copy_polyline[0] new_copy_geom_end = new_copy_polyline[len(new_copy_polyline) - 1] new_start_to_start_distance = paste_geom_start.distance( new_copy_geom_start) if self.application_settings.get_tolerance( ) < new_start_to_start_distance: self.showBarMessage( "Start point distance is bigger than tolerance.", Qgis.Critical) return new_start_to_end_distance = paste_geom_end.distance(new_copy_geom_end) if self.application_settings.get_tolerance( ) < new_start_to_end_distance: self.showBarMessage( "End points distance is bigger than tolerance.", Qgis.Critical) return # We update the geometry. result = layer.changeGeometry( paste_feature.id(), QgsGeometry.fromPolylineXY(new_copy_polyline)) # We change the mapping method to LandSurveying to identify that the polyline now matches the landsurvey polyline. mapping_method_idx = layer.fields().indexOf('mapping_method') layer.changeAttributeValue(paste_feature.id(), mapping_method_idx, "LandSurveying") if not result: self.showBarMessage("Can't paste geometry, something went wrong.", Qgis.Critical) return self.iface.mapCanvas().refresh() def tryGetFeaturesGeomsFromClipBoard(self): cb = QApplication.clipboard() clipboard_text = cb.text() if sys.version_info[0] == 2: clipboard_text = clipboard_text.encode('utf-8') reader = csv.DictReader(StringIO(clipboard_text), delimiter='\t') geoms = [] for row in reader: wkt_geom = row.get('wkt_geom') geom = QgsGeometry.fromWkt(wkt_geom) if not geom: self.showBarMessage( 'Can\'t create geometry from wkt: %s' % wkt_geom, Qgis.Critical) return [] geoms.append(geom) return geoms def showBarMessage(self, message, level=Qgis.Info, duration=-1): self.iface.messageBar().pushMessage("Error", message, level=level, duration=duration)
class FeatureSelectorWidget(QWidget): featureIdentified = pyqtSignal(QgsFeature) def __init__(self, parent): QWidget.__init__(self, parent) editLayout = QHBoxLayout() editLayout.setContentsMargins(0, 0, 0, 0) editLayout.setSpacing(2) self.setLayout(editLayout) self.lineEdit = QLineEdit(self) self.lineEdit.setReadOnly(True) editLayout.addWidget(self.lineEdit) self.highlightFeatureButton = QToolButton(self) self.highlightFeatureButton.setPopupMode(QToolButton.MenuButtonPopup) self.highlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionHighlightFeature.svg"), "Highlight feature", self) self.scaleHighlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionScaleHighlightFeature.svg"), "Scale and highlight feature", self) self.panHighlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionPanHighlightFeature.svg"), "Pan and highlight feature", self) self.highlightFeatureButton.addAction(self.highlightFeatureAction) self.highlightFeatureButton.addAction(self.scaleHighlightFeatureAction) self.highlightFeatureButton.addAction(self.panHighlightFeatureAction) self.highlightFeatureButton.setDefaultAction(self.highlightFeatureAction) editLayout.addWidget(self.highlightFeatureButton) self.mapIdentificationButton = QToolButton(self) self.mapIdentificationButton.setIcon(QgsApplication.getThemeIcon("/mActionMapIdentification.svg")) self.mapIdentificationButton.setText("Select on map") self.mapIdentificationButton.setCheckable(True) editLayout.addWidget(self.mapIdentificationButton) self.mapIdentificationButton.clicked.connect(self.mapIdentification) self.highlightFeatureButton.triggered.connect(self.highlightActionTriggered) self.layer = None self.mapTool = None self.canvas = None self.windowWidget = None self.highlight = None self.feature = QgsFeature() def setCanvas(self, mapCanvas): self.mapTool = QgsMapToolIdentifyFeature(mapCanvas) self.mapTool.setButton(self.mapIdentificationButton) self.canvas = mapCanvas def setLayer(self, layer): self.layer = layer def setFeature(self, feature, canvasExtent = CanvasExtent.Fixed): self.lineEdit.clear() self.feature = feature if not self.feature.isValid() or self.layer is None: return featureTitle = feature.attribute(self.layer.displayField()) if featureTitle == '': featureTitle = feature.id() self.lineEdit.setText(str(featureTitle)) self.highlightFeature(canvasExtent) def clear(self): self.feature = QgsFeature() self.lineEdit.clear() def mapIdentification(self): if self.layer is None or self.mapTool is None or self.canvas is None: return self.mapTool.setLayer(self.layer) self.canvas.setMapTool(self.mapTool) self.windowWidget = QWidget.window(self) self.canvas.window().raise_() self.canvas.activateWindow() self.canvas.setFocus() self.mapTool.featureIdentified.connect(self.mapToolFeatureIdentified) self.mapTool.deactivated.connect(self.mapToolDeactivated) def mapToolFeatureIdentified(self, feature): feature = QgsFeature(feature) self.featureIdentified.emit(feature) self.unsetMapTool() self.setFeature(feature) def mapToolDeactivated(self): if self.windowWidget is not None: self.windowWidget.raise_() self.windowWidget.activateWindow() def highlightFeature(self, canvasExtent = CanvasExtent.Fixed): if self.canvas is None or not self.feature.isValid(): return geom = self.feature.geometry() if geom is None: return if canvasExtent == CanvasExtent.Scale: featBBox = geom.boundingBox() featBBox = self.canvas.mapSettings().layerToMapCoordinates(self.layer, featBBox) extent = self.canvas.extent() if not extent.contains(featBBox): extent.combineExtentWith(featBBox) extent.scale(1.1) self.canvas.setExtent(extent) self.canvas.refresh() elif canvasExtent == CanvasExtent.Pan: centroid = geom.centroid() center = centroid.asPoint() center = self.canvas.mapSettings().layerToMapCoordinates(self.layer, center) self.canvas.zoomByFactor(1.0, center) # refresh is done in this method # highlight self.delete_highlight() self.highlight = QgsHighlight(self.canvas, geom, self.layer) settings = QSettings() color = QColor(settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha())) buffer = 2*float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM)) min_width = 2*float(settings.value("/Map/highlight/min_width", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.highlight.setColor(color) # sets also fill with default alpha color.setAlpha(alpha) self.highlight.setFillColor(color) # sets fill with alpha self.highlight.setBuffer(buffer) self.highlight.setMinWidth(min_width) self.highlight.setWidth(4.0) self.highlight.show() self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.delete_highlight) self.timer.start(3000) def delete_highlight(self): if self.highlight is not None: self.highlight.hide() del self.highlight self.highlight = None def unsetMapTool(self): if self.canvas is not None and self.mapTool is not None: # this will call mapToolDeactivated self.canvas.unsetMapTool(self.mapTool) def highlightActionTriggered(self, action): self.highlightFeatureButton.setDefaultAction(action) if action == self.highlightFeatureAction: self.highlightFeature() elif action == self.scaleHighlightFeatureAction: self.highlightFeature(CanvasExtent.Scale) elif action == self.panHighlightFeatureAction: self.highlightFeature(CanvasExtent.Pan)
class DetailsTreeView(DetailsDBHandler, DetailsDockWidget): def __init__(self, iface, spatial_unit_dock): """ The method initializes the dockwidget. :param iface: QGIS user interface class :type class qgis.utils.iface :param plugin: The STDM plugin :type class :return: None """ from stdm.ui.entity_browser import _EntityDocumentViewerHandler DetailsDockWidget.__init__(self, iface, spatial_unit_dock) DetailsDBHandler.__init__(self) self.spatial_unit_dock = spatial_unit_dock self.view = QTreeView() self.view.setSelectionBehavior( QAbstractItemView.SelectRows ) #self.feature_ids = [] self.layer_table = None self.entity = None self.feature_models = {} self.party_models = {} self.STR_models = {} self.feature_STR_model = {} self.removed_feature = None self.selected_root = None self.model = QStandardItemModel() self.view.setModel(self.model) self.view.setUniformRowHeights(True) self.view.setRootIsDecorated(True) self.view.setAlternatingRowColors(True) self.view.setWordWrap(True) self.view.setHeaderHidden(True) self.view.setEditTriggers( QAbstractItemView.NoEditTriggers ) self.current_profile = current_profile() self.social_tenure = self.current_profile.social_tenure self.spatial_unit = self.social_tenure.spatial_unit self.party = self.social_tenure.party self.view.setMinimumWidth(250) self.doc_viewer_title = QApplication.translate( 'EntityBrowser', 'Document Viewer' ) self.doc_viewer = _EntityDocumentViewerHandler( self.doc_viewer_title, self.iface.mainWindow() ) def set_layer_entity(self): self.layer_table = self.get_layer_source( self.iface.activeLayer() ) if self.layer_table in spatial_tables(): self.entity = self.current_profile.entity_by_name( self.layer_table ) else: self.treeview_error('The layer is not a spatial entity layer. ') def activate_feature_details(self, button_clicked=True): """ Action for showing feature details. :return: """ # Get the active layer. active_layer = self.iface.activeLayer() # TODO fix feature_details_btn is deleted error. if active_layer is not None and \ self.spatial_unit_dock.feature_details_btn.isChecked(): # if feature detail dock is not defined or hidden, create empty dock. if self is None or self.isHidden(): # if the selected layer is not a feature layer, show not # feature layer. (implicitly included in the if statement). if not self.feature_layer(active_layer): self.spatial_unit_dock.feature_details_btn.setChecked(False) return # If the selected layer is feature layer, get data and # display treeview in a dock widget else: select_feature = QApplication.translate( "STDMQGISLoader", "Please select a feature to view their details." ) self.init_dock() self.add_tree_view() self.model.clear() self.treeview_error(select_feature) # enable the select tool self.activate_select_tool() # set entity from active layer in the child class self.set_layer_entity() # set entity for the super class DetailModel self.set_entity(self.entity) # Registery column widget self.set_formatter() #set formatter for social tenure relationship. self.set_formatter(self.social_tenure) self.set_formatter(self.party) # pull data, show treeview active_layer.selectionChanged.connect( self.show_tree ) self.steam_signals(self.entity) # if feature_detail dock is open, toggle close else: self.close_dock( self.spatial_unit_dock.feature_details_btn ) self.feature_details = None # if no active layer, show error message and uncheck the feature tool else: if button_clicked: self.active_layer_check() self.spatial_unit_dock.feature_details_btn.setChecked(False) def add_tree_view(self): """ Adds tree view to the dock widget and sets style. :return: None """ self.tree_scrollArea.setWidget(self.view) def clear_feature_models(self): self.feature_models.clear() def reset_tree_view(self, no_feature=False): #clear feature_ids list, model and highlight self.model.clear() self.clear_sel_highlight() # remove sel_highlight self.disable_buttons(no_feature) if self.removed_feature is None: self.STR_models.clear() self.feature_models.clear() else: self.removed_feature = None features = self.selected_features() # if the selected feature is over 1, # activate multi_select_highlight if not features is None: self.view.clicked.connect( self.multi_select_highlight ) # if there is at least one selected feature if len(features) > 0: self.add_tree_view() #self.feature_ids = features def disable_buttons(self, bool): self.edit_btn.setDisabled(bool) self.delete_btn.setDisabled(bool) def show_tree(self): selected_features = self.selected_features() if len(selected_features) < 1: self.reset_tree_view(True) return if not self.entity is None: self.reset_tree_view() if len(selected_features) < 1: self.disable_buttons(True) return layer_icon = QIcon(':/plugins/stdm/images/icons/layer.gif') roots = self.add_parent_tree( layer_icon, format_name(self.entity.short_name) ) if roots is None: return for id, root in roots.iteritems(): db_model = entity_id_to_model(self.entity, id) self.add_roots(db_model, root, id) def add_parent_tree(self, icon, title): roots = OrderedDict() for feature_id in self.selected_features(): root = QStandardItem(icon, title) root.setData(feature_id) self.set_bold(root) self.model.appendRow(root) roots[feature_id] = root return roots def add_roots(self, model, parent, feature_id): self.feature_models[feature_id] = model if model is None: return self.column_widget_registry(model, self.entity) for i, (col, row) in enumerate(self.formatted_record.iteritems()): child = QStandardItem('{}: {}'.format(col, row)) child.setSelectable(False) parent.appendRow([child]) # Add Social Tenure Relationship steam as a last child if i == len(self.formatted_record)-1: self.add_STR_child(parent, feature_id) self.expand_node(parent) def add_STR_steam(self, parent, STR_id): str_icon = QIcon( ':/plugins/stdm/images/icons/social_tenure.png' ) title = 'Social Tenure Relationship' str_root = QStandardItem(str_icon, title) str_root.setData(STR_id) self.set_bold(str_root) parent.appendRow([str_root]) return str_root def add_no_STR_steam(self, parent): if self.entity.name == self.spatial_unit.name: no_str_icon = QIcon( ':/plugins/stdm/images/icons/remove.png' ) title = 'No STR Defined' no_str_root = QStandardItem(no_str_icon, title) self.set_bold(no_str_root) parent.appendRow([no_str_root]) def add_STR_child(self, parent, feature_id): if len(self.feature_STR_link(feature_id)) < 1: self.add_no_STR_steam(parent) return for record in self.feature_STR_link(feature_id): self.STR_models[record.id] = record str_root = self.add_STR_steam(parent, record.id) # add STR children self.column_widget_registry(record, self.social_tenure) for i, (col, row) in enumerate( self.formatted_record.iteritems() ): STR_child = QStandardItem( '{}: {}'.format(col, row) ) STR_child.setSelectable(False) str_root.appendRow([STR_child]) if i == len(self.formatted_record)-1: self.add_party_child( str_root, record.party_id ) self.feature_STR_model[feature_id] = self.STR_models.keys() def add_party_steam(self, parent, party_id): party_icon = QIcon( ':/plugins/stdm/images/icons/table.png' ) title = format_name(self.party.short_name) party_root = QStandardItem(party_icon, title) party_root.setData(party_id) self.set_bold(party_root) parent.appendRow([party_root]) party_root.setEditable(False) return party_root def add_party_child(self, parent, party_id): db_model = entity_id_to_model(self.party, party_id) self.party_models[party_id] = db_model party_root = self.add_party_steam(parent, party_id) # add STR children self.column_widget_registry(db_model, self.party) for col, row in self.formatted_record.iteritems(): party_child = QStandardItem('{}: {}'.format(col, row)) party_child.setSelectable(False) party_root.appendRow([party_child]) def set_bold(self, standard_item): """ Make a text of Qstandaritem to bold. :param standard_item: Qstandaritem :type: Qstandaritem :return: None """ font = standard_item.font() font.setBold(True) standard_item.setFont(font) def treeview_error(self, message, icon=None): """ Displays error message in feature details treeview. :param title: the title of the treeview. :type: String :param message: The message to be displayed. :type: String :param icon: The icon of the item. :type: Resource string :return: None """ not_feature_ft_msg = QApplication.translate( 'FeatureDetails', message ) if icon== None: root = QStandardItem(not_feature_ft_msg) else: root = QStandardItem(icon, not_feature_ft_msg) self.view.setRootIsDecorated(False) self.model.appendRow(root) self.view.setRootIsDecorated(True) def expand_node(self, parent): """ Make the last tree node expand. :param parent: The parent to expand :type QStandardItem :return:None """ index = self.model.indexFromItem(parent) self.view.expand(index) def multi_select_highlight(self, index): """ Highlights a feature with rubberBald class when selecting features are more than one. :param index: Selected QTreeView item index :type Integer :return: None """ map = self.iface.mapCanvas() try: # Get the selected item text using the index selected_item = self.model.itemFromIndex(index) # Use mutli-select only when more than 1 items are selected. if self.layer.selectedFeatures() < 2: return self.selected_root = selected_item # Split the text to get the key and value. selected_item_text = selected_item.text() selected_value = selected_item.data() # If the first word is feature, expand & highlight. if selected_item_text == format_name(self.spatial_unit.short_name): self.view.expand(index) # expand the item # Clear any existing highlight self.clear_sel_highlight() # Insert highlight # Create expression to target the selected feature expression = QgsExpression( "\"id\"='" + str(selected_value) + "'" ) # Get feature iteration based on the expression ft_iteration = self.layer.getFeatures( QgsFeatureRequest(expression) ) # Retrieve geometry and attributes for feature in ft_iteration: # Fetch geometry geom = feature.geometry() self.sel_highlight = QgsHighlight(map, geom, self.layer) self.sel_highlight.setFillColor(selection_color()) self.sel_highlight.setWidth(4) self.sel_highlight.setColor(QColor(212,95,0, 255)) self.sel_highlight.show() break except AttributeError: # pass attribute error on child items such as party pass except IndexError: pass def steam_signals(self, entity): self.edit_btn.clicked.connect( lambda : self.edit_selected_steam( entity ) ) self.delete_btn.clicked.connect( self.delete_selected_item ) self.view_document_btn.clicked.connect( lambda : self.view_steam_document( entity ) ) def steam_data(self, mode): item = None # if self.view.currentIndex().text() == format_name(self.party): # return None, None # One item is selected and number of feature is also 1 if len(self.layer.selectedFeatures()) == 1 and \ len(self.view.selectedIndexes()) == 1: index = self.view.selectedIndexes()[0] item = self.model.itemFromIndex(index) result = item.data() # One item is selected on the map but not on the treeview elif len(self.layer.selectedFeatures()) == 1 and \ len(self.view.selectedIndexes()) == 0: item = self.model.item(0, 0) result = item.data() # multiple features are selected but one treeview item is selected elif len(self.layer.selectedFeatures()) > 1 and \ len(self.view.selectedIndexes()) == 1: item = self.selected_root result = self.selected_root.data() # multiple features are selected but no treeview item is selected elif len(self.layer.selectedFeatures()) > 1 and \ len(self.view.selectedIndexes()) == 0: result = 'Please, select an item to {}.'.format(mode) else: result = 'Please, select at least one feature to {}.'.format(mode) if result is None: if item is None: item = self.model.item(0, 0) result = item.data() else: result = item.parent().data() return result, item def edit_selected_steam(self, entity): id, item = self.steam_data('edit') feature_edit = True if id is None: return if isinstance(id, str): data_error = QApplication.translate('DetailsTreeView', id) QMessageBox.warning( self.iface.mainWindow(), "Edit Error", data_error ) return if item.text() == 'Social Tenure Relationship': model = self.STR_models[id] feature_edit = False ##TODO add STR wizard edit mode here. elif item.text() == format_name(self.party.short_name): feature_edit = False model = self.party_models[id] editor = EntityEditorDialog( self.party, model, self.iface.mainWindow() ) editor.exec_() else: model = self.feature_models[id] editor = EntityEditorDialog( entity, model, self.iface.mainWindow() ) editor.exec_() #root = self.find_root(entity, id) self.view.expand(item.index()) if feature_edit: self.update_edited_steam(entity, id) else: self.update_edited_steam(self.social_tenure, id) def delete_selected_item(self): str_edit = False id, item = self.steam_data('delete') if isinstance(id, str): data_error = QApplication.translate( 'DetailsTreeView', id ) QMessageBox.warning( self.iface.mainWindow(), 'Delete Error', data_error ) return if item.text() == 'Social Tenure Relationship': str_edit = True db_model = self.STR_models[id] elif item.text() == format_name(self.spatial_unit.short_name) and \ id not in self.feature_STR_model.keys(): db_model = self.feature_models[id] # if spatial unit is linked to STR, don't allow delete elif item.text() == format_name(self.spatial_unit.short_name) and \ id in self.feature_STR_model.keys(): delete_warning = QApplication.translate( 'DetailsTreeView', 'You have to first delete the social tenure \n' 'relationship to delete the {} record.'.format( item.text() ) ) QMessageBox.warning( self.iface.mainWindow(), 'Delete Error', delete_warning ) return # If it is party node, STR exists and don't allow delete. elif item.text() == format_name(self.party.short_name): delete_warning = QApplication.translate( 'DetailsTreeView', 'You have to first delete the social tenure \n' 'relationship to delete the {} record.'.format( item.text() ) ) QMessageBox.warning( self.iface.mainWindow(), 'Delete Error', delete_warning ) return else: return delete_warning = QApplication.translate( 'DetailsTreeView', 'Are you sure you want to delete ' 'the selected record(s)?\n' 'This action cannot be undone.' ) delete_question = QMessageBox.warning( self.iface.mainWindow(), "Delete Warning", delete_warning, QMessageBox.Yes | QMessageBox.No ) if delete_question == QMessageBox.Yes: db_model.delete() if str_edit: del self.STR_models[id] else: self.removed_feature = id del self.feature_models[id] self.updated_removed_steam(str_edit, item) else: return def update_edited_steam(self, entity, feature_id): # remove rows before adding the updated ones. self.layer.setSelectedFeatures( self.feature_models.keys() ) root = self.find_root(entity, feature_id) if root is None: return self.view.selectionModel().select( root.index(), self.view.selectionModel().Select ) self.expand_node(root) self.multi_select_highlight(root.index()) def find_root(self, entity, feature_id): all_roots = self.model.findItems( format_name(entity.short_name) ) root = None for item in all_roots: if item.data() == feature_id: root = item break return root def updated_removed_steam(self, STR_edit, item): if not STR_edit: if len(self.feature_models) > 1: self.refresh_layers() feature_ids = self.feature_models.keys() self.layer.setSelectedFeatures( feature_ids ) else: item.removeRows(0, 2) item.setText('No STR Definded') no_str_icon = QIcon( ':/plugins/stdm/images/icons/remove.png' ) item.setIcon(no_str_icon) def view_steam_document(self, entity): # Slot raised to show the document viewer for the selected entity id, item = self.steam_data('edit') if id is None: return if isinstance(id, str): data_error = QApplication.translate('DetailsTreeView', id) QMessageBox.warning( self.iface.mainWindow(), "Edit Error", data_error ) return if item.text() == 'Social Tenure Relationship': db_model = self.STR_models[id] else: db_model = self.feature_models[id] if not db_model is None: docs = db_model.documents # Notify there are no documents for the selected doc if len(docs) == 0: msg = QApplication.translate( 'EntityBrowser', 'There are no supporting documents ' 'for the selected record.' ) QMessageBox.warning( self, self.doc_viewer_title, msg ) else: self.doc_viewer.load(docs)
def getFlash(): h = QgsHighlight( self.canvas, geometry, layer ) h.setColor( QColor( 255, 0, 0, 255 ) ) h.setFillColor( QColor( 255, 0, 0, 100 ) ) h.setWidth( 2 ) return h
class LrsDockWidget(QDockWidget, Ui_LrsDockWidget): def __init__(self, parent, iface): # #debug( "LrsDockWidget.__init__") self.iface = iface self.lrs = None # Lrs object self.lrsLayer = None # Common input LrsLayer for locate/events/measure self.genSelectionDialog = None self.locatePoint = None # QgsPointXY self.locateHighlight = None # QgsHighlight self.errorPointLayer = None self.errorPointLayerManager = None self.errorLineLayer = None self.errorLineLayerManager = None self.qualityLayer = None self.qualityLayerManager = None self.pluginDir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath() ).path() + "/python/plugins/lrs" super(LrsDockWidget, self).__init__(parent) # Set up the user interface from Designer. self.setupUi(self) # keep progress frame height self.genProgressFrame.setMinimumHeight(self.genProgressFrame.height()) self.hideGenProgress() self.tabWidget.currentChanged.connect(self.tabChanged) # ------------- locate, events, measure have synchronized lrs layer and route field -------- lrsLayerComboList = [ self.locateLrsLayerCombo, self.eventsLrsLayerCombo, self.measureLrsLayerCombo ] self.lrsLayerCM = LrsLayerComboManager( lrsLayerComboList, geometryType=QgsWkbTypes.LineGeometry, geometryHasM=True, settingsName='lrsLayerId') self.lrsLayerCM.layerChanged.connect(self.lrsLayerChanged) lrsRouteFieldComboList = [ self.locateLrsRouteFieldCombo, self.eventsLrsRouteFieldCombo, self.measureLrsRouteFieldCombo ] self.lrsRouteFieldCM = LrsFieldComboManager( lrsRouteFieldComboList, self.lrsLayerCM, settingsName='lrsRouteField', allowNone=True) # using activated() which is called on user interaction to avoid be called 3 times from each combo # self.lrsRouteFieldCM.fieldNameChanged.connect(self.lrsRouteFieldNameChanged) self.lrsRouteFieldCM.fieldNameActivated.connect( self.lrsRouteFieldNameActivated) # ----------------------- locateTab --------------------------- self.locateRouteCM = LrsComboManager(self.locateRouteCombo) self.locateHighlightWM = LrsWidgetManager( self.locateHighlightCheckBox, settingsName='locateHighlight', defaultValue=True) self.locateBufferWM = LrsWidgetManager(self.locateBufferSpin, settingsName='locateBuffer', defaultValue=200.0) self.locateRouteCombo.currentIndexChanged.connect( self.locateRouteChanged) self.locateMeasureSpin.valueChanged.connect(self.resetLocateEvent) self.locateBufferSpin.valueChanged.connect(self.locateBufferChanged) self.locateCenterButton.clicked.connect(self.locateCenter) self.locateHighlightCheckBox.stateChanged.connect( self.locateHighlightChanged) self.locateZoomButton.clicked.connect(self.locateZoom) self.locateHelpButton.clicked.connect(lambda: self.showHelp('locate')) self.resetLocateRoutes() self.locateProgressBar.hide() # ----------------------- eventsTab --------------------------- self.eventsLayerCM = LrsLayerComboManager(self.eventsLayerCombo, settingsName='eventsLayerId') self.eventsRouteFieldCM = LrsFieldComboManager( self.eventsRouteFieldCombo, self.eventsLayerCM, settingsName='eventsRouteField') self.eventsMeasureStartFieldCM = LrsFieldComboManager( self.eventsMeasureStartFieldCombo, self.eventsLayerCM, types=QVARIANT_NUMBER_TYPE_LIST, settingsName='eventsMeasureStartField') self.eventsMeasureEndFieldCM = LrsFieldComboManager( self.eventsMeasureEndFieldCombo, self.eventsLayerCM, types=QVARIANT_NUMBER_TYPE_LIST, allowNone=True, settingsName='eventsMeasureEndField') self.eventsFeaturesSelectCM = LrsComboManager( self.eventsFeaturesSelectCombo, options=((ALL_FEATURES, self.tr('All features')), (SELECTED_FEATURES, self.tr('Selected features'))), defaultValue=ALL_FEATURES, settingsName='eventsFeaturesSelect') self.eventsOutputNameWM = LrsWidgetManager( self.eventsOutputNameLineEdit, settingsName='eventsOutputName', defaultValue='LRS events') self.eventsErrorFieldWM = LrsWidgetManager( self.eventsErrorFieldLineEdit, settingsName='eventsErrorField', defaultValue='lrs_err') validator = QRegExpValidator(QRegExp('[A-Za-z_][A-Za-z0-9_]+'), None) self.eventsErrorFieldLineEdit.setValidator(validator) self.eventsButtonBox.button(QDialogButtonBox.Ok).clicked.connect( self.createEvents) self.eventsButtonBox.button(QDialogButtonBox.Reset).clicked.connect( self.resetEventsOptionsAndWrite) self.eventsButtonBox.button(QDialogButtonBox.Help).clicked.connect( lambda: self.showHelp('events')) self.eventsLayerCombo.currentIndexChanged.connect( self.resetEventsButtons) self.eventsRouteFieldCombo.currentIndexChanged.connect( self.resetEventsButtons) self.eventsMeasureStartFieldCombo.currentIndexChanged.connect( self.resetEventsButtons) self.eventsMeasureEndFieldCombo.currentIndexChanged.connect( self.resetEventsButtons) self.eventsOutputNameLineEdit.textEdited.connect( self.resetEventsButtons) self.resetEventsOptions() self.resetEventsButtons() self.eventsProgressBar.hide() self.eventsLayerCM.reload() # ----------------------- measureTab --------------------------- self.measureLayerCM = LrsLayerComboManager( self.measureLayerCombo, geometryType=QgsWkbTypes.PointGeometry, settingsName='measureLayerId') self.measureRouteFieldCM = LrsFieldComboManager( self.measureRouteFieldCombo, self.measureLayerCM, allowNone=True, settingsName='measureRouteField') self.measureThresholdWM = LrsWidgetManager( self.measureThresholdSpin, settingsName='measureThreshold', defaultValue=100.0) self.measureOutputNameWM = LrsWidgetManager( self.measureOutputNameLineEdit, settingsName='measureOutputName', defaultValue='LRS measure') self.measureOutputRouteFieldWM = LrsWidgetManager( self.measureOutputRouteFieldLineEdit, settingsName='measureOutputRouteField', defaultValue='route') validator = QRegExpValidator(QRegExp('[A-Za-z_][A-Za-z0-9_]+'), None) self.measureOutputRouteFieldLineEdit.setValidator(validator) self.measureMeasureFieldWM = LrsWidgetManager( self.measureMeasureFieldLineEdit, settingsName='measureMeasureField', defaultValue='measure') self.measureMeasureFieldLineEdit.setValidator(validator) self.measureButtonBox.button(QDialogButtonBox.Ok).clicked.connect( self.calculateMeasures) self.measureButtonBox.button(QDialogButtonBox.Reset).clicked.connect( self.resetMeasureOptionsAndWrite) self.measureButtonBox.button(QDialogButtonBox.Help).clicked.connect( lambda: self.showHelp('measures')) self.measureLayerCombo.currentIndexChanged.connect( self.resetMeasureButtons) self.measureOutputNameLineEdit.textEdited.connect( self.resetMeasureButtons) self.measureOutputRouteFieldLineEdit.textEdited.connect( self.resetMeasureButtons) self.measureMeasureFieldLineEdit.textEdited.connect( self.resetMeasureButtons) self.resetMeasureOptions() self.resetMeasureButtons() self.measureProgressBar.hide() self.measureLayerCM.reload() # ------------- genTab ----------------------- self.genLineLayerCM = LrsLayerComboManager( self.genLineLayerCombo, geometryType=QgsWkbTypes.LineGeometry, settingsName='lineLayerId') self.genLineRouteFieldCM = LrsFieldComboManager( self.genLineRouteFieldCombo, self.genLineLayerCM, settingsName='lineRouteField') self.genPointLayerCM = LrsLayerComboManager( self.genPointLayerCombo, geometryType=QgsWkbTypes.PointGeometry, settingsName='pointLayerId') self.genPointRouteFieldCM = LrsFieldComboManager( self.genPointRouteFieldCombo, self.genPointLayerCM, settingsName='pointRouteField') self.genPointMeasureFieldCM = LrsFieldComboManager( self.genPointMeasureFieldCombo, self.genPointLayerCM, types=QVARIANT_NUMBER_TYPE_LIST, settingsName='pointMeasureField') self.genMeasureUnitCM = LrsUnitComboManager( self.genMeasureUnitCombo, settingsName='measureUnit', defaultValue=LrsUnits.KILOMETER) self.genSelectionModeCM = LrsComboManager( self.genSelectionModeCombo, options=(('all', self.tr('All routes')), ('include', self.tr('Include routes')), ('exclude', self.tr('Exclude routes'))), defaultValue='all', settingsName='selectionMode') self.genSelectionWM = LrsWidgetManager(self.genSelectionLineEdit, settingsName='selection') self.genThresholdWM = LrsWidgetManager(self.genThresholdSpin, settingsName='threshold', defaultValue=100.0) self.genSnapWM = LrsWidgetManager(self.genSnapSpin, settingsName='snap', defaultValue=0.0) self.genParallelModeCM = LrsComboManager( self.genParallelModeCombo, options=(('error', self.tr('Mark as errors')), ('span', self.tr('Span by straight line')), ('exclude', self.tr('Exclude'))), defaultValue='error', settingsName='parallelMode') self.genExtrapolateWM = LrsWidgetManager(self.genExtrapolateCheckBox, settingsName='extrapolate', defaultValue=False) self.genOutputNameWM = LrsWidgetManager(self.genOutputNameLineEdit, settingsName='lrsOutputName', defaultValue='LRS') self.genLineLayerCombo.currentIndexChanged.connect( self.resetGenerateButtons) self.genLineLayerCombo.currentIndexChanged.connect( self.updateLabelsUnits) self.genLineRouteFieldCombo.currentIndexChanged.connect( self.resetGenerateButtons) self.genPointLayerCombo.currentIndexChanged.connect( self.resetGenerateButtons) self.genPointRouteFieldCombo.currentIndexChanged.connect( self.resetGenerateButtons) self.genPointMeasureFieldCombo.currentIndexChanged.connect( self.resetGenerateButtons) self.genSelectionModeCombo.currentIndexChanged.connect( self.enableGenerateSelection) self.genSelectionButton.clicked.connect( self.openGenerateSelectionDialog) self.genButtonBox.button(QDialogButtonBox.Ok).clicked.connect( self.generateLrs) self.genButtonBox.button(QDialogButtonBox.Reset).clicked.connect( self.resetGenerateOptionsAndWrite) self.genButtonBox.button(QDialogButtonBox.Help).clicked.connect( lambda: self.showHelp('calibration')) self.genOutputNameLineEdit.textChanged.connect(self.resetGenButtons) self.genCreateOutputButton.setEnabled(False) self.genCreateOutputButton.clicked.connect(self.createLrsOutput) # load layers after other combos were connected self.genLineLayerCM.reload() self.genPointLayerCM.reload() self.enableGenerateSelection() # ------------- errorTab ----------------------- self.errorVisualizer = LrsErrorVisualizer(self.iface.mapCanvas()) self.errorModel = None self.errorView.horizontalHeader().setStretchLastSection(True) self.errorZoomButton.setEnabled(False) self.errorZoomButton.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.errorZoomButton.setText('Zoom') self.errorZoomButton.clicked.connect(self.errorZoom) self.errorFilterLineEdit.textChanged.connect(self.errorFilterChanged) self.addErrorLayersButton.clicked.connect(self.addErrorLayers) self.addQualityLayerButton.clicked.connect(self.addQualityLayer) self.errorButtonBox.button(QDialogButtonBox.Help).clicked.connect( lambda: self.showHelp('errors')) # ---------------------------- statistics tab ---------------------------- # currently not used (did not correspond well to errors) # self.tabWidget.removeTab( self.tabWidget.indexOf(self.statsTab) ) # ------------------ help tab ------------------------- # Importing QWebEngineView gives (Qt 5.8.0, PyQt5 5.8.2): # ImportError: QtWebEngineWidgets must be imported before a QCoreApplication instance is created # from PyQt5.QtWebEngineWidgets import QWebEngineView # self.helpWebEngineView = QWebEngineView(self.helpTab) # QTextBrowser does not render perfectly all html created by Sphinx -> see help/source/conf.py # http://doc.qt.io/qt-5/richtext-html-subset.html # QTextBrowser would not render external web sites -> open in browser self.helpTextBrowser.setOpenExternalLinks(True) self.helpTextBrowser.setSource(QUrl(self.getHelpUrl())) # self.helpTextBrowser.setSource(QUrl("http://www.mpasolutions.it/")) # self.helpTextBrowser.setSource(QUrl("http://www.google.com/")) # ----------------------------------------------------- # after all combos were created and connected self.lrsLayerCM.reload() # -> lrsRouteFieldCM -> .... # ----------------------------------------------------- self.enableTabs() QgsProject.instance().layersWillBeRemoved.connect( self.layersWillBeRemoved) QgsProject.instance().readProject.connect(self.projectRead) QgsProject().instance().crsChanged.connect(self.mapSettingsCrsChanged) self.updateLabelsUnits() # newProject is currently missing in sip # QgsProject.instance().newProject.connect( self.projectNew ) # read project if plugin was reloaded self.projectRead() def lrsLayerChanged(self, layer): # debug("lrsLayerChanged layer: %s" % (layer.name() if layer else None)) self.lrsLayer = None if layer is not None: self.lrsLayer = LrsLayer(layer) self.lrsRouteFieldCM.reset() self.resetLocateRoutes() self.updateMeasureUnits() # don't write here, the layer is changing also when loading plugin -> # written when route is selected # self.lrsLayerCM.writeToProject() def lrsRouteFieldNameActivated(self, fieldName): # debug("lrsRouteFieldNameActivated fieldName = " + fieldName) self.loadLrsLayer() self.lrsLayerCM.writeToProject() self.lrsRouteFieldCM.writeToProject() def loadLrsLayer(self): fieldName = self.lrsRouteFieldCM.value() if self.lrsLayer: self.lrsLayer.setRouteFieldName(fieldName) if fieldName: self.showLrsLayerProgressBar() self.lrsLayer.load(self.loadLrsLayerProgress) self.hideLrsLayerProgressBar() else: self.lrsLayer.reset() self.resetLocateRoutes() def errorFilterChanged(self, text): if not self.sortErrorModel: return self.sortErrorModel.setFilterWildcard(text) def projectRead(self): # debug("projectRead") if not QgsProject: return project = QgsProject.instance() if not project: return self.lrsLayerCM.readFromProject() self.lrsRouteFieldCM.readFromProject() self.loadLrsLayer() # load if layer + route field were selected self.readGenerateOptions() self.readLocateOptions() self.readEventsOptions() self.readMeasureOptions() # --------------------- set error layers if stored in project ------------------- errorLineLayerId = project.readEntry(PROJECT_PLUGIN_NAME, "errorLineLayerId")[0] # debug("projectRead errorLineLayerId = %s" % errorLineLayerId) self.errorLineLayer = project.mapLayer(errorLineLayerId) # debug("projectRead errorLineLayer = %s" % self.errorLineLayer) # layers must be tested 'is not None' (because layers have __len__(), some strange len) if self.errorLineLayer is not None: self.errorLineLayerManager = LrsErrorLayerManager( self.errorLineLayer) errorPointLayerId = project.readEntry(PROJECT_PLUGIN_NAME, "errorPointLayerId")[0] self.errorPointLayer = project.mapLayer(errorPointLayerId) if self.errorPointLayer is not None: self.errorPointLayerManager = LrsErrorLayerManager( self.errorPointLayer) qualityLayerId = project.readEntry(PROJECT_PLUGIN_NAME, "qualityLayerId")[0] self.qualityLayer = project.mapLayer(qualityLayerId) if self.qualityLayer is not None: self.qualityLayerManager = LrsQualityLayerManager( self.qualityLayer) self.resetGenerateButtons() # #debug # if self.genLineLayerCM.getLayer(): # self.generateLrs() # only when reloading! def projectNew(self): self.deleteLrs() self.resetGenerateOptions() self.resetEventsOptions() self.resetMeasureOptions() self.enableTabs() def deleteLrs(self): if self.lrs is not None: self.lrs.disconnect() del self.lrs self.lrs = None def close(self): # #debug( "LrsDockWidget.close") self.deleteLrs() QgsProject.instance().layersWillBeRemoved.disconnect( self.layersWillBeRemoved) QgsProject.instance().readProject.disconnect(self.projectRead) QgsProject().instance().crsChanged.disconnect( self.mapSettingsCrsChanged) # Must delete combo managers to disconnect! del self.genLineLayerCM del self.genLineRouteFieldCM del self.genPointLayerCM del self.genPointRouteFieldCM del self.genPointMeasureFieldCM del self.errorVisualizer del self.eventsLayerCM del self.eventsRouteFieldCM del self.eventsMeasureStartFieldCM del self.eventsMeasureEndFieldCM del self.eventsFeaturesSelectCM self.clearLocateHighlight() super(LrsDockWidget, self).close() def layersWillBeRemoved(self, layerIdList): # debug("layersWillBeRemoved layerIdList = %s" % layerIdList) project = QgsProject.instance() # layers must be tested 'is not None' (because layers have __len__(), some strange len) for id in layerIdList: if self.errorPointLayer is not None and self.errorPointLayer.id( ) == id: # debug("layersWillBeRemoved errorPointLayer.id = %s -> unset" % self.errorPointLayer.id()) self.errorPointLayerManager = None self.errorPointLayer = None project.removeEntry(PROJECT_PLUGIN_NAME, "errorPointLayerId") if self.errorLineLayer is not None and self.errorLineLayer.id( ) == id: self.errorLineLayerManager = None self.errorLineLayer = None project.removeEntry(PROJECT_PLUGIN_NAME, "errorLineLayerId") if self.qualityLayer is not None and self.qualityLayer.id() == id: self.qualityLayerManager = None self.qualityLayer = None project.removeEntry(PROJECT_PLUGIN_NAME, "qualityLayerId") def enableTabs(self): enable = bool(self.lrs) # self.errorTab.setEnabled(enable) # self.locateTab.setEnabled(enable) # self.eventsTab.setEnabled(enable) # self.measureTab.setEnabled(enable) # self.statsTab.setEnabled(enable) def tabChanged(self, index): # #debug("tabChanged index = %s" % index ) pass def mapSettingsCrsChanged(self): # #debug("mapSettingsCrsChanged") self.updateLabelsUnits() @staticmethod def getUnitsLabel(crs): if crs: return " (%s)" % QgsUnitTypes.encodeUnit(crs.mapUnits()) else: return "" def getThresholdLabel(self, crs): label = "Max point distance" if crs is not None: label += self.getUnitsLabel(crs) return label def getHelpUrl(self): return "file:///" + self.pluginDir + "/help/index.html" def showHelp(self, anchor=None): # debug("showHelp anchor = %s" % anchor) url = self.getHelpUrl() if anchor: url += "#" + anchor # debug("showHelp url = %s" % url) self.helpTextBrowser.setSource(QUrl(url)) # QDesktopServices.openUrl(QUrl(url)) # open in default browser self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(self.helpTab)) def lrsEdited(self): self.resetStats() # ------------------- GENERATE (CALIBRATE) ------------------- def resetGenerateButtons(self): enabled = self.genLineLayerCombo.currentIndex( ) != -1 and self.genLineRouteFieldCombo.currentIndex( ) != -1 and self.genPointLayerCombo.currentIndex( ) != -1 and self.genPointRouteFieldCombo.currentIndex( ) != -1 and self.genPointMeasureFieldCombo.currentIndex() != -1 self.genButtonBox.button(QDialogButtonBox.Ok).setEnabled(enabled) def resetGenerateOptions(self): self.genLineLayerCM.reset() self.genLineRouteFieldCM.reset() self.genPointLayerCM.reset() self.genPointRouteFieldCM.reset() self.genPointMeasureFieldCM.reset() self.genMeasureUnitCM.reset() self.genSelectionModeCM.reset() self.genSelectionWM.reset() self.genThresholdWM.reset() self.genSnapWM.reset() self.genParallelModeCM.reset() self.genExtrapolateWM.reset() self.genOutputNameWM.reset() self.resetGenerateButtons() def resetGenerateOptionsAndWrite(self): self.resetGenerateOptions() self.writeGenerateOptions() def enableGenerateSelection(self): enable = self.genSelectionModeCM.value() != 'all' self.genSelectionLineEdit.setEnabled(enable) self.genSelectionButton.setEnabled(enable) # save settings in project def writeGenerateOptions(self): self.genLineLayerCM.writeToProject() self.genLineRouteFieldCM.writeToProject() self.genPointLayerCM.writeToProject() self.genPointRouteFieldCM.writeToProject() self.genPointMeasureFieldCM.writeToProject() self.genMeasureUnitCM.writeToProject() self.genSelectionModeCM.writeToProject() self.genSelectionWM.writeToProject() self.genThresholdWM.writeToProject() self.genSnapWM.writeToProject() self.genParallelModeCM.writeToProject() self.genExtrapolateWM.writeToProject() self.genOutputNameWM.writeToProject() def readGenerateOptions(self): self.genLineLayerCM.readFromProject() self.genLineRouteFieldCM.readFromProject() self.genPointLayerCM.readFromProject() self.genPointRouteFieldCM.readFromProject() self.genPointMeasureFieldCM.readFromProject() self.genMeasureUnitCM.readFromProject() self.genSelectionModeCM.readFromProject() self.genSelectionWM.readFromProject() self.genThresholdWM.readFromProject() self.genSnapWM.readFromProject() self.genParallelModeCM.readFromProject() self.genExtrapolateWM.readFromProject() self.genOutputNameWM.readFromProject() def getGenerateSelection(self): return map(str.strip, self.genSelectionLineEdit.text().split(',')) def openGenerateSelectionDialog(self): if not self.genSelectionDialog: self.genSelectionDialog = LrsSelectionDialog() self.genSelectionDialog.accepted.connect( self.generateSelectionDialogAccepted) layer = self.genLineLayerCM.getLayer() fieldName = self.genLineRouteFieldCM.getFieldName() select = self.getGenerateSelection() self.genSelectionDialog.load(layer, fieldName, select) self.genSelectionDialog.show() def generateSelectionDialogAccepted(self): selection = self.genSelectionDialog.selected() selection = ",".join(map(str, selection)) self.genSelectionLineEdit.setText(selection) def getGenerateCrs(self): crs = None # debug ( "genLineLayerCM = %s" % self.genLineLayerCM ) lineLayer = self.genLineLayerCM.getLayer() if lineLayer: # debug('lineLayer.crs().authid() = %s' % lineLayer.crs().authid()) crs = lineLayer.crs() if isProjectCrsEnabled(): # #debug ('enabled mapCanvas crs = %s' % self.iface.mapCanvas().mapSettings().destinationCrs().authid() ) crs = getProjectCrs() return crs # set threshold units according to current crs def updateLabelsUnits(self): crs = self.getGenerateCrs() label = self.getThresholdLabel(crs) self.genThresholdLabel.setText(label) label = "Max lines snap" + self.getUnitsLabel(crs) self.genSnapLabel.setText(label) def generateLrs(self): # debug ( 'generateLrs') self.deleteLrs() self.errorVisualizer.clearHighlight() self.writeGenerateOptions() crs = self.getGenerateCrs() selection = self.getGenerateSelection() snap = self.genSnapSpin.value() threshold = self.genThresholdSpin.value() parallelMode = self.genParallelModeCM.value() extrapolate = self.genExtrapolateCheckBox.isChecked() # self.mapUnitsPerMeasureUnit = self.genMapUnitsPerMeasureUnitSpin.value() measureUnit = self.genMeasureUnitCM.unit() self.lrs = LrsCalib(self.genLineLayerCM.getLayer(), self.genLineRouteFieldCM.getFieldName(), self.genPointLayerCM.getLayer(), self.genPointRouteFieldCM.getFieldName(), self.genPointMeasureFieldCM.getFieldName(), selectionMode=self.genSelectionModeCM.value(), selection=selection, crs=crs, snap=snap, threshold=threshold, parallelMode=parallelMode, extrapolate=extrapolate, measureUnit=measureUnit) self.genProgressLabel.setText("Writing features") self.lrs.progressChanged.connect(self.showGenProgress) self.lrs.calibrate() self.hideGenProgress() self.resetStats() # ------------------- errors ------------------- self.errorZoomButton.setEnabled(False) self.errorModel = LrsErrorModel() self.errorModel.addErrors(self.lrs.getErrors()) self.sortErrorModel = QSortFilterProxyModel() self.sortErrorModel.setFilterKeyColumn(-1) # all columns self.sortErrorModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.sortErrorModel.setDynamicSortFilter(True) self.sortErrorModel.setSourceModel(self.errorModel) self.errorView.setModel(self.sortErrorModel) self.sortErrorModel.sort(0) self.errorView.resizeColumnsToContents() self.errorView.setSelectionBehavior(QAbstractItemView.SelectRows) # Attention, if selectionMode is QTableView.SingleSelection, selection is not # cleared if deleted row was selected (at least one row is always selected) self.errorView.setSelectionMode(QTableView.SingleSelection) self.errorView.selectionModel().selectionChanged.connect( self.errorSelectionChanged) self.lrs.updateErrors.connect(self.updateErrors) self.resetErrorLayers() self.resetQualityLayer() # layers must be tested 'is not None' (because layers have __len__(), some strange len) if self.errorPointLayer is not None or self.errorLineLayer is not None or self.qualityLayer is not None: self.iface.mapCanvas().refresh() self.lrs.edited.connect(self.lrsEdited) self.resetGenButtons() self.resetLocateRoutes() self.resetEventsButtons() self.resetMeasureButtons() self.updateMeasureUnits() self.enableTabs() def isCalibrated(self): return self.lrs is not None and self.lrs.isCalibrated() def resetGenButtons(self): self.genCreateOutputButton.setEnabled( self.isCalibrated() and len(self.genOutputNameLineEdit.text().strip()) > 0) def createLrsOutput(self): output = LrsOutput(self.iface, self.lrs, self.showGenProgress) output.output(self.genOutputNameLineEdit.text().strip()) self.hideGenProgress() def showGenProgress(self, label, percent): self.genProgressFrame.show() self.genProgressLabel.setText(label) self.genProgressBar.setValue(percent) def hideGenProgress(self): self.genProgressFrame.hide() # ------------------------------- ERRORS ------------------------------- def updateErrors(self, errorUpdates): # #debug ( "updateErrors" ) # because SingleSelection does not allow to deselect row, we have to clear selection manually index = self.getSelectedErrorIndex() if index: rows = self.errorModel.rowsToBeRemoved(errorUpdates) selected = index.row() if selected in rows: self.errorView.selectionModel().clear() self.errorModel.updateErrors(errorUpdates) self.errorSelectionChanged() self.updateErrorLayers(errorUpdates) self.updateQualityLayer(errorUpdates) # def errorSelectionChanged(self, selected, deselected ): def errorSelectionChanged(self): error = self.getSelectedError() self.errorVisualizer.highlight(error, self.lrs.crs) self.errorZoomButton.setEnabled(error is not None) def getSelectedErrorIndex(self): sm = self.errorView.selectionModel() if not sm.hasSelection(): return None index = sm.selection().indexes()[0] index = self.sortErrorModel.mapToSource(index) return index def getSelectedError(self): index = self.getSelectedErrorIndex() if not index: return None return self.errorModel.getError(index) def errorZoom(self): error = self.getSelectedError() if not error: return self.errorVisualizer.zoom(error, self.lrs.crs) # add new error layers to map def addErrorLayers(self): project = QgsProject.instance() if self.errorLineLayer is None: self.errorLineLayer = LrsErrorLineLayer(self.lrs.crs) self.errorLineLayerManager = LrsErrorLayerManager( self.errorLineLayer) self.errorLineLayer.renderer().symbol().setColor(QColor(Qt.red)) self.resetErrorLineLayer() QgsProject.instance().addMapLayers([ self.errorLineLayer, ]) project.writeEntry(PROJECT_PLUGIN_NAME, "errorLineLayerId", self.errorLineLayer.id()) if self.errorPointLayer is None: self.errorPointLayer = LrsErrorPointLayer(self.lrs.crs) self.errorPointLayerManager = LrsErrorLayerManager( self.errorPointLayer) self.errorPointLayer.renderer().symbol().setColor(QColor(Qt.red)) self.resetErrorPointLayer() QgsProject.instance().addMapLayers([ self.errorPointLayer, ]) project.writeEntry(PROJECT_PLUGIN_NAME, "errorPointLayerId", self.errorPointLayer.id()) # reset error layers content (features) def resetErrorLayers(self): # #debug ( "resetErrorLayers" ) self.resetErrorPointLayer() self.resetErrorLineLayer() def updateErrorLayers(self, errorUpdates): if self.errorPointLayerManager: self.errorPointLayerManager.updateErrors(errorUpdates) if self.errorLineLayerManager: self.errorLineLayerManager.updateErrors(errorUpdates) def updateQualityLayer(self, errorUpdates): if self.qualityLayerManager: self.qualityLayerManager.update(errorUpdates) def resetErrorPointLayer(self): # debug("resetErrorPointLayer %s" % self.errorPointLayer) if self.errorPointLayerManager is None: return self.errorPointLayerManager.clear() errors = self.lrs.getErrors() self.errorPointLayerManager.addErrors(errors, self.lrs.crs) def resetErrorLineLayer(self): if self.errorLineLayerManager is None: return self.errorLineLayerManager.clear() errors = self.lrs.getErrors() self.errorLineLayerManager.addErrors(errors, self.lrs.crs) def addQualityLayer(self): if not self.qualityLayer: self.qualityLayer = LrsQualityLayer(self.lrs.crs) self.qualityLayerManager = LrsQualityLayerManager( self.qualityLayer) self.resetQualityLayer() QgsProject.instance().addMapLayers([ self.qualityLayer, ]) project = QgsProject.instance() project.writeEntry(PROJECT_PLUGIN_NAME, "qualityLayerId", self.qualityLayer.id()) def resetQualityLayer(self): # #debug ( "resetQualityLayer %s" % self.qualityLayer ) if not self.qualityLayerManager: return self.qualityLayerManager.clear() features = self.lrs.getQualityFeatures() self.qualityLayerManager.addFeatures(features, self.lrs.crs) # ------------------------ common layer for LOCATE, EVENTS, MEASURES ------------- def showLrsLayerProgressBar(self): self.locateProgressBar.show() self.eventsProgressBar.show() self.measureProgressBar.show() def hideLrsLayerProgressBar(self): self.locateProgressBar.hide() self.eventsProgressBar.hide() self.measureProgressBar.hide() def loadLrsLayerProgress(self, percent): self.locateProgressBar.setValue(percent) self.eventsProgressBar.setValue(percent) self.measureProgressBar.setValue(percent) # ------------------------------------ LOCATE ------------------------------------ def resetLocateOptions(self): self.locateHighlightWM.reset() self.locateBufferWM.reset() def readLocateOptions(self): self.locateHighlightWM.readFromProject() self.locateBufferWM.readFromProject() def resetLocateRoutes(self): # debug("resetLocateRoutes lrsLayer: %s" % (self.lrsLayer if self.lrsLayer else None)) options = [(None, '')] if self.lrsLayer: options.extend([(id, "%s" % id) for id in self.lrsLayer.getRouteIds()]) # debug("resetLocateRoutes options: %s" % options) self.locateRouteCM.setOptions(options) def locateRouteChanged(self): # #debug ('locateRouteChanged') rangesText = '' routeId = self.locateRouteCM.value() if self.lrsLayer and routeId is not None: ranges = [] for r in self.lrsLayer.getRouteMeasureRanges(routeId): rang = "%s-%s" % ( formatMeasure(r[0], self.lrsLayer.measureUnit), formatMeasure(r[1], self.lrsLayer.measureUnit)) ranges.append(rang) rangesText = ", ".join(ranges) # #debug ('ranges: %s' % rangesText ) self.locateRanges.setText(rangesText) self.resetLocateEvent() def locateBufferChanged(self): self.locateBufferWM.writeToProject() def resetLocateEvent(self): self.clearLocateHighlight() routeId = self.locateRouteCM.value() measure = self.locateMeasureSpin.value() coordinates = '' point = None if routeId is not None: point, error = self.lrsLayer.eventPointXY(routeId, measure) if point: mapSettings = self.iface.mapCanvas().mapSettings() if isProjectCrsEnabled( ) and getProjectCrs() != self.lrsLayer.crs: transform = QgsCoordinateTransform( self.lrsLayer.crs, mapSettings.destinationCrs(), QgsProject.instance()) point = transform.transform(point) coordinates = "%.3f,%.3f" % (point.x(), point.y()) else: coordinates = error self.locatePoint = point # QgsPointXY self.highlightLocatePoint() self.locateCoordinates.setText(coordinates) self.locateCenterButton.setEnabled(bool(point)) self.locateZoomButton.setEnabled(bool(point)) def locateHighlightChanged(self): # #debug ('locateHighlightChanged') self.clearLocateHighlight() self.locateHighlightWM.writeToProject() self.highlightLocatePoint() def highlightLocatePoint(self): # #debug ('highlightLocatePoint') self.clearLocateHighlight() if not self.locatePoint: return if not self.locateHighlightCheckBox.isChecked(): return mapCanvas = self.iface.mapCanvas() mapSettings = mapCanvas.mapSettings() # QgsHighlight does reprojection from layer CRS crs = getProjectCrs() if isProjectCrsEnabled() else self.lrsLayer.crs layer = QgsVectorLayer('Point?crs=' + crsString(crs), 'LRS locate highlight', 'memory') #self.locateHighlight = QgsHighlight(mapCanvas, QgsGeometry.fromPoint(self.locatePoint), layer) # QgsGeometry(QgsPoint) takes ownership! self.locateHighlight = QgsHighlight( mapCanvas, QgsGeometry(QgsPoint(self.locatePoint)), layer) # highlight point size is hardcoded in QgsHighlight self.locateHighlight.setWidth(2) self.locateHighlight.setColor(Qt.yellow) self.locateHighlight.show() def clearLocateHighlight(self): # #debug ('clearLocateHighlight') if self.locateHighlight: del self.locateHighlight self.locateHighlight = None def locateCenter(self): if not self.locatePoint: return mapCanvas = self.iface.mapCanvas() extent = mapCanvas.extent() extent.scale(1.0, self.locatePoint.x(), self.locatePoint.y()) self.iface.mapCanvas().setExtent(extent) self.iface.mapCanvas().refresh() def locateZoom(self): if not self.locatePoint: return p = self.locatePoint b = self.locateBufferSpin.value() extent = QgsRectangle(p.x() - b, p.y() - b, p.x() + b, p.y() + b) self.iface.mapCanvas().setExtent(extent) self.iface.mapCanvas().refresh() # ---------------------------------- EVENTS ---------------------------------- def resetEventsOptions(self): self.eventsLayerCM.reset() self.eventsRouteFieldCM.reset() self.eventsMeasureStartFieldCM.reset() self.eventsMeasureEndFieldCM.reset() self.eventsFeaturesSelectCM.reset() self.eventsOutputNameWM.reset() self.eventsErrorFieldWM.reset() self.resetEventsButtons() def resetEventsOptionsAndWrite(self): self.resetEventsOptions() self.writeEventsOptions() def resetEventsButtons(self): enabled = bool(self.lrsLayer) and self.eventsLayerCombo.currentIndex( ) != -1 and self.eventsRouteFieldCombo.currentIndex( ) != -1 and self.eventsMeasureStartFieldCombo.currentIndex( ) != -1 and bool(self.eventsOutputNameLineEdit.text()) self.eventsButtonBox.button(QDialogButtonBox.Ok).setEnabled(enabled) # save settings in project def writeEventsOptions(self): self.eventsLayerCM.writeToProject() self.eventsRouteFieldCM.writeToProject() self.eventsMeasureStartFieldCM.writeToProject() self.eventsMeasureEndFieldCM.writeToProject() self.eventsFeaturesSelectCM.writeToProject() self.eventsOutputNameWM.writeToProject() self.eventsErrorFieldWM.writeToProject() def readEventsOptions(self): self.eventsLayerCM.readFromProject() self.eventsRouteFieldCM.readFromProject() self.eventsMeasureStartFieldCM.readFromProject() self.eventsMeasureEndFieldCM.readFromProject() self.eventsFeaturesSelectCM.readFromProject() self.eventsOutputNameWM.readFromProject() self.eventsErrorFieldWM.readFromProject() def createEvents(self): self.writeEventsOptions() self.eventsProgressBar.show() layer = self.eventsLayerCM.getLayer() routeFieldName = self.eventsRouteFieldCM.getFieldName() startFieldName = self.eventsMeasureStartFieldCM.getFieldName() endFieldName = self.eventsMeasureEndFieldCM.getFieldName() featuresSelect = self.eventsFeaturesSelectCM.value() outputName = self.eventsOutputNameLineEdit.text() if not outputName: outputName = self.eventsOutputNameWM.defaultValue() errorFieldName = self.eventsErrorFieldLineEdit.text() events = LrsEvents(self.lrsLayer, self.eventsProgressBar) events.create(layer, featuresSelect, routeFieldName, startFieldName, endFieldName, errorFieldName, outputName) # ------------------- MEASURE ------------------- def resetMeasureOptions(self): # #debug('resetMeasureOptions') self.measureLayerCM.reset() self.measureRouteFieldCM.reset() self.measureThresholdWM.reset() self.measureOutputNameWM.reset() self.measureOutputRouteFieldWM.reset() self.measureMeasureFieldWM.reset() self.resetMeasureButtons() def resetMeasureOptionsAndWrite(self): self.resetMeasureOptions() self.writeMeasureOptions() def resetMeasureButtons(self): # #debug('resetMeasureButtons') enabled = bool(self.lrsLayer) and self.measureLayerCombo.currentIndex( ) != -1 and bool(self.measureOutputNameLineEdit.text()) and bool( self.measureOutputRouteFieldLineEdit.text()) and bool( self.measureMeasureFieldLineEdit.text()) self.measureButtonBox.button(QDialogButtonBox.Ok).setEnabled(enabled) # save settings in project def writeMeasureOptions(self): self.measureLayerCM.writeToProject() self.measureRouteFieldCM.writeToProject() self.measureThresholdWM.writeToProject() self.measureOutputNameWM.writeToProject() self.measureOutputRouteFieldWM.writeToProject() self.measureMeasureFieldWM.writeToProject() def readMeasureOptions(self): self.measureLayerCM.readFromProject() self.measureRouteFieldCM.readFromProject() self.measureThresholdWM.readFromProject() self.measureOutputNameWM.readFromProject() self.measureOutputRouteFieldWM.readFromProject() self.measureMeasureFieldWM.readFromProject() # set threshold units according to current crs def updateMeasureUnits(self): crs = self.lrsLayer.crs if self.lrsLayer else None label = self.getThresholdLabel(crs) # debug('updateMeasureUnits label = %s' % label) self.measureThresholdLabel.setText(label) def calculateMeasures(self): # #debug('calculateMeasures') self.writeMeasureOptions() self.measureProgressBar.show() layer = self.measureLayerCM.getLayer() routeFieldName = self.measureRouteFieldCM.getFieldName() threshold = self.measureThresholdSpin.value() outputName = self.measureOutputNameLineEdit.text() if not outputName: outputName = self.measureOutputNameWM.defaultValue() outputRouteFieldName = self.measureOutputRouteFieldLineEdit.text() measureFieldName = self.measureMeasureFieldLineEdit.text() measures = LrsMeasures(self.iface, self.lrsLayer, self.measureProgressBar) measures.calculate(layer, routeFieldName, outputRouteFieldName, measureFieldName, threshold, outputName) # ------------------- STATS ------------------- def resetStats(self): # debug ( 'setStats' ) # html = '' if self.lrs: # if self.lrs.getEdited(): # html = 'Statistics are not available if an input layer has been edited after calibration. Run calibration again to get fresh statistics.' # else: # html = self.lrs.getStatsHtml() # self.statsTextEdit.setHtml(html) self.errorTotalLength.setText("%.3f" % self.lrs.getStat('length')) self.errorIncludedLength.setText( "%.3f" % self.lrs.getStat('lengthIncluded')) self.errorSuccessLength.setText("%.3f" % self.lrs.getStat('lengthOk')) # -------------------- widget ---------------------- def saveWidgetGeometry(self): # debug("LrsDockWidget.saveWidgetGeometry") settings = QgsSettings() settings.setValue("/Windows/lrs/geometry", self.saveGeometry()) def restoreWidgetGeometry(self): # debug("LrsDockWidget.restoreWidgetGeometry") settings = QgsSettings() self.restoreGeometry( settings.value("/Windows/lrs/geometry", QByteArray()))