class PlaceMarkerMapTool(QgsMapToolEmitPoint): ''' classdocs ''' def __init__(self, canvas): ''' Constructor ''' self.canvas = canvas super(PlaceMarkerMapTool, self).__init__(self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.reset() def reset(self): self.rubberBand.reset(QGis.Polygon) def canvasReleaseEvent(self, e): p1 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() - 2, e.y() - 2) p2 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() + 2, e.y() - 2) p3 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() + 2, e.y() + 2) p4 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() - 2, e.y() + 2) self.reset() self.rubberBand.addPoint(p1, False) self.rubberBand.addPoint(p2, False) self.rubberBand.addPoint(p3, False) self.rubberBand.addPoint(p4, True) self.rubberBand.show() def deactivate(self): self.reset() super(PlaceMarkerMapTool, self).deactivate()
class LineDrawer(QgsMapToolEmitPoint): def __init__(self, canvas): # call the parent constructor QgsMapToolEmitPoint.__init__(self, canvas) # store the passed canvas self.canvas = canvas # flag to know whether the tool is performing a drawing operation self.isDrawing = False # create and setup the rubber band to display the line self.rubberBand = QgsRubberBand( self.canvas, False ) # False = not a polygon = a line self.rubberBand.setColor( Qt.red ) self.rubberBand.setWidth( 1 ) def clear(self): self.rubberBand.reset( False ) # False = not a polygon = a line def delete(self): self.canvas.scene().removeItem( self.rubberBand ) def canvasPressEvent(self, e): # which the mouse button? if e.button() == Qt.LeftButton: # left click # if it's the first left click, clear the rubberband if not self.isDrawing: self.clear() # we are drawing now self.isDrawing = True # convert the clicked position to map coordinates point = self.toMapCoordinates( e.pos() ) # add a new point to the rubber band self.rubberBand.addPoint( point, True ) # True = display updates on the canvas # and finally show the rubber band self.rubberBand.show() elif e.button() == Qt.RightButton: # right click, stop drawing self.isDrawing = False # emit a signal self.emit( SIGNAL("editingFinished()") ) def canvasMoveEvent(self, e): # check if it's already drawing if not self.isDrawing: return # convert the mouse position to map coordinates point = self.toMapCoordinates( e.pos() ) # move the last point to the new coordinates self.rubberBand.movePoint( point ) def geometry(self): return self.rubberBand.asGeometry()
class GetPointMapTool(QgsMapToolEmitPoint): coordCaptured = "" def __init__(self, canvas, iface, dockwidget, currentMapTool): self.canvas = canvas self.iface = iface self.currentMapTool = currentMapTool self.dockwidget = dockwidget QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Point) self.rubberBand.setColor(QColor(255,5,5)) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.point = self.toMapCoordinates(e.pos()) self.isEmittingPoint = True self.showPoint(self.point) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.coordCaptured = self.pointdef() if self.coordCaptured is not None: print "Point:", self.coordCaptured self.coordCaptured = str(self.coordCaptured).strip("(") self.coordCaptured = str(self.coordCaptured).strip(")") self.dockwidget.munLineEdit.setText(self.coordCaptured) self.iface.mapCanvas().setMapTool(self.currentMapTool) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) # self.showRect(self.startPoint, self.endPoint) def showPoint(self, point): self.rubberBand.reset(QGis.Polygon) point1 = QgsPoint(point.x(), point.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.show() def pointdef(self): return QgsPoint(self.point)
def _createRubberBand(self, geometryType, moveBand=False): settings = QSettings() rb = QgsRubberBand(self.canvas(), geometryType) rb.setWidth(int(settings.value('/qgis/digitizing/line_width', 1))) color = QColor(int(settings.value('/qgis/digitizing/line_color_red', 255)), int(settings.value('/qgis/digitizing/line_color_green', 0)), int(settings.value('/qgis/digitizing/line_color_blue', 0))) myAlpha = int(settings.value('/qgis/digitizing/line_color_alpha', 200)) / 255.0 if (moveBand): myAlpha = myAlpha * float(settings.value('/qgis/digitizing/line_color_alpha_scale', 0.75)) rb.setLineStyle(Qt.DotLine) if (geometryType == QGis.Polygon): color.setAlphaF(myAlpha) color.setAlphaF(myAlpha) rb.setColor(color) rb.show() return rb
def highlight(self,geometry): def processEvents(): try: qApp.processEvents() except: QApplication.processEvents() highlight = QgsRubberBand(self.canvas, geometry.type()) highlight.setColor(QColor("#36AF6C")) highlight.setFillColor(QColor("#36AF6C")) highlight.setWidth(2) highlight.setToGeometry(geometry,self.canvas.currentLayer()) processEvents() sleep(.1) highlight.hide() processEvents() sleep(.1) highlight.show() processEvents() sleep(.1) highlight.reset() processEvents()
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), self.canvas) self.bridge.setAutoSetupOnFirstLayer(False) QgsProject.instance().writeProject.connect(self.bridge.writeProject) QgsProject.instance().readProject.connect(self.bridge.readProject) # self.canvas.setInteractive(False) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.snapping = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snapping) QgsProject.instance().readProject.connect(self.snapping.readConfigFromProject) if hasattr(self.canvas, 'setParallelRenderingEnabled'): threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) if roam.config.settings.get('north_arrow', False): self.northarrow = NorthArrow(":/icons/north", self.canvas) self.northarrow.setPos(10, 10) self.canvas.scene().addItem(self.northarrow) self.scalebar_enabled = roam.config.settings.get('scale_bar', False) if self.scalebar_enabled: self.scalebar = ScaleBarItem(self.canvas) self.canvas.scene().addItem(self.scalebar) self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 50)) self.currentfeatureband.setOutlineColour(QColor(186, 93, 212)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.openfeatureform.connect(self.feature_form_loaded) RoamEvents.sync_complete.connect(self.refresh_map) RoamEvents.snappingChanged.connect(self.snapping_changed) self.snappingbutton = QToolButton() self.snappingbutton.setText("Snapping: On") self.snappingbutton.setAutoRaise(True) self.snappingbutton.pressed.connect(self.toggle_snapping) spacer = QWidget() spacer2 = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.scalewidget = QgsScaleComboBox() self.scalebutton = QToolButton() self.scalebutton.setAutoRaise(True) self.scalebutton.setMaximumHeight(self.statusbar.height()) self.scalebutton.pressed.connect(self.selectscale) self.scalebutton.setText("Scale") self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False) self.scalelist.hide() self.scalelist.setlabel("Map Scale") self.scalelist.setmodel(self.scalewidget.model()) self.scalelist.closewidget.connect(self.scalelist.close) self.scalelist.itemselected.connect(self.update_scale_from_item) self.scalelist.itemselected.connect(self.scalelist.close) self.positionlabel = QLabel('') self.gpslabel = QLabel("GPS: Not active") self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.updatestatuslabel) self.canvas.scaleChanged.connect(self.updatestatuslabel) GPS.gpsposition.connect(self.update_gps_label) GPS.gpsdisconnected.connect(self.gps_disconnected) self.connectButtons() def clear_plugins(self): toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames): for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'toolbars'): roam.utils.warning("No toolbars() function found in {}".format(name)) continue toolbars = plugin_mod.toolbars() self.load_plugin_toolbars(toolbars) def load_plugin_toolbars(self, toolbars): for ToolBarClass in toolbars: toolbar = ToolBarClass(plugins.api, self) self.addToolBar(Qt.BottomToolBarArea, toolbar) toolbar.setProperty("plugin_toolbar", True) def snapping_changed(self, snapping): """ Called when the snapping settings have changed. Updates the label in the status bar. :param snapping: """ if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ snap = not snapping global snapping snapping = snap RoamEvents.snappingChanged.emit(snapping) def selectscale(self): """ Show the select scale widget. :return: """ self.scalelist.show() def update_scale_from_item(self, index): """ Update the canvas scale from the selected scale item. :param index: The index of the selected item. """ scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole)) self.canvas.zoomScale(1.0 / scale) def update_gps_label(self, position, gpsinfo): """ Update the GPS label in the status bar with the GPS status. :param position: The current GPS position. :param gpsinfo: The current extra GPS information. """ self.gpslabel.setText("GPS: PDOP {0:.2f} HDOP {1:.2f} VDOP {2:.2f}".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) def gps_disconnected(self): """ Called when the GPS is disconnected. Updates the label in the status bar with the message. :return: """ self.gpslabel.setText("GPS Not Active") def updatestatuslabel(self, *args): """ Update the status bar labels when the information has changed. """ extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format(extent.center().toString())) scale = 1.0 / self.canvas.scale() scale = self.scalewidget.toString(scale) self.scalebutton.setText(scale) def refresh_map(self): """ Refresh the map """ self.canvas.refresh() def updatescale(self): """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) def init_qgisproject(self, doc): """ Called when the project file is read for the firs time. :param doc: The XML doc. :return: The current canvas CRS :note: This method is old and needs to be refactored into something else. """ return self.canvas.mapSettings().destinationCrs() def showEvent(self, *args, **kwargs): """ Handle the show event of the of the map widget. We have to do a little hack here to make the QGIS map refresh. """ if QGis.QGIS_VERSION_INT == 20200 and self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, *args): """ Called when the feature form is loaded. :param form: The Form object. Holds a reference to the forms layer. :param feature: The current capture feature """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): """ Highlight the selection on the canvas. This updates all selected objects based on the result set. :param results: A dict-of-list of layer-features. """ self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0)) band.setIconSize(25) band.setWidth(5) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) band.setZValue(self.currentfeatureband.zValue() - 1) for feature in features: band.addGeometry(feature.geometry(), layer) self.canvas.update() def highlight_active_selection(self, layer, feature, features): """ Update the current active selected feature. :param layer: The layer of the active feature. :param feature: The active feature. :param features: The other features in the set to show as non active selection. :return: """ self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) self.canvas.update() def clear_selection(self): """ Clear the selection from the canvas. Resets all selection rubbber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.canvas.update() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): """ Push a feature on the edit stack so the feature can have the geometry edited. :note: This is a big hack and I don't like it! :param form: The form for the current feature :param feature: The active feature. """ def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() self.canvas.mapTool().setEditMode(True, feature.geometry()) break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): """ Clear all temp objects from the canvas. :return: """ def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): """ Called when the settings have been updated in the Roam config. :param settings: A dict of the settings. """ self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): """ Updates the map canvas based on the GPS position. By default if the GPS is outside the canvas extent the canvas will move to center on the GPS. Can be turned off in settings. :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position, gpsinfo) def gps_first_fix(self, postion, gpsinfo): """ Called the first time the GPS gets a fix. If set this will zoom to the GPS after the first fix :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): """ Zoom to ta given position on the map.. """ rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): """ Called when the GPS is disconnected """ self.marker.hide() def select_data_entry(self): """ Open the form selection widget to allow the user to pick the active capture form. """ def showformerror(form): pass def actions(): for form in self.project.forms: if not self.form_valid_for_capture(form): continue action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form", wrap=5) formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): """ Called when the project is loaded. Main entry point for a loade project. :param project: The Roam project that has been loaded. """ self.project = project self.actionPan.trigger() firstform = self.first_capture_form() if firstform: self.load_form(firstform) self.dataentryselection.setVisible(True) else: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.refresh() projectscales, _ = QgsProject.instance().readBoolEntry("Scales", "/useProjectScales") if projectscales: projectscales, _ = QgsProject.instance().readListEntry("Scales", "/ScalesList") self.scalewidget.updateScales(projectscales) else: scales = ["1:50000", "1:25000", "1:10000", "1:5000", "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100"] scales = roam.config.settings.get('scales', scales) self.scalewidget.updateScales(scales) if self.scalebar_enabled: self.scalebar.update() self.actionPan.toggle() self.clear_plugins() self.add_plugins(project.enabled_plugins) def setMapTool(self, tool, *args): """ Set the active map tool in the canvas. :param tool: The QgsMapTool to set. """ if tool == self.canvas.mapTool(): return if hasattr(tool, "setSnapping"): tool.setSnapping(snapping) self.canvas.setMapTool(tool) def connectButtons(self): """ Connect the default buttons in the interface. Zoom, pan, etc """ def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/select')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def form_valid_for_capture(self, form): """ Check if the given form is valid for capture. :param form: The form to check. :return: True if valid form for capture """ return form.has_geometry and self.project.layer_can_capture(form.QGISLayer) def first_capture_form(self): """ Return the first valid form for capture. """ for form in self.project.forms: if self.form_valid_for_capture(form): return form def load_form(self, form): """ Load the given form so it's the active one for capture :param form: The form to load """ self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ layer = form.QGISLayer tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass feature = form.new_feature(geometry=geometry) RoamEvents.load_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) self.canvas.mapTool().setEditMode(False, None) def clearCapatureTools(self): """ Clear the capture tools from the toolbar. :return: True if the capture button was active at the time of clearing. """ captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh self.canvas.freeze() tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): if node.layer().type() == QgsMapLayer.RasterLayer: if node.isVisible() == Qt.Checked: state = Qt.Unchecked else: state = Qt.Checked node.setVisible(state) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class RectangleMapTool(QgsMapToolEmitPoint): rectangleCreated = pyqtSignal() deactivated = pyqtSignal() def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubberBand.setColor(QColor(255, 0, 0, 100)) self.rubberBand.setWidth(2) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False if self.rectangle() is not None: self.rectangleCreated.emit() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(startPoint.x(), endPoint.y()) point3 = QgsPointXY(endPoint.x(), endPoint.y()) point4 = QgsPointXY(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) # True to update canvas self.rubberBand.addPoint(point4, True) self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or \ self.startPoint.y() == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def setRectangle(self, rect): if rect == self.rectangle(): return False if rect is None: self.reset() else: self.startPoint = QgsPointXY(rect.xMaximum(), rect.yMaximum()) self.endPoint = QgsPointXY(rect.xMinimum(), rect.yMinimum()) self.showRect(self.startPoint, self.endPoint) return True def deactivate(self): QgsMapTool.deactivate(self) self.deactivated.emit()
class GeorefRasterBy2PointsMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.firstPoint = None self.rubberBandOrigin = QgsRubberBand( self.canvas, QgsWkbTypes.PointGeometry) self.rubberBandOrigin.setColor(Qt.red) self.rubberBandOrigin.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubberBandOrigin.setIconSize(7) self.rubberBandOrigin.setWidth(2) self.rubberBandDisplacement = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.rubberBandExtent = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(2) self.isLayerVisible = True self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = self.firstPoint = None self.isEmittingPoint = False self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def deactivate(self): QgsMapToolEmitPoint.deactivate(self) self.reset() def canvasPressEvent(self, e): if self.firstPoint is None: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.originalCenter = self.layer.center # this tool do the displacement itself TODO update so it is done by # transformed coordinates + new center) self.originalCornerPoints = \ self.layer.transformedCornerCoordinates( *self.layer.transformParameters()) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) self.showDisplacement(self.startPoint, self.endPoint) self.layer.history.append({"action": "2pointsA", "center": self.layer.center}) else: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.startY = e.pos().y() self.endY = self.startY self.isEmittingPoint = True self.height = self.canvas.height() self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) self.layer.history.append( {"action": "2pointsB", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale, "rotation": self.layer.rotation}) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() if self.firstPoint is None: x = (self.originalCenter.x() + self.endPoint.x() - self.startPoint.x()) y = (self.originalCenter.y() + self.endPoint.y() - self.startPoint.y()) self.layer.setCenter(QgsPointXY(x, y)) self.firstPoint = self.endPoint setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() else: rotation = self.computeRotation() xScale = yScale = self.computeScale() self.layer.moveCenterFromPointRotate( self.firstPoint, rotation, xScale, yScale) self.layer.setRotation(self.layer.rotation + rotation) self.layer.setScale(self.layer.xScale * xScale, self.layer.yScale * yScale) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rasterShadow.reset() self.firstPoint = None self.startPoint = self.endPoint = None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) if self.firstPoint is None: self.showDisplacement(self.startPoint, self.endPoint) else: self.endY = e.pos().y() rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) def computeRotation(self): # The angle is the difference between angle # horizontal/endPoint-firstPoint and horizontal/startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.degrees(math.atan2(-dY, dX) - math.atan2(-dY0, dX0)) def computeScale(self): # The scale is the ratio between endPoint-firstPoint and # startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.sqrt((dX * dX + dY * dY) / (dX0 * dX0 + dY0 * dY0)) def showRotationScale(self, rotation, xScale, yScale): center, _, _, _ = self.layer.transformParameters() # newRotation = rotation + originalRotation cornerPoints = self.layer.transformedCornerCoordinatesFromPoint( self.firstPoint, rotation, xScale, yScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() # Calculate the displacement of the center due to the rotation from # another point. newCenterDX = (cornerPoints[0].x() + cornerPoints[2].x()) / 2 - center.x() newCenterDY = (cornerPoints[0].y() + cornerPoints[2].y()) / 2 - center.y() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(newCenterDX, newCenterDY, False) self.rasterShadow.setDeltaScale(xScale, yScale, False) self.rasterShadow.setDeltaRotation(rotation, True) self.rasterShadow.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point0 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point1 = QgsPointXY(self.firstPoint.x(), self.firstPoint.y()) point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) self.rubberBandDisplacement.addPoint(point0, False) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint( point2, True) # true to update canvas self.rubberBandDisplacement.show() def showDisplacement(self, startPoint, endPoint): self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandOrigin.addPoint(endPoint, True) self.rubberBandOrigin.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(endPoint.x(), endPoint.y()) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint( point2, True) # true to update canvas self.rubberBandDisplacement.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in self.originalCornerPoints: self._addDisplacementToPoint(self.rubberBandExtent, point, False) # for closing self._addDisplacementToPoint( self.rubberBandExtent, self.originalCornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(self.endPoint.x( ) - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True) self.rasterShadow.show() def _addDisplacementToPoint(self, rubberBand, point, doUpdate): x = point.x() + self.endPoint.x() - self.startPoint.x() y = point.y() + self.endPoint.y() - self.startPoint.y() self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
class SelectDownloadExtentMapTool(QgsMapTool): def __init__(self, canvas, dialog): self.canvas = canvas self.dialog = dialog QgsMapTool.__init__(self, self.canvas) self.setCursor(Qt.CrossCursor) self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.dialog.setExtent(self.rectangle()) self.dialog.showNormal() self.dialog.raise_() self.dialog.activateWindow() self.reset() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QGis.Polygon) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y( ) == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint)
class RectangleMapTool(QgsMapToolEmitPoint): """ Map tool that lets the user define the analysis extents. """ rectangle_created = pyqtSignal() deactivated = pyqtSignal() def __init__(self, canvas): """Constructor for the map tool. :param canvas: Canvas that tool will interact with. :type canvas: QgsMapCanvas """ self.canvas = canvas self.start_point = None self.end_point = None self.is_emitting_point = False QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubber_band = QgsRubberBand(self.canvas, geometryType=QGis.Line) self.rubber_band.setColor(QColor(0, 0, 240, 100)) # Needs QGIS 2.6 # self.rubber_band.setFillColor(QColor(0, 0, 240, 0)) self.rubber_band.setWidth(1) self.reset() def reset(self): """ Clear the rubber band for the analysis extents. """ self.start_point = self.end_point = None self.is_emitting_point = False self.rubber_band.reset(QGis.Polygon) def canvasPressEvent(self, e): """ Handle canvas press events so we know when user is capturing the rect. :param e: A Qt event object. :type: QEvent """ self.start_point = self.toMapCoordinates(e.pos()) self.end_point = self.start_point self.is_emitting_point = True self.show_rectangle(self.start_point, self.end_point) def canvasReleaseEvent(self, e): """ Handle canvas release events has finished capturing e :param e: A Qt event object. :type: QEvent """ _ = e self.is_emitting_point = False self.rectangle_created.emit() def canvasMoveEvent(self, e): """ :param e: :return: """ if not self.is_emitting_point: return self.end_point = self.toMapCoordinates(e.pos()) self.show_rectangle(self.start_point, self.end_point) def show_rectangle(self, start_point, end_point): """ Show the rectangle on the canvas. :param start_point: QGIS Point object representing the origin ( top left). :type start_point: QgsPoint :param end_point: QGIS Point object representing the contra-origin ( bottom right). :type end_point: QgsPoint :return: """ self.rubber_band.reset(QGis.Polygon) if (start_point.x() == end_point.x() or start_point.y() == end_point.y()): return point1 = start_point point2 = QgsPoint(end_point.x(), start_point.y()) point3 = end_point point4 = QgsPoint(start_point.x(), end_point.y()) update_canvas = False self.rubber_band.addPoint(point1, update_canvas) self.rubber_band.addPoint(point2, update_canvas) self.rubber_band.addPoint(point3, update_canvas) self.rubber_band.addPoint(point4, update_canvas) # noinspection PyArgumentEqualDefault # no False so canvas will update # close the polygon otherwise it shows as a filled rect self.rubber_band.addPoint(point1) self.rubber_band.show() def rectangle(self): """ Accessor for the rectangle. :return: A rectangle showing the designed extent. :rtype: QgsRectangle """ if self.start_point is None or self.end_point is None: return None elif self.start_point.x() == self.end_point.x() or \ self.start_point.y() == self.end_point.y(): return None return QgsRectangle(self.start_point, self.end_point) def set_rectangle(self, rectangle): """ Set the rectangle for the selection. :param rectangle: :return: """ if rectangle == self.rectangle(): return False if rectangle is None: self.reset() else: self.start_point = QgsPoint( rectangle.xMinimum(), rectangle.yMinimum()) self.end_point = QgsPoint( rectangle.xMaximum(), rectangle.yMaximum()) self.show_rectangle(self.start_point, self.end_point) return True def deactivate(self): """ Disable the tool. """ QgsMapTool.deactivate(self) self.deactivated.emit()
class MultiLayerSelection(QgsMapTool): finished = QtCore.pyqtSignal(list) def __init__(self, canvas, iface): """ Tool Behaviours: (all behaviours start edition, except for rectangle one) 1- Left Click: Clears previous selection, selects feature, sets feature layer as active layer. The selection is done with the following priority: Point, Line then Polygon. Selection is only done in visible layer. 2- Control + Left Click: Adds to selection selected feature. This selection follows the priority in item 1. 3- Right Click: Opens feature form 4- Control + Right Click: clears selection and set feature's layer as activeLayer. activeLayer's definition follows priority of item 1; 5- Shift + drag and drop: draws a rectangle, then features that intersect this rectangl'e are added to selection """ self.iface = iface self.canvas = canvas self.toolAction = None QgsMapTool.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.hoverRubberBand = QgsRubberBand(self.canvas, QGis.Polygon) mFillColor = QColor( 254, 178, 76, 63 ) self.rubberBand.setColor(mFillColor) self.hoverRubberBand.setColor(QColor( 255, 0, 0, 90 )) self.rubberBand.setWidth(1) self.reset() self.blackList = self.getBlackList() self.cursorChanged = False self.cursorChangingHotkey = QtCore.Qt.Key_Alt self.menuHovered = False # indicates hovering actions over context menu def keyPressEvent(self, e): """ Reimplemetation of keyPressEvent() in order to handle cursor changing hotkey (Alt). """ if e.key() == self.cursorChangingHotkey and not self.cursorChanged: self.cursorChanged = True QtGui.QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor)) else: self.cursorChanged = False QtGui.QApplication.restoreOverrideCursor() def getBlackList(self): settings = QSettings() settings.beginGroup('PythonPlugins/DsgTools/Options') valueList = settings.value('valueList') if valueList: valueList = valueList.split(';') return valueList else: return ['moldura'] def reset(self): """ Resets rubber band. """ self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Polygon) def keyPressEvent(self, e): """ Reimplemetation of keyPressEvent() in order to handle cursor changing hotkey (F2). """ if e.key() == self.cursorChangingHotkey and not self.cursorChanged: self.cursorChanged = True QtGui.QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor)) else: self.cursorChanged = False QtGui.QApplication.restoreOverrideCursor() def canvasMoveEvent(self, e): """ Used only on rectangle select. """ if self.menuHovered: # deactivates rubberband when the context menu is "destroyed" self.hoverRubberBand.reset(QGis.Polygon) if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates( e.pos() ) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): """ Builds rubberband rect. """ self.rubberBand.reset(QGis.Polygon) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def rectangle(self): """ Builds rectangle from self.startPoint and self.endPoint """ if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def setAction(self, action): self.toolAction = action self.toolAction.setCheckable(True) def canvasReleaseEvent(self, e): """ After the rectangle is built, here features are selected. """ if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier: firstGeom = self.checkSelectedLayers() self.isEmittingPoint = False r = self.rectangle() if r is None: return layers = self.canvas.layers() for layer in layers: #ignore layers on black list and features that are not vector layers if not isinstance(layer, QgsVectorLayer) or (self.layerHasPartInBlackList(layer.name())): continue if firstGeom is not None and layer.geometryType() != firstGeom: # if there are features already selected, shift will only get the same type geometry # if more than one ty of geometry is present, only the strongest will be selected continue #builds bbRect and select from layer, adding selection bbRect = self.canvas.mapSettings().mapToLayerCoordinates(layer, r) layer.select(bbRect, True) self.rubberBand.hide() def canvasPressEvent(self, e): """ Method used to build rectangle if shift is held, otherwise, feature select/deselect and identify is done. """ if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier: self.isEmittingPoint = True self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) else: self.isEmittingPoint = False self.createContextMenu(e) def getCursorRect(self, e): """ Calculates small cursor rectangle around mouse position. Used to facilitate operations """ p = self.toMapCoordinates(e.pos()) w = self.canvas.mapUnitsPerPixel() * 10 return QgsRectangle(p.x()-w, p.y()-w, p.x()+w, p.y()+w) def layerHasPartInBlackList(self, lyrName): """ Verifies if terms in black list appear on lyrName """ for item in self.getBlackList(): if item.lower() in lyrName.lower(): return True return False def getPrimitiveDict(self, e, hasControlModifier=False): """ Builds a dict with keys as geometryTypes of layer, which are QGis.Point (value 0), QGis.Line (value 1) or QGis.Polygon (value 2), and values as layers from self.iface.legendInterface().layers(). When self.iface.legendInterface().layers() is called, a list of layers ordered according to lyr order in TOC is returned. """ #these layers are ordered by view order primitiveDict = dict() firstGeom = self.checkSelectedLayers() for lyr in self.iface.legendInterface().layers(): #ordered layers #layer types other than VectorLayer are ignored, as well as layers in black list and layers that are not visible if (lyr.type() != QgsMapLayer.VectorLayer) or (self.layerHasPartInBlackList(lyr.name())) or not self.iface.legendInterface().isLayerVisible(lyr): continue if hasControlModifier and (not firstGeom) and (not primitiveDict.keys() or lyr.geometryType() < firstGeom): firstGeom = lyr.geometryType() geomType = lyr.geometryType() if geomType not in primitiveDict.keys(): primitiveDict[geomType] = [] #removes selection if (not hasControlModifier and e.button() == QtCore.Qt.LeftButton) or (hasControlModifier and e.button() == QtCore.Qt.RightButton): lyr.removeSelection() primitiveDict[geomType].append(lyr) if hasControlModifier and firstGeom in [0, 1, 2]: return { firstGeom : primitiveDict[firstGeom] } else: return primitiveDict def deactivate(self): """ Deactivate tool. """ QtGui.QApplication.restoreOverrideCursor() self.hoverRubberBand.reset(QGis.Polygon) try: if self.toolAction: self.toolAction.setChecked(False) if self is not None: QgsMapTool.deactivate(self) except: pass def activate(self): """ Activate tool. """ if self.toolAction: self.toolAction.setChecked(True) QgsMapTool.activate(self) def setSelectionFeature(self, layer, feature, selectAll=False, setActiveLayer=False): """ Selects a given feature on canvas. :param layer: (QgsVectorLayer) layer containing the target feature. :param feature: (QgsFeature) taget feature to be selected. :param selectAll: (bool) indicates whether or not this fuction was called from a select all command. so it doesn't remove selection from those that are selected already from the list. :param setActiveLayer: (bool) indicates whether method should set layer as active. """ idList = layer.selectedFeaturesIds() # self.iface.setActiveLayer(layer) # esp acho q o problema eh aqui # layer.startEditing() featId = feature.id() if featId not in idList: idList.append(featId) elif not selectAll: idList.pop(idList.index(featId)) layer.setSelectedFeatures(idList) if setActiveLayer: layer.startEditing() if self.iface.activeLayer() != layer: self.iface.setActiveLayer(layer) return def setSelectionListFeature(self, dictLayerFeature, selectAll=True): """ Selects all features on canvas of a given dict. :param dictLayerFeature: (dict) dict of layers/features to be selected. :param selectAll: (bool) indicates if "All"-command comes from a "Select All". In that case, selected features won't be deselected. """ for layer in dictLayerFeature.keys(): geomType = layer.geometryType() # ID list of features already selected idList = layer.selectedFeaturesIds() # restart feature ID list for each layer featIdList = [] for feature in dictLayerFeature[layer]: featId = feature.id() if featId not in idList: idList.append(featId) elif not selectAll: idList.pop(idList.index(featId)) layer.setSelectedFeatures(idList) layer.startEditing() # last layer is set active and if self.iface.activeLayer() != layer: self.iface.setActiveLayer(layer) def openMultipleFeatureForm(self, dictLayerFeature): """ Opens all features Feature Forms of a given list. :param dictLayerFeature: (dict) dict of layers/features to have their feature form exposed. """ for layer, features in dictLayerFeature.iteritems(): for feat in features: self.iface.openFeatureForm(layer, feat, showModal=False) def filterStrongestGeometry(self, dictLayerFeature): """ Filter a given dict of features for its strongest geometry. :param dictLayerFeature: (dict) a dict of layers and its features to be filtered. :return: (dict) filtered dict with only layers of the strongest geometry on original dict. """ strongest_geometry = 3 outDict = dict() if dictLayerFeature: for lyr in dictLayerFeature.keys(): # to retrieve strongest geometry value if strongest_geometry > lyr.geometryType(): strongest_geometry = lyr.geometryType() if strongest_geometry == 0: break for lyr in dictLayerFeature.keys(): if lyr.geometryType() == strongest_geometry: outDict[lyr] = dictLayerFeature[lyr] return outDict def createRubberBand(self, feature, layer, geom): """ Creates a rubber band around from a given a standard feature string. :param feature: taget feature to be highlighted :param layer: layer containing the target feature :param geom: int indicating geometry type of target feature """ if geom == 0: self.hoverRubberBand.reset(QGis.Point) elif geom == 1: self.hoverRubberBand.reset(QGis.Line) else: self.hoverRubberBand.reset(QGis.Polygon) self.hoverRubberBand.addGeometry(feature.geometry(), layer) # to inform the code that menu has been hovered over self.menuHovered = True def createMultipleRubberBand(self, dictLayerFeature): """ Creates rubberbands around features. :param dictLayerFeature: (dict) dict of layer/features to have rubberbands built around. """ # only one type of geometry at a time will have rubberbands around it geom = dictLayerFeature.keys()[0].geometryType() if geom == 0: self.hoverRubberBand.reset(QGis.Point) elif geom == 1: self.hoverRubberBand.reset(QGis.Line) else: self.hoverRubberBand.reset(QGis.Polygon) for layer, features in dictLayerFeature.iteritems(): for feat in features: self.hoverRubberBand.addGeometry(feat.geometry(), layer) self.menuHovered = True def checkSelectedLayers(self): """ Checks if there are layers selected on canvas. If there are, returns the geometry type of selected feature(s). If more than one type of feature is selected, the "strongest" geometry is returned. """ geom = None for layer in self.iface.legendInterface().layers(): if isinstance(layer, QgsVectorLayer): selection = layer.selectedFeatures() if len(selection): if geom == None: geom = layer.geometryType() continue elif layer.geometryType() < geom: geom = layer.geometryType() continue return geom def addCallBackToAction(self, action, onTriggeredAction, onHoveredAction=None): """ Adds action the command to the action. If onHoveredAction is given, signal "hovered" is applied with given action. :param action: (QAction) associated with target context menu. :param onTriggeredAction: (object) action to be executed when the given action is triggered. :param onHoveredAction: (object) action to be executed whilst the given action is hovered. """ action.triggered[()].connect(onTriggeredAction) if onHoveredAction: action.hovered[()].connect(onHoveredAction) def getCallback(self, e, layer, feature, geomType=None, selectAll=True): """ Gets the callback for an action. :param e: (QMouseEvent) mouse event on canvas. :param layer: (QgsVectorLayer) layer to be treated. :param feature: (QgsFeature) feature to be treated. :param geomType: (int) code indicating layer geometry type. It is retrieved OTF in case it's not given. :return: (tuple-of function_lambda) callbacks for triggered and hovered signals. """ if not geomType: geomType = layer.geometryType() if e.button() == QtCore.Qt.LeftButton: # line added to make sure the action is associated with current loop value, # lambda function is used with standard parameter set to current loops value. triggeredAction = lambda t=[layer, feature] : self.setSelectionFeature(t[0], feature=t[1], selectAll=selectAll, setActiveLayer=True) hoveredAction = lambda t=[layer, feature] : self.createRubberBand(feature=t[1], layer=t[0], geom=geomType) elif e.button() == QtCore.Qt.RightButton: selected = (QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier) if selected: triggeredAction = lambda layer=layer : self.iface.setActiveLayer(layer) hoveredAction = None else: triggeredAction = lambda t=[layer, feature] : self.iface.openFeatureForm(t[0], t[1], showModal=False) hoveredAction = lambda t=[layer, feature] : self.createRubberBand(feature=t[1], layer=t[0], geom=geomType) return triggeredAction, hoveredAction def getCallbackMultipleFeatures(self, e, dictLayerFeature, selectAll=True): """ Sets the callback of an action with a list features as target. :param e: (QMouseEvent) mouse event on canvas. :param dictLayerFeature: (dict) dictionary containing layers/features to be treated. :return: (tuple-of function_lambda) callbacks for triggered and hovered signals. """ # setting the action for the "All" options if e.button() == QtCore.Qt.LeftButton: triggeredAction = lambda t=dictLayerFeature: self.setSelectionListFeature(dictLayerFeature=t, selectAll=selectAll) else: triggeredAction = lambda t=dictLayerFeature: self.openMultipleFeatureForm(dictLayerFeature=t) # to trigger "Hover" signal on QMenu for the multiple options hoveredAction = lambda t=dictLayerFeature : self.createMultipleRubberBand(dictLayerFeature=t) return triggeredAction, hoveredAction def createSubmenu(self, e, parentMenu, menuDict, genericAction, selectAll): """ Creates a submenu in a given parent context menu and populates it, with classes/feature sublevels from the menuDict. :param e: (QMouseEvent) mouse event on canvas. If menuDict has only 1 class in it, method will populate parent QMenu. :param parentMenu: (QMenu) menu containing the populated submenu :param menuDict: (dict) dictionary containing all classes and their features to be filled into submenu. :param genericAction: (str) text to be shown into generic action description on the outter level of submenu. :return: (dict) mapping of classes and their own QMenu object. """ # creating a dict to handle all "menu" for each class submenuDict = dict() # sort the layers from diciotnary classNameDict = { cl.name() : cl for cl in menuDict.keys() } layers = sorted(classNameDict.keys()) for className in layers: # menu for features of each class cl = classNameDict[className] geomType = cl.geometryType() # get layer database name dsUri = cl.dataProvider().dataSourceUri() temp = [] if '/' in dsUri or '\\' in dsUri: db_name = dsUri.split("|")[0] if "|" in dsUri else dsUri # data source is a file, not a postgres database dbIsFile = True elif 'memory' in dsUri: db_name = self.tr(u'{0} (Memory Layer)').format(className) dbIsFile = True else: db_name = dsUri.split("'")[1] dbIsFile = False if len(menuDict) == 1: # if dictionaty has only 1 class, no need for an extra QMenu - features will be enlisted directly # order features by ID to be displayer ordered featDict = { feat.id() : feat for feat in menuDict[cl] } orderedFeatIdList = sorted(featDict.keys()) for featId in orderedFeatIdList: feat = featDict[featId] if dbIsFile: s = u'{0} (feat_id = {1})'.format(db_name, featId) else: s = u'{0}.{1} (feat_id = {2})'.format(db_name, className, featId) # inserting action for each feature action = parentMenu.addAction(s) triggeredAction, hoveredAction = self.getCallback(e=e, layer=cl, feature=feat, geomType=geomType, selectAll=selectAll) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) # inserting generic action, if necessary if len(menuDict[cl]) > 1: # if there are more than 1 feature to be filled, "All"-command should be added action = parentMenu.addAction(self.tr(u"{0} From Class {1}").format(genericAction, className)) triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=menuDict, selectAll=selectAll) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) # there is no mapping of class to be exposed, only information added to parent QMenu itself return dict() if dbIsFile: title = db_name else: title = '{0}.{1}'.format(db_name, className) submenuDict[cl] = QtGui.QMenu(title=title, parent=parentMenu) parentMenu.addMenu(submenuDict[cl]) # inserting an entry for every feature of each class in its own context menu # order features by ID to be displayer ordered featDict = { feat.id() : feat for feat in menuDict[cl] } orderedFeatIdList = sorted(featDict.keys()) for featId in orderedFeatIdList: feat = featDict[featId] s = u'feat_id = {0}'.format(featId) action = submenuDict[cl].addAction(s) triggeredAction, hoveredAction = self.getCallback(e=e, layer=cl, feature=feat, geomType=geomType, selectAll=selectAll) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) # set up list for the "All"-commands temp.append([cl, feat, geomType]) # adding generic action for each class if len(menuDict[cl]) > 1: # if there are more than 1 feature to be filled, "All"-command should be added action = submenuDict[cl].addAction(self.tr(u"{0} From Class {1}").format(genericAction, className)) triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature={ cl : menuDict[cl] }, selectAll=selectAll) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) return submenuDict def setContextMenuStyle(self, e, dictMenuSelected, dictMenuNotSelected): """ Defines how many "submenus" the context menu should have. There are 3 context menu scenarios to be handled: :param e: (QMouseEvent) mouse event on canvas. :param dictMenuSelected: (dict) dictionary of classes and its selected features being treatead. :param dictMenuNotSelected: (dict) dictionary of classes and its non selected features being treatead. """ # finding out filling conditions selectedDict = bool(dictMenuSelected) notSelectedDict = bool(dictMenuNotSelected) # finding out if one of either dictionaty are filled ("Exclusive or") selectedXORnotSelected = (selectedDict != notSelectedDict) # setting "All"-command name if e.button() == QtCore.Qt.RightButton: genericAction = self.tr('Open All Feature Forms') else: genericAction = self.tr('Select All Features') # in case one of given dict is empty if selectedXORnotSelected: if selectedDict: menuDict, menu = dictMenuSelected, QtGui.QMenu(title=self.tr('Selected Features')) genericAction = self.tr('Deselect All Features') # if the dictionary is from selected features, we want commands to be able to deselect them selectAll = False else: menuDict, menu = dictMenuNotSelected, QtGui.QMenu(title=self.tr('Not Selected Features')) genericAction = self.tr('Select All Features') # if the dictionary is from non-selected features, we want commands to be able to select them selectAll = True if e.button() == QtCore.Qt.RightButton: genericAction = self.tr('Open All Feature Forms') self.createSubmenu(e=e, parentMenu=menu, menuDict=menuDict, genericAction=genericAction, selectAll=selectAll) if len(menuDict) != 1 and len(menuDict.values()) > 1: # if there's only one class, "All"-command is given by createSubmenu method action = menu.addAction(genericAction) triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=menuDict, selectAll=selectAll) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) elif selectedDict: # if both of them is empty one more QMenu level is added menu = QtGui.QMenu() selectedMenu = QtGui.QMenu(title=self.tr('Selected Features')) notSelectedMenu = QtGui.QMenu(title=self.tr('Not Selected Features')) menu.addMenu(selectedMenu) menu.addMenu(notSelectedMenu) selectedGenericAction = self.tr('Deselect All Features') notSelectedGenericAction = self.tr('Select All Features') # selectAll is set to True as now we want command to Deselect Features in case they are selected self.createSubmenu(e=e, parentMenu=selectedMenu, menuDict=dictMenuSelected, genericAction=selectedGenericAction, selectAll=False) if len(dictMenuSelected) != 1 and len(dictMenuSelected.values()) > 1: # if there's only one class, "All"-command is given by createSubmenu method action = selectedMenu.addAction(selectedGenericAction) triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=dictMenuSelected, selectAll=False) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) self.createSubmenu(e=e, parentMenu=notSelectedMenu, menuDict=dictMenuNotSelected, genericAction=notSelectedGenericAction, selectAll=True) if len(dictMenuNotSelected) != 1 and len(dictMenuNotSelected.values()) > 1: # if there's only one class, "All"-command is given by createSubmenu method action = notSelectedMenu.addAction(notSelectedGenericAction) triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=dictMenuNotSelected, selectAll=True) self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction) menu.exec_(self.canvas.viewport().mapToGlobal(e.pos())) def checkSelectedFeaturesOnDict(self, menuDict): """ Checks all selected features from a given dictionary ( { (QgsVectorLayer)layer : [ (QgsFeature)feat ] } ). :param menuDict: (dict) dictionary with layers and their features to be analyzed. :return: (tuple-of-dict) both dictionaries of selected and non-selected features of each layer. """ selectedFeaturesDict, notSelectedFeaturesDict = dict(), dict() for cl in menuDict.keys(): selectedFeats = [f.id() for f in cl.selectedFeatures()] for feat in menuDict[cl]: if feat.id() in selectedFeats: if cl not in selectedFeaturesDict.keys(): selectedFeaturesDict[cl] = [feat] else: selectedFeaturesDict[cl].append(feat) else: if cl not in notSelectedFeaturesDict.keys(): notSelectedFeaturesDict[cl] = [feat] else: notSelectedFeaturesDict[cl].append(feat) return selectedFeaturesDict, notSelectedFeaturesDict def reprojectSearchArea(self, layer, geom): """ Reprojects search area if necessary, according to what is being searched. :param layer: (QgsVectorLayer) layer which target rectangle has to have same SRC. :param geom: (QgsRectangle) rectangle representing search area. """ #geom always have canvas coordinates epsg = self.canvas.mapSettings().destinationCrs().authid() #getting srid from something like 'EPSG:31983' srid = layer.crs().authid() if epsg == srid: return geom crsSrc = QgsCoordinateReferenceSystem(epsg) crsDest = QgsCoordinateReferenceSystem(srid) # Creating a transformer coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest) # here we have to put authid, not srid auxGeom = QgsGeometry.fromRect(geom) auxGeom.transform(coordinateTransformer) return auxGeom.boundingBox() def createContextMenu(self, e): """ Creates the context menu for overlapping layers. :param e: mouse event caught from canvas. """ selected = (QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier) if selected: firstGeom = self.checkSelectedLayers() # setting a list of features to iterate over layerList = self.getPrimitiveDict(e, hasControlModifier=selected) layers = [] for key in layerList.keys(): layers += layerList[key] if layers: rect = self.getCursorRect(e) lyrFeatDict = dict() for layer in layers: if not isinstance(layer, QgsVectorLayer): continue geomType = layer.geometryType() # iterate over features inside the mouse bounding box bbRect = self.canvas.mapSettings().mapToLayerCoordinates(layer, rect) for feature in layer.getFeatures(QgsFeatureRequest(bbRect)): geom = feature.geometry() if geom: searchRect = self.reprojectSearchArea(layer, rect) if selected: # if Control was held, appending behaviour is different if not firstGeom: firstGeom = geomType elif firstGeom > geomType: firstGeom = geomType if geomType == firstGeom and geom.intersects(searchRect): # only appends features if it has the same geometry as first selected feature if layer in lyrFeatDict.keys(): lyrFeatDict[layer].append(feature) else: lyrFeatDict[layer] = [feature] else: if geom.intersects(searchRect): if layer in lyrFeatDict.keys(): lyrFeatDict[layer].append(feature) else: lyrFeatDict[layer] = [feature] lyrFeatDict = self.filterStrongestGeometry(lyrFeatDict) if lyrFeatDict: moreThanOneFeat = lyrFeatDict.values() and len(lyrFeatDict.values()) > 1 or len(lyrFeatDict.values()[0]) > 1 if moreThanOneFeat: # if there are overlapping features (valid candidates only) selectedFeaturesDict, notSelectedFeaturesDict = self.checkSelectedFeaturesOnDict(menuDict=lyrFeatDict) self.setContextMenuStyle(e=e, dictMenuSelected=selectedFeaturesDict, dictMenuNotSelected=notSelectedFeaturesDict) else: layer = lyrFeatDict.keys()[0] feature = lyrFeatDict[layer][0] selected = (QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier) if e.button() == QtCore.Qt.LeftButton: # if feature is selected, we want it to be de-selected self.setSelectionFeature(layer=layer, feature=feature, selectAll=False, setActiveLayer=True) elif selected: if self.iface.activeLayer() != layer: self.iface.setActiveLayer(layer) else: self.iface.openFeatureForm(layer, feature, showModal=False)
class ApisMapToolEmitPolygonAndPoint(QgsMapTool, ApisMapToolMixin): mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem) def __init__(self, canvas): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.rubberBand = None self.tempRubberBand = None self.vertexMarker = None self.capturedPoints = [] self.derivedPoint = None self.capturing = False self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() self.addVertex(event.pos()) elif event.button() == Qt.RightButton: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point != None and polygon != None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom != None and polygonGeom != None: self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def canvasMoveEvent(self, event): if self.tempRubberBand != None and self.capturing: mapPt = self.transformCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPt) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() event.ignore() if event.key() == Qt.Key_Escape: self.stopCapturing() self.clearScene() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point != None and polygon != None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom != None and polygonGeom != None: self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def startCapturing(self): self.clearScene() self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setWidth(2) self.rubberBand.setFillColor(QColor(220, 0, 0, 120)) self.rubberBand.setBorderColor(QColor(220, 0, 0)) self.rubberBand.setLineStyle(Qt.DotLine) self.rubberBand.show() self.tempRubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.tempRubberBand.setWidth(2) self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0)) self.tempRubberBand.setBorderColor(QColor(220, 0, 0)) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setIconType(1) self.vertexMarker.setColor(QColor(220, 0, 0)) self.vertexMarker.setIconSize(16) self.vertexMarker.setPenWidth(3) self.vertexMarker.show() self.capturing = True def clearScene(self): if self.vertexMarker: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None def stopCapturing(self): if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.capturing = False self.capturedPoints = [] self.derivedPoint = None self.canvas.refresh() def addVertex(self, canvasPoint): mapPt = self.transformCoordinates(canvasPoint) self.rubberBand.addPoint(mapPt) self.capturedPoints.append(mapPt) bandSize = self.rubberBand.numberOfVertices() if bandSize > 2: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() self.tempRubberBand.reset(QGis.Polygon) firstPoint = self.rubberBand.getPoint(0, 0) self.tempRubberBand.addPoint(firstPoint) self.tempRubberBand.movePoint(mapPt) self.tempRubberBand.addPoint(mapPt) def removeLastVertex(self): if not self.capturing: return bandSize = self.rubberBand.numberOfVertices() tempBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if bandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if bandSize > 1: if tempBandSize > 1: point = self.rubberBand.getPoint(0, bandSize - 2) self.tempRubberBand.movePoint(tempBandSize - 2, point) else: self.tempRubberBand.reset(QGis.Polygon) bandSize = self.rubberBand.numberOfVertices() if bandSize < 3: self.vertexMarker.hide() else: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() del self.capturedPoints[-1] def getCapturedPolygon(self): polygon = self.capturedPoints if len(polygon) < 3: return None else: return polygon def getDerivedPoint(self): point = self.derivedPoint if point == None: return None else: return point def getPointGeometry(self, geom): p = QgsGeometry.fromPoint(geom) if p.isGeosValid() and not p.isGeosEmpty(): return p else: return None def getPolygonGeometry(self, geom): p = QgsGeometry.fromPolygon([geom]) if p.isGeosValid() and not p.isGeosEmpty(): return p else: return None
class ArkMapToolInteractive(QgsMapTool): _active = False _dragging = False _panningEnabled = False _zoomingEnabled = False _zoomRubberBand = None #QgsRubberBand() _zoomRect = None # QRect() _snappingEnabled = False _snapper = None #QgsMapCanvasSnapper() _snappingMarker = None # QgsVertexMarker() _showSnappableVertices = False _snappableVertices = [] # [QgsPoint()] _snappableMarkers = [] # [QgsVertexMarker()] def __init__(self, canvas, snappingEnabled=False, showSnappableVertices=False): super(ArkMapToolInteractive, self).__init__(canvas) self._snappingEnabled = snappingEnabled self._showSnappableVertices = showSnappableVertices def __del__(self): if self._active: self.deactivate() def isActive(self): return self._active def activate(self): super(ArkMapToolInteractive, self).activate() self._active = True self._startSnapping() def deactivate(self): self._active = False if self._snappingEnabled: self._stopSnapping() if (self._zoomRubberBand is not None): self.canvas().scene().removeItem(self._zoomRubberBand) self._zoomRubberBand = None super(ArkMapToolInteractive, self).deactivate() def setAction(self, action): super(ArkMapToolInteractive, self).setAction(action) self.action().triggered.connect(self._activate) def _activate(self): self.canvas().setMapTool(self) def panningEnabled(self): return self._panningEnabled def setPanningEnabled(self, enabled): self._panningEnabled = enabled def zoomingEnabled(self): return self._zoomingEnabled def setZoomingEnabled(self, enabled): self._zoomingEnabled = enabled def snappingEnabled(self): return self._snappingEnabled def setSnappingEnabled(self, enabled): if (self._snappingEnabled == enabled): return self._snappingEnabled = enabled if not self._active: return if enabled: self._startSnapping() else: self._stopSnapping() def _startSnapping(self): self._snapper = QgsMapCanvasSnapper() self._snapper.setMapCanvas(self.canvas()) if self._showSnappableVertices: self._startSnappableVertices() def _stopSnapping(self): self._deleteSnappingMarker() self._snapper = None if self._showSnappableVertices: self._stopSnappableVertices() def showSnappableVertices(self): return self._showSnappableVertices def setShowSnappableVertices(self, show): if (self._showSnappableVertices == show): return self._showSnappableVertices = show if not self._active: return if show: self._startSnappableVertices() else: self._stopSnappableVertices() def _startSnappableVertices(self): self.canvas().layersChanged.connect(self._layersChanged) self.canvas().extentsChanged.connect(self._redrawSnappableMarkers) QgsProject.instance().snapSettingsChanged.connect(self._layersChanged) self._layersChanged() def _stopSnappableVertices(self): self._deleteSnappableMarkers() self._snappableLayers = [] self.canvas().layersChanged.disconnect(self._layersChanged) self.canvas().extentsChanged.disconnect(self._redrawSnappableMarkers) QgsProject.instance().snapSettingsChanged.disconnect(self._layersChanged) def canvasMoveEvent(self, e): super(ArkMapToolInteractive, self).canvasMoveEvent(e) if not self._active: return e.ignore() if (self._panningEnabled and e.buttons() & Qt.LeftButton): # Pan map mode if not self._dragging: self._dragging = True self.setCursor(QCursor(Qt.ClosedHandCursor)) self.canvas().panAction(e) e.accept() elif (self._zoomingEnabled and e.buttons() & Qt.RightButton): # Zoom map mode if not self._dragging: self._dragging = True self.setCursor(QCursor(Qt.ClosedHandCursor)) self._zoomRubberBand = QgsRubberBand(self.canvas(), QGis.Polygon) color = QColor(Qt.blue) color.setAlpha(63) self._zoomRubberBand.setColor(color) self._zoomRect = QRect(0, 0, 0, 0) self._zoomRect.setTopLeft(e.pos()) self._zoomRect.setBottomRight(e.pos()) if self._zoomRubberBand is not None: self._zoomRubberBand.setToCanvasRectangle(self._zoomRect) self._zoomRubberBand.show() e.accept() elif self._snappingEnabled: mapPoint, snapped = self._snapCursorPoint(e.pos()) if (snapped): self._createSnappingMarker(mapPoint) else: self._deleteSnappingMarker() def canvasReleaseEvent(self, e): super(ArkMapToolInteractive, self).canvasReleaseEvent(e) e.ignore() if (e.button() == Qt.LeftButton): if self._dragging: # Pan map mode self.canvas().panActionEnd(e.pos()) self.setCursor(capture_point_cursor) self._dragging = False e.accept() elif (e.button() == Qt.RightButton): if self._dragging: # Zoom mode self._zoomRect.setBottomRight(e.pos()) if (self._zoomRect.topLeft() != self._zoomRect.bottomRight()): coordinateTransform = self.canvas().getCoordinateTransform() ll = coordinateTransform.toMapCoordinates(self._zoomRect.left(), self._zoomRect.bottom()) ur = coordinateTransform.toMapCoordinates(self._zoomRect.right(), self._zoomRect.top()) r = QgsRectangle() r.setXMinimum(ll.x()) r.setYMinimum(ll.y()) r.setXMaximum(ur.x()) r.setYMaximum(ur.y()) r.normalize() if (r.width() != 0 and r.height() != 0): self.canvas().setExtent(r) self.canvas().refresh() self._dragging = False if (self._zoomRubberBand is not None): self.canvas().scene().removeItem(self._zoomRubberBand) self._zoomRubberBand = None e.accept() def keyPressEvent(self, e): super(ArkMapToolInteractive, self).keyPressEvent(e) if (e.key() == Qt.Key_Escape): self.canvas().unsetMapTool(self) e.accept() def _snapCursorPoint(self, cursorPoint): res, snapResults = self._snapper.snapToBackgroundLayers(cursorPoint) if (res != 0 or len(snapResults) < 1): return self.toMapCoordinates(cursorPoint), False else: # Take a copy as QGIS will delete the result! snappedVertex = QgsPoint(snapResults[0].snappedVertex) return snappedVertex, True def _createSnappingMarker(self, snapPoint): if (self._snappingMarker is None): self._snappingMarker = QgsVertexMarker(self.canvas()) self._snappingMarker.setIconType(QgsVertexMarker.ICON_CROSS) self._snappingMarker.setColor(Qt.magenta) self._snappingMarker.setPenWidth(3) self._snappingMarker.setCenter(snapPoint) def _deleteSnappingMarker(self): if (self._snappingMarker is not None): self.canvas().scene().removeItem(self._snappingMarker) self._snappingMarker = None def _createSnappableMarkers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return extent = self.canvas().extent() for vertex in self._snappableVertices.asMultiPoint(): if (extent.contains(vertex)): marker = QgsVertexMarker(self.canvas()) marker.setIconType(QgsVertexMarker.ICON_X) marker.setColor(Qt.gray) marker.setPenWidth(1) marker.setCenter(vertex) self._snappableMarkers.append(marker) def _deleteSnappableMarkers(self): for marker in self._snappableMarkers: self.canvas().scene().removeItem(marker) del self._snappableMarkers[:] def _layersChanged(self): if (not self._showSnappableVertices or not self._snappingEnabled): return self._buildSnappableLayers() self._deleteSnappableMarkers() self._createSnappableMarkers() def _redrawSnappableMarkers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return self._deleteSnappableMarkers() self._createSnappableMarkers() def _buildSnappableLayers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return vertices = [] for layer in self.canvas().layers(): ok, enabled, type, units, tolerance, avoid = QgsProject.instance().snapSettingsForLayer(layer.id()) if (ok and enabled and not layer.isEditable()): for feature in layer.getFeatures(): geometry = feature.geometry() if geometry is None: pass elif geometry.type() == QGis.Point: vertices.extend([geometry.asPoint()]) elif geometry.type() == QGis.Line: vertices.extend(geometry.asPolyline()) elif geometry.type() == QGis.Polygon: lines = geometry.asPolygon() for line in lines: vertices.extend(line) self._snappableVertices = QgsGeometry.fromMultiPoint(vertices) self._snappableVertices.simplify(0)
class RectangleMapTool(QgsMapToolEmitPoint): def __init__(self, canvas): QgsMapToolEmitPoint.__init__(self, canvas) self.canvas = canvas self.rubberBand = QgsRubberBand(canvas, QGis.Polygon) self.rubberBand.setColor(QColor(255, 0, 0, 180)) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isDrawing = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint mapSettings = self.canvas.mapSettings() if QGis.QGIS_VERSION_INT >= 20300 else self.canvas.mapRenderer() self.mupp = mapSettings.mapUnitsPerPixel() self.rotation = mapSettings.rotation() if QGis.QGIS_VERSION_INT >= 20700 else 0 self.isDrawing = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isDrawing = False self.emit(SIGNAL("rectangleCreated()")) def canvasMoveEvent(self, e): if not self.isDrawing: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QGis.Polygon) if startPoint.x() == endPoint.x() and startPoint.y() == endPoint.y(): return for i, pt in enumerate(self._rect(startPoint, endPoint).vertices()): self.rubberBand.addPoint(pt, bool(i == 3)) self.rubberBand.show() def _rect(self, startPoint, endPoint): if startPoint is None or endPoint is None: return None p0 = self.toCanvasCoordinates(startPoint) p1 = self.toCanvasCoordinates(endPoint) canvas_rect = QgsRectangle(QgsPoint(p0.x(), p0.y()), QgsPoint(p1.x(), p1.y())) center = QgsPoint((startPoint.x() + endPoint.x()) / 2, (startPoint.y() + endPoint.y()) / 2) return RotatedRect(center, self.mupp * canvas_rect.width(), self.mupp * canvas_rect.height()).rotate(self.rotation, center) def rectangle(self): return self._rect(self.startPoint, self.endPoint) def setRectangle(self, rect): if rect == self._rect(self.startPoint, self.endPoint): return False v = rect.vertices() self.startPoint = v[3] self.endPoint = v[1] self.showRect(self.startPoint, self.endPoint) return True
class DrawRect(QgsMapTool): '''Classe de sélection avec un Rectangle''' selectionDone = pyqtSignal() move = pyqtSignal() def __init__(self, iface, couleur): self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.iface = iface self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(couleur) self.reset() return None def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rb.reset(True) # true, its a polygon def canvasPressEvent(self, e): if not e.button() == Qt.LeftButton: return self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True def canvasReleaseEvent(self, e): self.isEmittingPoint = False if not e.button() == Qt.LeftButton: return None if self.rb.numberOfVertices() > 3: self.selectionDone.emit() else: width, height, ok = RectangleDialog().getSize() if width > 0 and height > 0 and ok: self.rb.addPoint( QgsPointXY( self.startPoint.x() + width, self.startPoint.y() - height)) self.showRect( self.startPoint, QgsPointXY( self.startPoint.x() + width, self.startPoint.y() - height)) self.selectionDone.emit() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.move.emit() self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rb.reset(QgsWkbTypes.PolygonGeometry) # true, it's a polygon if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(startPoint.x(), endPoint.y()) point3 = QgsPointXY(endPoint.x(), endPoint.y()) point4 = QgsPointXY(endPoint.x(), startPoint.y()) self.rb.addPoint(point1, False) self.rb.addPoint(point2, False) self.rb.addPoint(point3, False) self.rb.addPoint(point4, True) # true to update canvas self.rb.show() def deactivate(self): self.rb.reset(True) QgsMapTool.deactivate(self)
class DrawCircle(QgsMapTool): '''Outil de sélection par cercle, tiré de selectPlusFr''' selectionDone = pyqtSignal() move = pyqtSignal() def __init__(self, iface, color, segments): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.status = 0 self.segments = segments self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(color) return None def canvasPressEvent(self, e): if not e.button() == Qt.LeftButton: return self.status = 1 self.center = self.toMapCoordinates(e.pos()) rbcircle(self.rb, self.center, self.center, self.segments) return def canvasMoveEvent(self, e): if not self.status == 1: return # construct a circle with N segments cp = self.toMapCoordinates(e.pos()) rbcircle(self.rb, self.center, cp, self.segments) self.rb.show() self.move.emit() def canvasReleaseEvent(self, e): '''La sélection est faîte''' if not e.button() == Qt.LeftButton: return None self.status = 0 if self.rb.numberOfVertices() > 3: self.selectionDone.emit() else: radius, ok = QInputDialog.getDouble( self.iface.mainWindow(), tr('Radius'), tr('Give a radius in m:'), min=0) if radius > 0 and ok: cp = self.toMapCoordinates(e.pos()) cp.setX(cp.x() + radius) rbcircle(self.rb, self.toMapCoordinates( e.pos()), cp, self.segments) self.rb.show() self.selectionDone.emit() return None def reset(self): self.status = 0 self.rb.reset(True) def deactivate(self): self.rb.reset(True) QgsMapTool.deactivate(self)
class ApisMapToolEmitPointAndSquare(QgsMapTool, ApisMapToolMixin): # when mapping finished signal emitted that carries the Point and a Polygon Geometry (in Map Coordinates) mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem) def __init__(self, canvas, diagonal=200): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.vertexMarker = None self.rubberBand = None self.capturedPoint = None self.derivedPolygon = [] self.capturing = False # self.setLayers(pointLayer, polygonLayer) self.setDiagonal(diagonal) self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() self.setVertex(event.pos()) elif event.button() == Qt.RightButton: point = self.getCapturedPoint() polygon = self.getDerivedPolygon() self.stopCapturing() if point != None and polygon != None: self.mappingFinished.emit(self.getPointGeometry(point), self.getPolygonGeometry(polygon), self.canvas.mapSettings().destinationCrs()) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: #self.removeLastVertex() event.ignore() if event.key() == Qt.Key_Escape: self.stopCapturing() self.clearScene() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: point = self.getCapturedPoint() polygon = self.getDerivedPolygon() self.stopCapturing() if point != None and polygon != None: self.mappingFinished.emit(self.getPointGeometry(point), self.getPolygonGeometry(polygon), self.canvas.mapSettings().destinationCrs()) def startCapturing(self): self.clearScene() self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setIconType(1) self.vertexMarker.setColor(QColor(220, 0, 0)) self.vertexMarker.setIconSize(16) self.vertexMarker.setPenWidth(3) self.vertexMarker.show() self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setWidth(2) self.rubberBand.setFillColor(QColor(220, 0, 0, 120)) self.rubberBand.setBorderColor(QColor(220, 0, 0)) self.rubberBand.setLineStyle(Qt.DotLine) self.rubberBand.show() self.capturing = True def clearScene(self): if self.vertexMarker: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None def stopCapturing(self): self.capturing = False self.capturedPoint = None self.derivedPolygon = [] self.canvas.refresh() def setVertex(self, canvasPoint): mapPt = self.transformCoordinates(canvasPoint) # set/update vertexMarker Position self.vertexMarker.setCenter(mapPt) self.capturedPoint = mapPt # update rubberBand self.updateRubberBand() def updateRubberBand(self): if self.capturedPoint and self.rubberBand: # calculate Points self.derivedPolygon = self.calculateSquare(self.capturedPoint) self.rubberBand.reset(QGis.Polygon) for mapPt in self.derivedPolygon: self.rubberBand.addPoint(mapPt) def getCapturedPoint(self): point = self.capturedPoint if point == None: return None else: return point def getDerivedPolygon(self): polygon = self.derivedPolygon if polygon == None: return None else: return polygon def getPointGeometry(self, geom): return QgsGeometry.fromPoint(geom) def getPolygonGeometry(self, geom): return QgsGeometry.fromPolygon([geom]) def updateDiagonal(self, diagonal): self.setDiagonal(diagonal) # update Rubberband self.updateRubberBand()
class EarthMineQGIS(QObject): """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface super(EarthMineQGIS, self).__init__() self.movingfeature = None self.iface = iface self.viewer = None self.canvas = self.iface.mapCanvas() self.settings = QSettings() # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value("locale/userLocale")[0:2] locale_path = os.path.join(self.plugin_dir, "i18n", "EarthMineQGIS_{}.qm".format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > "4.3.3": QCoreApplication.installTranslator(self.translator) self.pointtool = QgsMapToolEmitPoint(self.canvas) self.pointtool.canvasClicked.connect(self.set_viewer_location) self.settingsdialog = SettingsDialog(self.iface.mainWindow()) self.actions = [] self.menu = self.tr(u"&Earthmine") self.toolbar = self.iface.addToolBar(u"EarthMineQGIS") self.toolbar.setObjectName(u"EarthMineQGIS") self.legend = self.iface.legendInterface() emcolor = QColor(1, 150, 51) self.tempband = QgsRubberBand(self.canvas, QGis.Line) self.tempband.setWidth(5) self.tempband.setColor(emcolor) self.tempbandpoints = QgsRubberBand(self.canvas, QGis.Point) self.tempbandpoints.setWidth(7) self.tempbandpoints.setColor(emcolor) self.movingband = QgsRubberBand(self.canvas, QGis.Point) self.movingband.setWidth(5) self.movingband.setColor(emcolor) self.layersignals = [] self.marker = None def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ":/icons/settings" self.add_action( icon_path, text=self.tr(u"Show Settings"), callback=self.show_settings, parent=self.iface.mainWindow() ) icon_path = ":/icons/viewer" self.add_action( icon_path, text=self.tr(u"Earthmine Viewer"), callback=self.open_viewer, parent=self.iface.mainWindow() ) self.marker = PostionMarker(self.canvas) self.marker.hide() self.viewer = Viewer(callbackobject=self) self.viewer.trackingChanged.connect(self.marker.setTracking) self.viewer.setLocationTriggered.connect(partial(self.canvas.setMapTool, self.pointtool)) self.viewer.updateFeatures.connect(self.update_earthmine_features) self.viewer.layerChanged.connect(self.iface.setActiveLayer) self.viewer.clearLine.connect(self.clear_bands) self.viewer.closed.connect(self.remove_items) self.iface.currentLayerChanged.connect(self.viewer.update_current_layer) cursor = QCursor(QPixmap(":/icons/location")) self.pointtool.setCursor(cursor) self.pointtool.setAction(self.viewer.setlocationaction) def remove_items(self): self.marker.setTracking(False) self.disconnect_projectsignals() self.iface.actionPan().trigger() def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" self.canvas.scene().removeItem(self.marker) del self.marker self.disconnect_projectsignals() for action in self.actions: self.iface.removePluginMenu(self.tr(u"&Earthmine"), action) self.iface.removeToolBarIcon(action) del self.toolbar self.iface.removeDockWidget(self.viewer) self.viewer.deleteLater() def disconnect_projectsignals(self): safe_disconnect(QgsMapLayerRegistry.instance().layerWasAdded, self.connect_layer_signals) safe_disconnect(QgsMapLayerRegistry.instance().layersRemoved, self.layers_removed) safe_disconnect(self.canvas.layersChanged, self.layers_changed) safe_disconnect(self.iface.projectRead, self.connect_signals) safe_disconnect(self.canvas.selectionChanged, self.selection_changed) safe_disconnect(self.canvas.selectionChanged, self.viewer.selection_changed) def clear_bands(self): self.tempband.reset(QGis.Line) self.tempbandpoints.reset(QGis.Point) def visible_layers(self): """ Return the visible layers shown in the map canvas :return: """ return (layer for layer, visible in self.layers_with_states() if visible) def layers_with_states(self): for layer in maplayers(): if not layer.type() == QgsMapLayer.VectorLayer: continue if not layer.geometryType() in [QGis.Point, QGis.Line]: continue yield layer, self.legend.isLayerVisible(layer) def _layer_feature_added(self, featureid): layer = self.sender() if not layer: return self.layer_feature_added(layer, featureid) def layer_feature_added(self, layer, featureid): if not self.viewer: return feature = layer.getFeatures(QgsFeatureRequest(featureid)).next() renderer = layer.rendererV2() transform = self.coordinatetransform(layer) featuredata = to_feature_data(layer.id(), feature, renderer, transform) geomtype = layer.geometryType() layerdata = dict(id=layer.id(), geomtype=QGis.vectorGeometryType(geomtype)) self.viewer.load_features(layerdata, featuredata) def _layer_feature_delete(self, featureid): layer = self.sender() if not layer: return self.layer_feature_delete(layer, featureid) def layer_feature_delete(self, layer, featureid): if not self.viewer: return self.viewer.remove_feature(layer.id(), featureid) def _layer_geometry_changed(self, featureid, geometry): layer = self.sender() if not layer: return self.layer_geometry_changed(layer, featureid, geometry) def layer_geometry_changed(self, layer, featureid, geometry): if not self.viewer: return geomtype = layer.geometryType() if geomtype == QGis.Point: geom = geometry.asPoint() transform = self.coordinatetransform(layer) point = transform.transform(geom, QgsCoordinateTransform.ReverseTransform) location = dict(lat=point.y(), lng=point.x()) self.viewer.edit_feature(layer.id(), featureid, [location]) elif geomtype == QGis.Line: self.layer_feature_delete(layer, featureid) self.layer_feature_added(layer, featureid) def connect_layer_signals(self, layer): if not layer.type() == QgsMapLayer.VectorLayer: return layer.featureAdded.connect(self._layer_feature_added) layer.featureDeleted.connect(self._layer_feature_delete) layer.editingStarted.connect(self.layer_editstate_changed) layer.editingStopped.connect(self.layer_editstate_changed) # HACK The new style doesn't work here # http://hub.qgis.org/issues/6573 signal = SIGNAL("geometryChanged(QgsFeatureId, QgsGeometry&)") self.connect(layer, signal, self._layer_geometry_changed) self.load_layer_features(layers=[layer]) def layer_editstate_changed(self): layer = self.sender() if layer == self.iface.activeLayer(): self.viewer.layer_changed(layer) def disconnect_signals(self): self.disconnect_projectsignals() for layer in maplayers(): if not layer.type() == QgsMapLayer.VectorLayer: return safe_disconnect(layer.featureAdded, self._layer_feature_added) safe_disconnect(layer.featureDeleted, self._layer_feature_delete) safe_disconnect(layer.editingStarted, self.layer_editstate_changed) safe_disconnect(layer.editingStopped, self.layer_editstate_changed) # HACK The new style doesn't work here # http://hub.qgis.org/issues/6573 signal = SIGNAL("geometryChanged(QgsFeatureId, QgsGeometry&)") self.disconnect(layer, signal, self._layer_geometry_changed) def connect_signals(self): for layer in maplayers(): self.connect_layer_signals(layer) self.center_on_canvas() def set_viewer_location(self, point, mousebutton): transform = self.coordinatetransform() point = transform.transform(point, QgsCoordinateTransform.ReverseTransform) self.viewer.set_location(point) def distancearea(self): area = QgsDistanceArea() dest = self.canvas.mapRenderer().destinationCrs() area.setSourceCrs(dest) return area, dest.mapUnits() def coordinatetransform(self, layer=None): """ Return the transform for WGS84 -> QGIS projection. """ source = QgsCoordinateReferenceSystem() source.createFromWkt( 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]' ) if not layer: dest = self.canvas.mapRenderer().destinationCrs() else: dest = layer.crs() transform = QgsCoordinateTransform(source, dest) return transform def earthmine_settings(self): settings = {} with settinggroup(self.settings, "plugins/Earthmine"): for key in ["serviceUrl", "baseDataUrl", "apiKey", "secretKey", "viewerUrl"]: if not self.settings.contains(key): raise EarthmineSettingsError("{} not set".format(key)) value = self.settings.value(key, type=str) if value is None: raise EarthmineSettingsError("{} not set".format(key)) settings[key] = value return settings @pyqtSlot() def ready(self): """ Called when the viewer is ready to be started. At this point the viewer hasn't been loaded so no other methods apart from startViewer will be handled. """ settings = self.earthmine_settings() self.viewer.startViewer(settings) @pyqtSlot() def viewerReady(self): """ Called once the viewer is loaded and ready to get location events. """ self.disconnect_signals() self.connect_signals() self.iface.projectRead.connect(self.connect_signals) self.canvas.layersChanged.connect(self.layers_changed) self.canvas.selectionChanged.connect(self.selection_changed) self.canvas.selectionChanged.connect(self.viewer.selection_changed) QgsMapLayerRegistry.instance().layersRemoved.connect(self.layers_removed) QgsMapLayerRegistry.instance().layerWasAdded.connect(self.connect_layer_signals) self.center_on_canvas() self.viewer.activelayercombo.setLayer(self.iface.activeLayer()) def center_on_canvas(self): point = self.canvas.extent().center() transform = self.coordinatetransform() point = transform.transform(point, QgsCoordinateTransform.ReverseTransform) self.viewer.set_location(point) self.viewer.infoaction.toggle() def selection_changed(self, layer): ids = [feature.id() for feature in layer.selectedFeatures()] if not ids: self.viewer.clear_selection(layer.id()) else: self.viewer.set_selection(layer.id(), ids) def layers_changed(self): layerstates = self.layers_with_states() for layer, visible in layerstates: layerid = layer.id() viewerloaded = self.viewer.layer_loaded(layerid) QgsMessageLog.instance().logMessage(layerid, "Earthmine") QgsMessageLog.instance().logMessage("Viewer State:" + str(viewerloaded), "Earthmine") QgsMessageLog.instance().logMessage("QGIS State:" + str(visible), "Earthmine") if (viewerloaded and visible) or (not viewerloaded and not visible): QgsMessageLog.instance().logMessage("Ignoring as states match", "Earthmine") continue if viewerloaded and not visible: QgsMessageLog.instance().logMessage( "Clearing layer because viewer loaded and disabled in QGIS", "Earthmine" ) self.viewer.clear_layer_features(layerid) continue if not viewerloaded and visible: QgsMessageLog.instance().logMessage("Loading layer", "Earthmine") self.load_layer_features(layers=[layer]) continue def layers_removed(self, layers): for layerid in layers: self.viewer.clear_layer_features(layerid) @pyqtSlot(str, float, float) def viewChanged(self, event, yaw, angle): self.marker.setAngle(angle) self.marker.setYaw(yaw) @pyqtSlot(str, str) def getInfo(self, layerid, featureid): featureid = int(featureid) activelayer = self.iface.activeLayer() if not activelayer: return activetool = self.viewer.active_tool() if not activetool in ["Info", "Select"]: return # Only show information for the active layer if not layerid == activelayer.id(): return layer = layer_by_id(layerid) if activetool == "Select": layer.setSelectedFeatures([featureid]) elif activetool == "Info": rq = QgsFeatureRequest(featureid) feature = layer.getFeatures(rq).next() dlg = get_feature_form(layer, feature) if dlg.dialog().exec_(): self.canvas.refresh() @pyqtSlot(str, str, float, float, bool) def featureMoved(self, layerid, featureid, lat, lng, end): layer = layer_by_id(layerid) transform = self.coordinatetransform(layer) point = transform.transform(lng, lat) if not end: self.movingband.show() self.movingband.setToGeometry(QgsGeometry.fromPoint(point), layer) self.movingband.updatePosition() self.movingband.update() else: self.movingband.hide() feature = feature_by_id(layer, featureid) startpoint = feature.geometry().asPoint() dx = point.x() - startpoint.x() dy = point.y() - startpoint.y() layer.beginEditCommand("Feature Moved") # Block signals for this move as the geometry changed signal will re add the geometry on use. layer.blockSignals(True) layer.translateFeature(feature.id(), dx, dy) layer.blockSignals(False) self.canvas.refresh() layer.endEditCommand() @pyqtSlot(str, str) def onError(self, message, stacktrace=None): self.iface.messageBar().pushMessage("Earthmine", message, QgsMessageBar.WARNING) QgsMessageLog.logMessage(stacktrace, "Earthmine") @pyqtSlot(float, float, float) def addPoint(self, lat, lng, z): layer = self.viewer.active_layer if not layer.isEditable(): self.iface.messageBar().pushMessage( "Earthmine", "Selected layer isn't editable. Please enable edit mode to add features", duration=3, level=QgsMessageBar.WARNING, ) return transform = self.coordinatetransform(layer) point = transform.transform(lng, lat) geom = QgsGeometry.fromPoint(point) self.add_feature(layer, geom, z) def add_feature(self, layer, geom, z=None): feature = QgsFeature(layer.pendingFields()) if z and self.viewer.copyZvalue: try: feature["Z"] = z except KeyError: QgsMessageLog.log("No Z found on layer {}".format(layer.name())) pass feature.setGeometry(geom) dlg = get_feature_form(layer, feature, isadd=True) if dlg.dialog().exec_(): self.canvas.refresh() @pyqtSlot(str, bool, str) def drawLine(self, points, end, stats): points = json.loads(points) stats = json.loads(stats) QgsMessageLog.logMessage(str(stats), "Earthmine") self.tempband.reset(QGis.Line) self.tempbandpoints.reset(QGis.Point) color = QColor(self.viewer.current_action_color) self.tempband.setColor(color) self.tempbandpoints.setColor(color) layer = self.viewer.active_layer transform = self.coordinatetransform(layer) earthminepoints = [] for point in points: newpoint = transform.transform(point["lng"], point["lat"]) self.tempband.addPoint(newpoint) self.tempbandpoints.addPoint(newpoint) empoint = EarthminePoint(newpoint, point) earthminepoints.append(empoint) if end and not self.viewer.mode == "Vertical": geom = self.tempband.asGeometry() self.add_feature(layer, geom) self.clear_bands() self.viewer.geom = EarthmineLine(earthminepoints, stats) self.tempband.show() self.tempbandpoints.show() @pyqtSlot(str, str, str, float) def locationChanged(self, lat, lng, yaw, angle): transform = self.coordinatetransform() point = transform.transform(float(lng), float(lat)) self.marker.setCenter(point) yaw = float(yaw) self.marker.setAngle(angle) self.marker.setYaw(yaw) self.marker.setTracking(self.viewer.tracking) if self.marker.tracking: rect = QgsRectangle(point, point) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(point): self.canvas.setExtent(rect) self.canvas.refresh() # Clear old features self.viewer.clear_features() self.load_layer_features(point) def update_earthmine_features(self, viewfeatures): self.viewer.clear_features() if viewfeatures: self.load_layer_features() def load_layer_features(self, point=None, layers=None): # TODO Move this logic into the viewer and let it track it's position if point is None and self.marker.map_pos is None: return if point is None: point = self.marker.map_pos area, units = self.distancearea() rect = search_area(units, area, point) if layers is None: layers = self.visible_layers() for layer in layers: transform = self.coordinatetransform(layer) # Transform the rect source = self.canvas.mapRenderer().destinationCrs() dest = layer.crs() recttransform = QgsCoordinateTransform(source, dest) rect = recttransform.transformBoundingBox(rect) features = list(get_features_in_area(layer, rect, transform, self.canvas.mapSettings())) geomtype = layer.geometryType() layerdata = dict(id=layer.id(), geomtype=QGis.vectorGeometryType(geomtype)) self.viewer.load_features(layerdata, features) # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate("EarthMineQGIS", message) def add_action( self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None, ): """Add a toolbar icon to the InaSAFE toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to s, "Earhtmine"how in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def open_viewer(self): """Run method that performs all the real work""" try: settings = self.earthmine_settings() except EarthmineSettingsError as ex: self.onError(ex.message) self.show_settings() return url = settings["viewerUrl"] if not url.startswith("http"): url = url.replace("\\\\", "\\") url = QUrl.fromLocalFile(url) else: url = QUrl(url) if not self.viewer.isVisible(): self.iface.addDockWidget(Qt.RightDockWidgetArea, self.viewer) self.viewer.loadviewer(url) def show_settings(self): self.settingsdialog.show()
class QgepMapToolConnectNetworkElements(QgsMapTool): """ This map tool connects wastewater networkelements. It works on two lists of layers: source layers with fields with a foreign key to a networkelement target layers which depict networkelements (reaches and network nodes) The tool will snap to source layers first and once one is chosen to a target layer. It will then ask which field(s) should be connected and perform the update on the database """ def __init__(self, iface, action): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.action = action self.rbline = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rbline.setColor(QColor('#f4530e')) self.rbline.setWidth(3) self.rbmarkers = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rbmarkers.setColor(QColor('#f4530e')) self.rbmarkers.setIconSize(6) self.source_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.target_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.source_feature = QgsFeature() self.rb_source_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_source_feature.setColor(QColor('#f49e79')) self.rb_source_feature.setWidth(3) self.target_feature = QgsFeature() self.rb_target_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_target_feature.setColor(QColor('#f49e79')) self.rb_target_feature.setWidth(3) def activate(self): """ Called by QGIS whenever the tool is activated. """ source_snap_layers = list() target_snap_layers = list() # A dict of layers and the fields that are foreign keys # pointing to wastewater networkelements self.network_element_sources = { QgepLayerManager.layer('vw_qgep_reach'): [ ('rp_to_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point To')), ('rp_from_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point From')) ], QgepLayerManager.layer('od_catchment_area'): [ ('fk_wastewater_networkelement_rw_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater current')), ('fk_wastewater_networkelement_rw_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater planned')), ('fk_wastewater_networkelement_ww_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater current')), ('fk_wastewater_networkelement_ww_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater planned')) ] } # A list of layers that can be used as wastewater networkelement # targets self.network_element_targets = [ QgepLayerManager.layer('vw_wastewater_node'), QgepLayerManager.layer('vw_qgep_reach') ] for layer in self.network_element_sources.keys(): if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) source_snap_layers.append(snap_layer) for layer in self.network_element_targets: if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) target_snap_layers.append(snap_layer) self.source_snapper.setLayers(source_snap_layers) self.source_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) self.target_snapper.setLayers(target_snap_layers) self.target_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) self.reset() self.action.setChecked(True) self.iface.mapCanvas().setCursor(QCursor(Qt.CrossCursor)) def canvasMoveEvent(self, event): """ When the mouse moves, update the rubberbands. """ pt = event.originalMapPoint() snap_match = self.snapper.snapToMap(pt) if snap_match.isValid(): if snap_match.type() != QgsPointLocator.Area: pt = snap_match.point() self.matchpoint = pt if self.source_match: if self.target_feature.id() != snap_match.featureId(): self.target_feature = self.get_feature_for_match( snap_match) self.rb_target_feature.setToGeometry( self.target_feature.geometry(), snap_match.layer()) self.rb_target_feature.show() self.rbmarkers.movePoint(pt) else: if self.source_feature.id() != snap_match.featureId(): self.source_feature = self.get_feature_for_match( snap_match) self.rb_source_feature.setToGeometry( self.source_feature.geometry(), snap_match.layer()) self.rb_source_feature.show() self.rbmarkers.movePoint(pt, 0) self.rbmarkers.show() else: self.rbmarkers.hide() if self.source_match: self.rb_target_feature.hide() else: self.rb_source_feature.hide() self.rbline.movePoint(pt) self.snapresult = snap_match def canvasReleaseEvent(self, event): """ On a click update the rubberbands and the snapping results if it's a left click. Reset if it's a right click. """ if event.button() == Qt.LeftButton: if self.snapresult.isValid(): if self.source_match: self.connect_features(self.source_match, self.snapresult) else: self.rbline.show() self.rbline.addPoint(self.matchpoint) self.source_match = self.snapresult self.snapper = self.target_snapper else: self.reset() def deactivate(self): """ Called by QGIS whenever this tool is deactivated. """ self.reset() self.action.setChecked(False) def reset(self): """ Resets the tool to a pristine state """ self.source_match = None self.rbline.hide() self.rbline.reset() self.rbmarkers.hide() self.rbmarkers.reset(QGis.Point) self.rbmarkers.addPoint(QgsPoint()) self.snapresult = None self.source_match = None self.snapper = self.source_snapper self.source_feature = QgsFeature() self.target_feature = QgsFeature() self.rb_source_feature.reset() self.rb_target_feature.reset() def get_feature_for_match(self, match): """ Get the feature for a snapping result @param match: The QgsPointLocator.SnapMatch object @return: A feature """ return next(match.layer().getFeatures(QgsFeatureRequest().setFilterFid(match.featureId()))) def connect_features(self, source, target): """ Connects the source feature with the target feature. @param source: A QgsPointLocator.Match object. Its foreign key will be updated. A dialog will be opened which asks the user for which foreign key(s) he wants to update. @param target: A QgsPointLocator.Match object. This feature will be used as link target. Its obj_id attribute will be used as primary key. """ dlg = QDialog(self.iface.mainWindow()) dlg.setWindowTitle(self.tr('Select properties to connect')) dlg.setLayout(QFormLayout()) properties = list() for prop in self.network_element_sources[source.layer()]: cbx = QCheckBox(prop[1]) cbx.setObjectName(prop[0]) properties.append(cbx) dlg.layout().addWidget(cbx) btn_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(btn_box) btn_box.accepted.connect(dlg.accept) btn_box.rejected.connect(dlg.reject) source_feature = self.get_feature_for_match(source) target_feature = self.get_feature_for_match(target) if dlg.exec_(): for cbx in properties: if cbx.isChecked(): source_feature[cbx.objectName()] = target_feature['obj_id'] if source.layer().updateFeature(source_feature): self.iface.messageBar().pushMessage('QGEP', self.tr('Connected {} to {}').format( source_feature[ 'identifier'], target_feature['identifier']), QgsMessageBar.INFO, 5) else: self.iface.messageBar().pushMessage('QGEP', self.tr( 'Error connecting features'), QgsMessageBar.WARNING, 5) self.reset()
class RectangleAreaTool(QgsMapTool): rectangleCreated = pyqtSignal(float, float, float, float) def __init__(self, canvas, action): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.active = False self.setAction(action) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) mFillColor = QColor(254, 178, 76, 63) self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBand.hide() self.transformCoordinates() self.rectangleCreated.emit(self.startPoint.x(), self.startPoint.y(), self.endPoint.x(), self.endPoint.y()) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates( e.pos() ) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(startPoint.x(), endPoint.y()) point3 = QgsPointXY(endPoint.x(), endPoint.y()) point4 = QgsPointXY(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def transformCoordinates(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y(): return None # Defining the crs from src and destiny epsg = self.canvas.mapSettings().destinationCrs().authid() crsSrc = QgsCoordinateReferenceSystem(epsg) crsDest = QgsCoordinateReferenceSystem(4326) # Creating a transformer coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) # Transforming the points self.startPoint = coordinateTransformer.transform(self.startPoint) self.endPoint = coordinateTransformer.transform(self.endPoint) def deactivate(self): self.rubberBand.hide() QgsMapTool.deactivate(self)
class Dialog(QDialog, Ui_nbEditor_dialog): def __init__(self, iface, ml, mc): """Constructor for the dialog. Args: iface: QgsInterface instance. """ QDialog.__init__(self, iface.mainWindow()) self.setupUi(self) self.ml = ml self.mCanvas = mc self.mRubberBand = QgsRubberBand(self.mCanvas, True) self.mRubberBand.reset(QGis.Polygon) self.mRubberBand.setColor(Qt.red) self.mRubberBand.setWidth(2) self.ids = [] self.ini(0) self.pushCancel.clicked.connect(self.close) self.pushOK.clicked.connect(self.convert) self.comboBox.addItems(['','Intersections','Touches','Within distance']) self.comboBox.currentIndexChanged.connect(self.nbMethod) self.ml.selectionChanged.connect(self.map2tab) def ini(self, n): self.model = QStandardItemModel(n, 1) self.tableView.setModel(self.model) self.model.setHeaderData(0, Qt.Horizontal, 'Neighbouring IDs') self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.selectionModel = QItemSelectionModel(self.model) self.tableView.setSelectionModel(self.selectionModel) self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.selectionModel().selectionChanged.connect(self.tab2map) self.progressBar.setValue(0) def settings(self): self.mod = min(self.ids) self.p = 1 if self.mod==1: self.p = 0 def map2tab(self): s = '' idx = self.tableView.selectionModel().selectedIndexes()[0] ts = str(self.model.itemData(idx)[0]) for fid in sorted(self.ml.selectedFeaturesIds()): s += '%s,' % str(int(fid)+self.p) s = s[:-1] if s!=ts: self.model.setData(idx, s) # in order to handle the symmetry if len(s)>len(ts): iLst = s.strip().replace(' ', '').split(',') jLst = ts.strip().replace(' ', '').split(',') else: iLst = ts.strip().replace(' ', '').split(',') jLst = s.strip().replace(' ', '').split(',') cent = str(idx.row()+self.p) dLst = list(set(iLst)-set(jLst)) for d in dLst: row = int(d)-self.p sor = str(self.model.itemData(self.model.index(row, 0))[0]) eLst = sor.strip().replace(' ', '').split(',') res = '' if cent in set(eLst): ii = eLst.index(cent) del eLst[ii] eLst = sorted(map(int, eLst)) for e in eLst: res += '%s,' % e res = res[:-1] else: u = sor + ',%s' % cent eLst = sorted(map(int, u.strip().replace(' ', '').split(','))) for e in eLst: res += '%s,' % e res = res[:-1] self.model.setData(self.model.index(row, 0, QModelIndex()), res) def nbWithinDist(self): dlg = xdist.Dialog() dlg.setModal(True) dlg.setWindowTitle("Between two objects") if dlg.exec_() == QDialog.Accepted: lDist = float(dlg.lineEdit.text()) if lDist==0: return feat = QgsFeature() provider = self.ml.dataProvider() e = provider.featureCount() self.settings() for ne in range(self.mod, e + self.mod): feat = QgsFeature() geom = QgsGeometry() fiter = self.ml.getFeatures(QgsFeatureRequest(ne)) if fiter.nextFeature(feat): geom = QgsGeometry(feat.geometry()) neighbours = self.hdist(feat, lDist) row = feat.id()-self.mod self.model.setData(self.model.index(row, 0, QModelIndex()), neighbours) self.progressBar.setValue(100*ne/e) def hdist(self, feata, lDist): geoma = QgsGeometry(feata.geometry()) feat = QgsFeature() provider = self.ml.dataProvider() feats = provider.getFeatures() self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0) self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount())) ne = 0 neighbours = "" while feats.nextFeature(feat): ne += 1 self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne) geomb = QgsGeometry(feat.geometry()) if feata.id()!=feat.id(): if geoma.distance(geomb)<=lDist: neighbours = neighbours + '%s,' % (feat.id()+self.p) return neighbours[:-1] def tab2map(self): QApplication.setOverrideCursor(Qt.WaitCursor) self.ml.selectionChanged.disconnect(self.map2tab) idx = self.tableView.selectionModel().selectedIndexes()[0] featureId = idx.row() + self.p s = self.model.itemData(idx) lst = s[0].strip().replace(' ', '').split(',') self.ml.removeSelection() for sid in lst: self.ml.select(int(sid)-self.p) provider = self.ml.dataProvider() feat = QgsFeature() layer = QgsVectorLayerCache(self.ml, provider.featureCount()) layer.featureAtId(idx.row()+self.mod, feat) geom = QgsGeometry(feat.geometry()) self.mRubberBand.setToGeometry(geom, self.ml) self.mRubberBand.show() self.ml.selectionChanged.connect(self.map2tab) QApplication.restoreOverrideCursor() def closeEvent(self,event): QApplication.setOverrideCursor(Qt.WaitCursor) self.ml.selectionChanged.disconnect(self.map2tab) self.ml.removeSelection() self.mRubberBand.hide() self.close() QApplication.restoreOverrideCursor() def convert(self): dlg = editor.Dialog() dlg.setModal(True) dlg.setWindowTitle("Neighbour list in BUGS format") num = "" adj = "" sumNumNeigh = 0 for row in range(0, self.model.rowCount()): ts = self.model.itemData(self.model.index(row, 0)) lst = ts[0].strip().replace(' ', '').split(',') num += '%s, ' % len(lst) sumNumNeigh += len(lst) lst.reverse() sor = ', '.join(lst) + ',' adj = adj + str(sor) + '\n' num = num[:-2] adj = adj[:-2] nblist = 'list(\nnum = c(%s),\nadj = c(%s),\nsumNumNeigh=%s)' % (num, adj, sumNumNeigh) dlg.plainTextEdit.appendPlainText(nblist) dlg.exec_() def nbMethod(self): QApplication.setOverrideCursor(Qt.WaitCursor) self.ml.selectionChanged.disconnect(self.map2tab) self.model.removeRows(0, self.model.rowCount(QModelIndex()), QModelIndex()) n = self.ml.dataProvider().featureCount() self.ini(n) self.ids = [] provider = self.ml.dataProvider() feats = provider.getFeatures() self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0) self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, n)) ne = 0 feat = QgsFeature() while feats.nextFeature(feat): ne += 1 self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne) self.ids.append(feat.id()) if self.comboBox.currentText()=="Touches": if self.ml.geometryType()==0: return else: self.nbTouches() if self.comboBox.currentText()=="Intersections": if self.ml.geometryType()==0: return else: self.nbIntersects() if self.comboBox.currentText()=="Within distance": self.nbWithinDist() self.ml.selectionChanged.connect(self.map2tab) QApplication.restoreOverrideCursor() def nbTouches(self): feat = QgsFeature() provider = self.ml.dataProvider() e = provider.featureCount() self.settings() for ne in range(self.mod, e + self.mod): feat = QgsFeature() geom = QgsGeometry() fiter = self.ml.getFeatures(QgsFeatureRequest(ne)) if fiter.nextFeature(feat): geom = QgsGeometry(feat.geometry()) neighbours = self.htouch(feat) row = feat.id()-self.mod self.model.setData(self.model.index(row, 0, QModelIndex()), neighbours) self.progressBar.setValue(100*ne/e) def htouch(self, feata): geoma = QgsGeometry(feata.geometry()) feat = QgsFeature() provider = self.ml.dataProvider() feats = provider.getFeatures() self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0) self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount())) ne = 0 neighbours = "" while feats.nextFeature(feat): ne += 1 self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne) geomb = QgsGeometry(feat.geometry()) if feata.id()!=feat.id(): if geoma.touches(geomb)==True: neighbours = neighbours + '%s,' % (feat.id()+self.p) return neighbours[:-1] def nbIntersects(self): feat = QgsFeature() provider = self.ml.dataProvider() e = provider.featureCount() self.settings() for ne in range(self.mod, e + self.mod): feat = QgsFeature() geom = QgsGeometry() fiter = self.ml.getFeatures(QgsFeatureRequest(ne)) if fiter.nextFeature(feat): geom = QgsGeometry(feat.geometry()) neighbours = self.hintersect(feat) row = feat.id()-self.mod self.model.setData(self.model.index(row, 0, QModelIndex()), neighbours) self.progressBar.setValue(100*ne/e) def hintersect(self, feata): geoma = QgsGeometry(feata.geometry()) feat = QgsFeature() provider = self.ml.dataProvider() feats = provider.getFeatures() self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0) self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount())) ne = 0 neighbours = "" while feats.nextFeature(feat): ne += 1 self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne) geomb = QgsGeometry(feat.geometry()) if feata.id()!=feat.id(): if geoma.intersects(geomb)==True: neighbours = neighbours + '%s,' % (feat.id()+self.p) return neighbours[:-1]
class ShapeTool(QgsMapTool): #signal emitted when the mouse is clicked. This indicates that the tool finished its job toolFinished = pyqtSignal() def __init__(self, canvas, geometryType, param, type, color = QColor( 254, 178, 76, 63 )): """ Constructor """ QgsMapTool.__init__(self, canvas) self.canvas = canvas self.active = False self.geometryType = geometryType self.param=param self.type=type self.cursor=None self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.setColor(color) self.reset() self.rotAngle = 0 self.currentCentroid = None self.rotate = False def setColor(self, mFillColor): """ Adjusting the color to create the rubber band """ self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(1) def reset(self): """ Resetting the rubber band """ self.startPoint = self.endPoint = None self.isEmittingPoint = False try: self.rubberBand.reset(QGis.Polygon) except: pass def rotateRect(self, centroid, e): """ Calculates the angle for the rotation. """ item_position = self.canvas.mapToGlobal(e.pos()) c = self.toCanvasCoordinates(centroid) c = self.canvas.mapToGlobal(c) rotAngle = pi - atan2(item_position.y() - c.y(), item_position.x() - c.x()) return rotAngle def canvasPressEvent(self, e): """ When the canvas is pressed the tool finishes its job """ # enforce mouse restoring if clicked right after rotation QtGui.QApplication.restoreOverrideCursor() self.canvas.unsetMapTool(self) self.toolFinished.emit() def canvasMoveEvent(self, e): """ Deals with mouse move event to update the rubber band position in the canvas """ ctrlIsHeld = QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier if e.button() != None and not ctrlIsHeld: if self.rotate: # change rotate status self.rotate = False QtGui.QApplication.restoreOverrideCursor() self.endPoint = self.toMapCoordinates( e.pos() ) elif e.button() != None and ctrlIsHeld \ and self.geometryType == self.tr(u"Square"): # calculate angle between mouse and last rubberband centroid before holding control self.rotAngle = self.rotateRect(self.currentCentroid, e) if not self.rotate: # only override mouse if it is not overriden already QtGui.QApplication.setOverrideCursor(QCursor(Qt2.BlankCursor)) self.rotate = True if self.geometryType == self.tr(u"Circle"): self.showCircle(self.endPoint) elif self.geometryType == self.tr(u"Square"): self.showRect(self.endPoint, sqrt(self.param)/2, self.rotAngle) def showCircle(self, startPoint): """ Draws a circle in the canvas """ nPoints = 50 x = startPoint.x() y = startPoint.y() if self.type == self.tr('distance'): r = self.param self.rubberBand.reset(QGis.Polygon) for itheta in range(nPoints+1): theta = itheta*(2.0*pi/nPoints) self.rubberBand.addPoint(QgsPoint(x+r*cos(theta), y+r*sin(theta))) self.rubberBand.show() else: r = sqrt(self.param/pi) self.rubberBand.reset(QGis.Polygon) for itheta in range(nPoints+1): theta = itheta*(2.0*pi/nPoints) self.rubberBand.addPoint(QgsPoint(x+r*cos(theta), y+r*sin(theta))) self.rubberBand.show() def showRect(self, startPoint, param, rotAngle=0): """ Draws a rectangle in the canvas """ self.rubberBand.reset(QGis.Polygon) x = startPoint.x() # center point x y = startPoint.y() # center point y # rotation angle is always applied in reference to center point # to avoid unnecessary calculations c = cos(rotAngle) s = sin(rotAngle) # translating coordinate system to rubberband centroid point1 = QgsPoint((- param), (- param)) point2 = QgsPoint((- param), ( param)) point3 = QgsPoint((param), ( param)) point4 = QgsPoint((param), (- param)) # rotating and moving to original coord. sys. point1_ = QgsPoint(point1.x()*c - point1.y()*s + x, point1.y()*c + point1.x()*s + y) point2_ = QgsPoint(point2.x()*c - point2.y()*s + x, point2.y()*c + point2.x()*s + y) point3_ = QgsPoint(point3.x()*c - point3.y()*s + x, point3.y()*c + point3.x()*s + y) point4_ = QgsPoint(point4.x()*c - point4.y()*s + x, point4.y()*c + point4.x()*s + y) self.rubberBand.addPoint(point1_, False) self.rubberBand.addPoint(point2_, False) self.rubberBand.addPoint(point3_, False) self.rubberBand.addPoint(point4_, True) self.rubberBand.show() self.currentCentroid = startPoint def deactivate(self): """ Deactivates the tool and hides the rubber band """ self.rubberBand.hide() QgsMapTool.deactivate(self) # restore mouse in case tool is disabled right after rotation QtGui.QApplication.restoreOverrideCursor() def activate(self): """ Activates the tool """ QgsMapTool.activate(self) def reproject(self, geom, canvasCrs): """ Reprojects geom from the canvas crs to the reference crs geom: geometry to be reprojected canvasCrs: canvas crs (from crs) """ destCrs = self.reference.crs() if canvasCrs.authid() != destCrs.authid(): coordinateTransformer = QgsCoordinateTransform(canvasCrs, destCrs) geom.transform(coordinateTransformer)
class RotateRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandExtent = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) # In case of rotation around pressed point (ctrl) # Use rubberBand for displaying an horizontal line. self.rubberBandDisplacement = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): self.startY = e.pos().y() self.endY = self.startY self.isEmittingPoint = True self.height = self.canvas.height() modifiers = QApplication.keyboardModifiers() self.isRotationAroundPoint = bool(modifiers & Qt.ControlModifier) self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) rotation = self.computeRotation() self.showRotation(rotation) self.layer.history.append({"action": "rotation", "rotation": self.layer.rotation, "center": self.layer.center}) # rotation set def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() rotation = self.computeRotation() if self.isRotationAroundPoint: self.layer.moveCenterFromPointRotate( self.startPoint, rotation, 1, 1) val = self.layer.rotation + rotation self.layer.setRotation(val) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endY = e.pos().y() rotation = self.computeRotation() self.showRotation(rotation) self.endPoint = self.toMapCoordinates(e.pos()) def computeRotation(self): if self.isRotationAroundPoint: dX = self.endPoint.x() - self.startPoint.x() dY = self.endPoint.y() - self.startPoint.y() return math.degrees(math.atan2(-dY, dX)) else: dY = self.endY - self.startY return 90.0 * dY / self.height def showRotation(self, rotation): if self.isRotationAroundPoint: cornerPoints = self.layer.transformedCornerCoordinatesFromPoint( self.startPoint, rotation, 1, 1) self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaRotationFromPoint( rotation, self.startPoint, True) self.rasterShadow.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point0 = QgsPointXY(self.startPoint.x() + 10, self.startPoint.y()) point1 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) self.rubberBandDisplacement.addPoint(point0, False) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint( point2, True) # true to update canvas self.rubberBandDisplacement.show() else: center, originalRotation, xScale, yScale = \ self.layer.transformParameters() newRotation = rotation + originalRotation cornerPoints = self.layer.transformedCornerCoordinates( center, newRotation, xScale, yScale) self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaRotation(rotation, True) self.rasterShadow.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) # for closing self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show()
class RectangleMapTool(QgsMapTool): def __init__(self, canvas, callback): self.canvas = canvas QgsMapTool.__init__(self, self.canvas) self.callback = callback self.rubberBand = QgsRubberBand(self.canvas, True) self.rubberBand.setColor(QColor(227, 26, 28, 255)) self.rubberBand.setWidth(5) self.rubberBand.setLineStyle(Qt.PenStyle(Qt.DashLine)) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(True) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False r = self.rectangle() if r is not None: # print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() self.rubberBand.hide() self.callback(r) # self.deactivate() return None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(True) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, False) self.rubberBand.addPoint(point1, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def deactivate(self): super(RectangleMapTool, self).deactivate() self.emit(QtCore.SIGNAL("deactivated()"))
class ScaleRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandExtent = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): pressed_button = e.button() if pressed_button == 1: self.startPoint = e.pos() self.endPoint = self.startPoint self.isEmittingPoint = True self.height = float(self.canvas.height()) self.width = float(self.canvas.width()) modifiers = QApplication.keyboardModifiers() self.isKeepRelativeScale = bool(modifiers & Qt.ControlModifier) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) scaling = self.computeScaling() self.showScaling(*scaling) self.layer.history.append({"action": "scale", "xScale": self.layer.xScale, "yScale": self.layer.yScale}) def canvasReleaseEvent(self, e): pressed_button = e.button() if pressed_button == 1: self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() xScale, yScale = self.computeScaling() self.layer.setScale(xScale * self.layer.xScale, yScale * self.layer.yScale) setLayerVisible(self.iface, self.layer, self.isLayerVisible) elif pressed_button == 2: number, ok = QInputDialog.getText( None, "Scale & DPI", "Enter scale,dpi (e.g. 3000,96)") if not ok: self.layer.history.pop() return scales = number.split(',') if len(scales) != 2: self.layer.history.pop() QMessageBox.information( self.iface.mainWindow(), "Error", "Must be 2 numbers") return scale = tryfloat(scales[0]) dpi = tryfloat(scales[1]) if scale and dpi: xScale = scale / (dpi / 0.0254) yScale = xScale else: self.layer.history.pop() QMessageBox.information( self.iface.mainWindow(), "Error", "Bad format: Must be scale,dpi (e.g. 3000,96)") return self.layer.setScale(xScale, yScale) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = e.pos() scaling = self.computeScaling() self.showScaling(*scaling) def computeScaling(self): dX = -(self.endPoint.x() - self.startPoint.x()) dY = self.endPoint.y() - self.startPoint.y() xScale = 1.0 - (dX / (self.width * 1.1)) yScale = 1.0 - (dY / (self.height * 1.1)) if self.isKeepRelativeScale: # keep same scale in both dimensions return (xScale, xScale) else: return (xScale, yScale) def showScaling(self, xScale, yScale): if xScale == 0 and yScale == 0: return center, rotation, originalXScale, originalYScale = \ self.layer.transformParameters() newXScale = xScale * originalXScale newYScale = yScale * originalYScale cornerPoints = self.layer.transformedCornerCoordinates( center, rotation, newXScale, newYScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) # for closing self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaScale(xScale, yScale, True) self.rasterShadow.show()
class RectangleMapTool(QgsMapToolEmitPoint): def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setColor(QColor(255, 0, 0, 180)) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.emit(SIGNAL("rectangleCreated()")) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QGis.Polygon) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint == None or self.endPoint == None: return None #elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y(): # return None return QgsRectangle(self.startPoint, self.endPoint) def setRectangle(self, rect): if rect == self.rectangle(): return False if rect == None: self.reset() else: self.startPoint = QgsPoint(rect.xMaximum(), rect.yMaximum()) self.endPoint = QgsPoint(rect.xMinimum(), rect.yMinimum()) self.showRect(self.startPoint, self.endPoint) return True
class MoveRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandDisplacement = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.rubberBandExtent = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) self.isLayerVisible = True self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.originalCenter = self.layer.center # this tool do the displacement itself TODO update so it is done by # transformed coordinates + new center) self.originalCornerPoints = self.layer.transformedCornerCoordinates( *self.layer.transformParameters()) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) self.showDisplacement(self.startPoint, self.endPoint) self.layer.history.append({"action": "move", "center": self.layer.center}) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() x = self.originalCenter.x() + self.endPoint.x() - self.startPoint.x() y = self.originalCenter.y() + self.endPoint.y() - self.startPoint.y() self.layer.setCenter(QgsPointXY(x, y)) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showDisplacement(self.startPoint, self.endPoint) def showDisplacement(self, startPoint, endPoint): self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(endPoint.x(), endPoint.y()) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint( point2, True) # true to update canvas self.rubberBandDisplacement.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in self.originalCornerPoints: self._addDisplacementToPoint(self.rubberBandExtent, point, False) # for closing self._addDisplacementToPoint( self.rubberBandExtent, self.originalCornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(self.endPoint.x( ) - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True) self.rasterShadow.show() def _addDisplacementToPoint(self, rubberBand, point, doUpdate): x = point.x() + self.endPoint.x() - self.startPoint.x() y = point.y() + self.endPoint.y() - self.startPoint.y() self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) if hasattr(self.canvas, 'setParallelRenderingEnabled'): self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) gpsspacewidget= QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(0, 0, 212, 76)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.featureformloaded.connect(self.feature_form_loaded) self.connectButtons() def init_qgisproject(self, doc): parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() return self.canvas.mapRenderer().destinationCrs() def showEvent(self, *args, **kwargs): if self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, project, editmode): self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def highlight_active_selection(self, layer, feature, features): self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def clear_selection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position) def gps_first_fix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): self.marker.hide() def select_data_entry(self): def showformerror(form): pass def actions(): for form in self.project.forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form") formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): self.project = project self.actionPan.trigger() try: firstform = project.forms[0] self.load_form(firstform) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.freeze(False) self.canvas.refresh() def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def load_form(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value RoamEvents.open_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class AdjustRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandExtent = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) self.rubberBandAdjustSide = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.setColor(Qt.red) self.rubberBandAdjustSide.setWidth(3) self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): # find the side of the rectangle closest to the click and some data # necessary to compute the new cneter and scale topLeft, topRight, bottomRight, bottomLeft = \ self.layer.cornerCoordinates() top = [topLeft, topRight] right = [bottomRight, topRight] bottom = [bottomRight, bottomLeft] left = [bottomLeft, topLeft] click = QgsGeometry.fromPointXY(self.toMapCoordinates(e.pos())) # order is important (for referenceSide) sides = [top, right, bottom, left] distances = [click.distance( QgsGeometry.fromPolylineXY(side)) for side in sides] self.indexSide = self.minDistance(distances) self.side = sides[self.indexSide] self.sidePoint = self.center(self.side) self.vector = self.directionVector(self.side) # side that does not move (opposite of indexSide) self.referenceSide = sides[(self.indexSide + 2) % 4] self.referencePoint = self.center(self.referenceSide) self.referenceDistance = self.distance( self.sidePoint, self.referencePoint) self.isXScale = self.indexSide % 2 == 1 self.startPoint = click.asPoint() self.endPoint = self.startPoint self.isEmittingPoint = True self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) adjustment = self.computeAdjustment() self.showAdjustment(*adjustment) self.layer.history.append( {"action": "adjust", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale}) def minDistance(self, distances): sortedDistances = [i[0] for i in sorted( enumerate(distances), key=itemgetter(1))] # first is min return sortedDistances[0] def directionVector(self, side): sideCenter = self.center(side) layerCenter = self.layer.center vector = [sideCenter.x() - layerCenter.x(), sideCenter.y() - layerCenter.y()] norm = math.sqrt(vector[0]**2 + vector[1]**2) normedVector = [vector[0] / norm, vector[1] / norm] return normedVector def center(self, side): return QgsPointXY((side[0].x() + side[1].x()) / 2, (side[0].y() + side[1].y()) / 2) def distance(self, pt1, pt2): return math.sqrt((pt1.x() - pt2.x())**2 + (pt1.y() - pt2.y())**2) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() center, xScale, yScale = self.computeAdjustment() self.layer.setCenter(center) self.layer.setScale(xScale * self.layer.xScale, yScale * self.layer.yScale) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) adjustment = self.computeAdjustment() self.showAdjustment(*adjustment) def computeAdjustment(self): dX = self.endPoint.x() - self.startPoint.x() dY = self.endPoint.y() - self.startPoint.y() # project on vector dp = dX * self.vector[0] + dY * self.vector[1] # do not go beyond 5% of the current size of side if dp < -0.95 * self.referenceDistance: dp = -0.95 * self.referenceDistance updatedSidePoint = QgsPointXY(self.sidePoint.x() + dp * self.vector[0], self.sidePoint.y() + dp * self.vector[1]) center = self.center([self.referencePoint, updatedSidePoint]) scaleFactor = self.distance(self.referencePoint, updatedSidePoint) if self.isXScale: xScale = scaleFactor / self.referenceDistance yScale = 1.0 else: xScale = 1.0 yScale = scaleFactor / self.referenceDistance return (center, xScale, yScale) def showAdjustment(self, center, xScale, yScale): _, rotation, originalXScale, originalYScale = \ self.layer.transformParameters() newXScale = xScale * originalXScale newYScale = yScale * originalYScale cornerPoints = self.layer.transformedCornerCoordinates( center, rotation, newXScale, newYScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) # for closing self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() # show rubberband for side # see def of indexSide in init: # cornerpoints are (topLeft, topRight, bottomRight, bottomLeft) self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.addPoint( cornerPoints[self.indexSide % 4], False) self.rubberBandAdjustSide.addPoint( cornerPoints[(self.indexSide + 1) % 4], True) self.rubberBandAdjustSide.show() self.rasterShadow.reset(self.layer) dx = center.x() - self.layer.center.x() dy = center.y() - self.layer.center.y() self.rasterShadow.setDeltaDisplacement(dx, dy, False) self.rasterShadow.setDeltaScale(xScale, yScale, True) self.rasterShadow.show()
class RectangleMapTool(QgsMapToolEmitPoint): def __init__(self, canvas): QgsMapToolEmitPoint.__init__(self, canvas) self.canvas = canvas self.rubberBand = QgsRubberBand(canvas, QGis.Polygon) self.rubberBand.setColor(QColor(255, 0, 0, 180)) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isDrawing = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint mapSettings = self.canvas.mapSettings( ) if QGis.QGIS_VERSION_INT >= 20300 else self.canvas.mapRenderer() self.mupp = mapSettings.mapUnitsPerPixel() self.rotation = mapSettings.rotation() if QGis.QGIS_VERSION_INT >= 20700 else 0 self.isDrawing = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isDrawing = False self.emit(SIGNAL("rectangleCreated()")) def canvasMoveEvent(self, e): if not self.isDrawing: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QGis.Polygon) if startPoint.x() == endPoint.x() and startPoint.y() == endPoint.y(): return for i, pt in enumerate(self._rect(startPoint, endPoint).vertices()): self.rubberBand.addPoint(pt, bool(i == 3)) self.rubberBand.show() def _rect(self, startPoint, endPoint): if startPoint is None or endPoint is None: return None p0 = self.toCanvasCoordinates(startPoint) p1 = self.toCanvasCoordinates(endPoint) canvas_rect = QgsRectangle( QgsPoint( p0.x(), p0.y()), QgsPoint( p1.x(), p1.y())) center = QgsPoint( (startPoint.x() + endPoint.x()) / 2, (startPoint.y() + endPoint.y()) / 2) return RotatedRect(center, self.mupp * canvas_rect.width(), self.mupp * canvas_rect.height()).rotate(self.rotation, center) def rectangle(self): return self._rect(self.startPoint, self.endPoint) def setRectangle(self, rect): if rect == self._rect(self.startPoint, self.endPoint): return False v = rect.vertices() self.startPoint = v[3] self.endPoint = v[1] self.showRect(self.startPoint, self.endPoint) return True