def _draw_rubberband(self, extent, colour, width=2): """ Draw a rubber band on the canvas. .. versionadded: 2.2.0 :param extent: Extent that the rubber band should be drawn for. :type extent: QgsRectangle :param colour: Colour for the rubber band. :type colour: QColor :param width: The width for the rubber band pen stroke. :type width: int :returns: Rubber band that should be set to the extent. :rtype: QgsRubberBand """ rubberband = QgsRubberBand( self.iface.mapCanvas(), geometryType=QGis.Line) rubberband.setColor(colour) rubberband.setWidth(width) update_display_flag = False point = QgsPoint(extent.xMinimum(), extent.yMinimum()) rubberband.addPoint(point, update_display_flag) point = QgsPoint(extent.xMaximum(), extent.yMinimum()) rubberband.addPoint(point, update_display_flag) point = QgsPoint(extent.xMaximum(), extent.yMaximum()) rubberband.addPoint(point, update_display_flag) point = QgsPoint(extent.xMinimum(), extent.yMaximum()) rubberband.addPoint(point, update_display_flag) point = QgsPoint(extent.xMinimum(), extent.yMinimum()) update_display_flag = True rubberband.addPoint(point, update_display_flag) return rubberband
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)
class QgepMapToolAddFeature( QgsMapTool ): def __init__(self, iface, layer): QgsMapTool.__init__(self, iface.mapCanvas() ) self.iface = iface self.canvas = iface.mapCanvas() self.layer = layer self.rubberband = QgsRubberBand( iface.mapCanvas(), layer.geometryType() ) self.rubberband.setColor( QColor( "#ee5555" ) ) self.rubberband.setWidth( 2 ) self.tempRubberband = QgsRubberBand( iface.mapCanvas(), layer.geometryType() ) self.tempRubberband.setColor( QColor( "#ee5555" ) ) self.tempRubberband.setWidth( 2 ) self.tempRubberband.setLineStyle(Qt.DotLine) def activate(self): QgsMapTool.activate( self ) self.canvas.setCursor( QCursor( Qt.CrossCursor ) ) pass def deactivate(self): QgsMapTool.deactivate( self ) self.canvas.unsetCursor() pass def isZoomTool( self ): return False #=========================================================================== # Events #=========================================================================== def canvasMoveEvent( self, event ): self.mouseMoved( event ) def canvasReleaseEvent( self, event ): if event.button() == Qt.RightButton: self.rightClicked ( event ) else: self.leftClicked( event ) def leftClicked(self, event): mousePos = self.canvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y() ) self.rubberband.addPoint( mousePos ) self.tempRubberband.reset() def rightClicked(self, event): f = QgsFeature( self.layer.pendingFields() ) f.setGeometry( self.rubberband.asGeometry() ) dlg = self.iface.getFeatureForm( self.layer, f ) dlg.setIsAddDialog(True) dlg.exec_() self.rubberband.reset() self.tempRubberband.reset() def mouseMoved(self, event): mousePos = self.canvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y() ) self.tempRubberband.movePoint( mousePos )
def drawManyPaths(self, rows, columns, con, args, geomType, canvasItemList, mapCanvas): ''' draws multi line string on the mapCanvas. ''' resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_path_id = None for row in rows: cur2 = con.cursor() result_path_id = str(row[columns[0]]) args['result_node_id'] = sql.Literal(row[columns[1]]) args['result_edge_id'] = sql.Literal(row[columns[2]]) if result_path_id != cur_path_id: cur_path_id = result_path_id if rubberBand: resultPathsRubberBands.append(rubberBand) rubberBand = None rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) rubberBand.setColor(QColor(255, 0, 0, 128)) rubberBand.setWidth(4) if row[columns[2]] != -1: query2 = sql.SQL(""" SELECT ST_AsText({transform_s}{geometry}{transform_e}) FROM {edge_schema}.{edge_table} WHERE {source} = {result_node_id} AND {id} = {result_edge_id} UNION SELECT ST_AsText({transform_s}ST_Reverse({geometry}){transform_e}) FROM {edge_schema}.{edge_table} WHERE {target} = {result_node_id} AND {id} = {result_edge_id} """).format(**args).as_string(con) cur2.execute(query2) row2 = cur2.fetchone() geom = QgsGeometry().fromWkt(str(row2[0])) if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) if rubberBand: resultPathsRubberBands.append(rubberBand) rubberBand = None
class DrawPolygon(QgsMapTool): '''Outil de sélection par polygone, tiré de selectPlusFr''' selectionDone = pyqtSignal() move = pyqtSignal() def __init__(self, iface, couleur): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.status = 0 self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(couleur) return None def keyPressEvent(self, e): if e.matches(QKeySequence.Undo): if self.rb.numberOfVertices() > 1: self.rb.removeLastPoint() def canvasPressEvent(self, e): if e.button() == Qt.LeftButton: if self.status == 0: self.rb.reset(QgsWkbTypes.PolygonGeometry) self.status = 1 self.rb.addPoint(self.toMapCoordinates(e.pos())) else: if self.rb.numberOfVertices() > 2: self.status = 0 self.selectionDone.emit() else: self.reset() return None def canvasMoveEvent(self, e): if self.rb.numberOfVertices() > 0 and self.status == 1: self.rb.removeLastPoint(0) self.rb.addPoint(self.toMapCoordinates(e.pos())) self.move.emit() return None def reset(self): self.status = 0 self.rb.reset(True) def deactivate(self): self.rb.reset(True) QgsMapTool.deactivate(self)
class RubberBandResultRenderer(): def __init__(self, iface, color = QColor('magenta'), size = 12): self.iface = iface self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor(color) self.rb.setIconSize(size) self.srs_wgs84 = QgsCoordinateReferenceSystem(4326) self.transformation = QgsCoordinateTransform(self.srs_wgs84, self.srs_wgs84) def show_point(self, point, center=False): #check srs if self.need_transform(): point = self.transform_point(point) self.rb.addPoint(point) if center: self.center_to_point(point) def clear(self): self.rb.reset(QGis.Point) def need_transform(self): return self.iface.mapCanvas().mapRenderer().destinationCrs().postgisSrid() != 4326 def transform_point(self, point): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transform(point) except: print 'Error on transform!' # DEBUG! need message??? return def center_to_point(self, point): canvas = self.iface.mapCanvas() new_extent = QgsRectangle(canvas.extent()) new_extent.scale(1, point) canvas.setExtent(new_extent) canvas.refresh()
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 FreehandPolygonMaptool(QgsMapTool, QObject): trigger = pyqtSignal(QgsGeometry) def __init__(self, canvas): QgsMapTool.__init__(self,canvas) self.canvas = canvas self.rb = QgsRubberBand(canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(QColor(255, 0, 0, 50)) def activate(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) def deactivate(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) def canvasMoveEvent(self, ev): worldPoint = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), ev.pos().x(), ev.pos().y()) self.rb.movePoint(worldPoint) def canvasPressEvent(self, ev): if ev.button() == Qt.LeftButton: """ Add a new point to the rubber band """ worldPoint = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), ev.pos().x(), ev.pos().y()) self.rb.addPoint(worldPoint) elif ev.button() == Qt.RightButton: """ Send back the geometry to the calling class """ self.trigger.emit(self.rb.asGeometry()) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return False
class DrawPoint(QgsMapTool): selectionDone = pyqtSignal() def __init__(self, iface, couleur): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rb.setColor(couleur) self.rb.setWidth(3) def canvasReleaseEvent(self, e): if e.button() == Qt.LeftButton: self.rb.addPoint(self.toMapCoordinates(e.pos())) self.selectionDone.emit() def reset(self): self.rb.reset(QgsWkbTypes.PointGeometry) def deactivate(self): self.rb.reset(QgsWkbTypes.PointGeometry) QgsMapTool.deactivate(self)
class SelectPoint(QgsMapTool): select = pyqtSignal() selectionDone = pyqtSignal() def __init__(self, iface, couleur): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(couleur) self.rbSelect = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) return None def canvasReleaseEvent(self, e): if e.button() == Qt.LeftButton: self.rbSelect.reset(QgsWkbTypes.PolygonGeometry) cp = self.toMapCoordinates( QPoint(e.pos().x() - 5, e.pos().y() - 5)) self.rbSelect.addPoint(cp) cp = self.toMapCoordinates( QPoint(e.pos().x() + 5, e.pos().y() - 5)) self.rbSelect.addPoint(cp) cp = self.toMapCoordinates( QPoint(e.pos().x() + 5, e.pos().y() + 5)) self.rbSelect.addPoint(cp) cp = self.toMapCoordinates( QPoint(e.pos().x() - 5, e.pos().y() + 5)) self.rbSelect.addPoint(cp) self.select.emit() else: self.selectionDone.emit() return None def reset(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) self.rbSelect.reset(QgsWkbTypes.PolygonGeometry) def deactivate(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) self.rbSelect.reset(QgsWkbTypes.PolygonGeometry) QgsMapTool.deactivate(self)
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()"))
def drawCostPaths(self, rows, con, args, geomType, canvasItemList, mapCanvas): resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_path_id = -1 for row in rows: cur2 = con.cursor() args['result_path_id'] = row[0] args['result_source_id'] = sql.Literal(row[1]) args['result_target_id'] = sql.Literal(row[2]) args['result_cost'] = row[3] if args['result_path_id'] != cur_path_id: cur_path_id = args['result_path_id'] if rubberBand: resultPathsRubberBands.append(rubberBand) rubberBand = None rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) rubberBand.setColor(QColor(255, 0, 0, 128)) rubberBand.setWidth(4) if args['result_cost'] != -1: query2 = sql.SQL(""" SELECT ST_AsText( ST_MakeLine( (SELECT {geometry_vt} FROM {vertex_schema}.{vertex_table} WHERE id = {result_source_id}), (SELECT {geometry_vt} FROM {vertex_schema}.{vertex_table} WHERE id = {result_target_id}) )) """).format(**args) # Utils.logMessage(query2) cur2.execute(query2) row2 = cur2.fetchone() # Utils.logMessage(str(row2[0])) geom = QgsGeometry().fromWkt(str(row2[0])) if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) # TODO label the edge instead of labeling the target points if rubberBand: resultPathsRubberBands.append(rubberBand) rubberBand = None resultNodesTextAnnotations = canvasItemList['annotations'] for row in rows: cur2 = con.cursor() args['result_seq'] = row[0] args['result_source_id'] = sql.Literal(row[1]) result_target_id = row[2] args['result_target_id'] = sql.Literal(result_target_id) result_cost = row[3] query2 = sql.SQL(""" SELECT ST_AsText( ST_startPoint({geometry}) ) FROM {edge_schema}.{edge_table} WHERE {source} = {result_target_id} UNION SELECT ST_AsText( ST_endPoint( {geometry} ) ) FROM {edge_schema}.{edge_table} WHERE {target} = {result_target_id} """).format(**args) cur2.execute(query2) row2 = cur2.fetchone() geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("{0!s}:{1}".format(result_target_id, result_cost)) textAnnotation = QgsTextAnnotation() textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) QgsMapCanvasAnnotationItem(textAnnotation, mapCanvas) resultNodesTextAnnotations.append(textAnnotation)
class QgepMapToolDigitizeDrainageChannel(QgsMapTool): ''' This is used to digitize a drainage channel. It lets you digitize two points and then creates a polygon based on these two points by adding an orthogonal offset at each side. Input: x==============x Output: ---------------- | | ---------------- Usage: Connect to the signals deactivated() and geometryDigitized() If geometryDigitized() is called you can use the member variable geometry which will contain a rectangle polygon deactivated() will be emited after a right click ''' geometryDigitized = pyqtSignal() def __init__(self, iface, layer): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.layer = layer self.rubberband = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.LineGeometry) self.rubberband.setColor(QColor("#ee5555")) self.rubberband.setWidth(2) self.firstPoint = None self.messageBarItem = None self.geometry = None def activate(self): """ Map tool is activated """ QgsMapTool.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) msgtitle = self.tr('Digitizing Drainage Channel') msg = self.tr('Digitize start and end point. Rightclick to abort.') self.messageBarItem = QgsMessageBar.createMessage(msgtitle, msg) self.iface.messageBar().pushItem(self.messageBarItem) def deactivate(self): """ Map tool is deactivated """ QgsMapTool.deactivate(self) self.iface.messageBar().popWidget(self.messageBarItem) try: self.iface.mapCanvas().scene().removeItem(self.rubberband) del self.rubberband except AttributeError: # Called repeatedly... bail out pass self.canvas.unsetCursor() def canvasMoveEvent(self, event): """ Mouse is moved: Update rubberband :param event: coordinates etc. """ mousepos = event.mapPoint() self.rubberband.movePoint(mousepos) def canvasReleaseEvent(self, event): """ Canvas is released. This means: * start digitizing * stop digitizing (create a rectangle * if the Ctrl-modifier is pressed, ask for the rectangle width :param event: coordinates etc. """ if event.button() == Qt.RightButton: self.deactivate() else: mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) if self.firstPoint: # If the first point was set before, we are doing the second one lp1 = self.rubberband.asGeometry().asPolyline()[0] lp2 = self.rubberband.asGeometry().asPolyline()[1] width = 0.2 if QApplication.keyboardModifiers() & Qt.ControlModifier: dlg = QDialog() dlg.setLayout(QGridLayout()) dlg.layout().addWidget(QLabel(self.tr('Enter width'))) txt = QLineEdit('0.2') dlg.layout().addWidget(txt) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(bb) bb.accepted.connect(dlg.accept) bb.rejected.connect(dlg.reject) if dlg.exec_(): try: width = float(txt.text()) except ValueError: width = 0.2 length = math.sqrt( math.pow(lp1.x() - lp2.x(), 2) + math.pow(lp1.y() - lp2.y(), 2)) xd = lp2.x() - lp1.x() yd = lp2.y() - lp1.y() pt1 = QgsPointXY(lp1.x() + width * (yd / length), lp1.y() - width * (xd / length)) pt2 = QgsPointXY(lp1.x() - width * (yd / length), lp1.y() + width * (xd / length)) pt3 = QgsPointXY(lp2.x() - width * (yd / length), lp2.y() + width * (xd / length)) pt4 = QgsPointXY(lp2.x() + width * (yd / length), lp2.y() - width * (xd / length)) self.geometry = QgsGeometry.fromPolygonXY( [[pt1, pt2, pt3, pt4, pt1]]) self.geometryDigitized.emit() self.firstPoint = mousepos
class Qgis2threejsDialog(QDialog): def __init__(self, iface, objectTypeManager, pluginManager, exportSettings=None, lastTreeItemData=None): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.objectTypeManager = objectTypeManager self.pluginManager = pluginManager self._settings = exportSettings or {} self.lastTreeItemData = lastTreeItemData self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.templateType = None self.currentItem = None self.currentPage = None # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) # output html filename ui.lineEdit_OutputFilename.setText(self._settings.get("OutputFilename", "")) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") # settings button icon = QIcon(os.path.join(tools.pluginDir(), "icons", "settings.png")) ui.toolButton_Settings.setIcon(icon) # popup menu displayed when settings button is pressed items = [["Load Settings...", self.loadSettings], ["Save Settings As...", self.saveSettings], [None, None], ["Clear Settings", self.clearSettings], [None, None], ["Plugin Settings...", self.pluginSettings]] self.menu = QMenu() self.menu_actions = [] for text, slot in items: if text: action = QAction(text, iface.mainWindow()) action.triggered.connect(slot) self.menu.addAction(action) self.menu_actions.append(action) else: self.menu.addSeparator() ui.toolButton_Settings.setMenu(self.menu) ui.toolButton_Settings.setPopupMode(QToolButton.InstantPopup) # progress bar and message label ui.progressBar.setVisible(False) ui.label_MessageIcon.setVisible(False) # buttons ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) ui.pushButton_Help.clicked.connect(self.help) # set up map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) #self.mapTool = PointMapTool(iface.mapCanvas()) # set up the template combo box self.initTemplateList() self.ui.comboBox_Template.currentIndexChanged.connect(self.currentTemplateChanged) # set up the properties pages self.pages = {} self.pages[ppages.PAGE_WORLD] = ppages.WorldPropertyPage(self) self.pages[ppages.PAGE_CONTROLS] = ppages.ControlsPropertyPage(self) self.pages[ppages.PAGE_DEM] = ppages.DEMPropertyPage(self) self.pages[ppages.PAGE_VECTOR] = ppages.VectorPropertyPage(self) container = ui.propertyPagesContainer for page in self.pages.itervalues(): page.hide() container.addWidget(page) # build object tree self.topItemPages = {ObjectTreeItem.ITEM_WORLD: ppages.PAGE_WORLD, ObjectTreeItem.ITEM_CONTROLS: ppages.PAGE_CONTROLS, ObjectTreeItem.ITEM_DEM: ppages.PAGE_DEM} self.initObjectTree() self.ui.treeWidget.currentItemChanged.connect(self.currentObjectChanged) self.ui.treeWidget.itemChanged.connect(self.objectItemChanged) self.currentTemplateChanged() # update item visibility ui.toolButton_Browse.clicked.connect(self.browseClicked) #iface.mapCanvas().mapToolSet.connect(self.mapToolSet) # to show button to enable own map tool def settings(self, clean=False): # save settings of current panel item = self.ui.treeWidget.currentItem() if item and self.currentPage: self.saveProperties(item, self.currentPage) # plugin version self._settings["PluginVersion"] = plugin_version # template and output html file path self._settings["Template"] = self.ui.comboBox_Template.currentText() self._settings["OutputFilename"] = self.ui.lineEdit_OutputFilename.text() if not clean: return self._settings # clean up settings - remove layers that don't exist in the layer registry registry = QgsMapLayerRegistry.instance() for itemId in [ObjectTreeItem.ITEM_OPTDEM, ObjectTreeItem.ITEM_POINT, ObjectTreeItem.ITEM_LINE, ObjectTreeItem.ITEM_POLYGON]: parent = self._settings.get(itemId, {}) for layerId in parent.keys(): if registry.mapLayer(layerId) is None: del parent[layerId] return self._settings def setSettings(self, settings): self._settings = settings # template and output html file path templateName = settings.get("Template") if templateName: cbox = self.ui.comboBox_Template index = cbox.findText(templateName) if index != -1: cbox.setCurrentIndex(index) filename = settings.get("OutputFilename") if filename: self.ui.lineEdit_OutputFilename.setText(filename) # update object tree self.ui.treeWidget.blockSignals(True) self.initObjectTree() self.ui.treeWidget.blockSignals(False) # update tree item visibility self.templateType = None self.currentTemplateChanged() def loadSettings(self): # file open dialog directory = QgsProject.instance().homePath() if not directory: directory = os.path.split(self.ui.lineEdit_OutputFilename.text())[0] if not directory: directory = QDir.homePath() filterString = "Settings files (*.qto3settings);;All files (*.*)" filename = QFileDialog.getOpenFileName(self, "Load Export Settings", directory, filterString) if not filename: return # load settings from file (.qto3settings) import json with open(filename) as f: settings = json.load(f) self.setSettings(settings) def saveSettings(self, filename=None): if not filename: # file save dialog directory = QgsProject.instance().homePath() if not directory: directory = os.path.split(self.ui.lineEdit_OutputFilename.text())[0] if not directory: directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, "Save Export Settings", directory, "Settings files (*.qto3settings)") if not filename: return # append .qto3settings extension if filename doesn't have if os.path.splitext(filename)[1].lower() != ".qto3settings": filename += ".qto3settings" # save settings to file (.qto3settings) import codecs import json with codecs.open(filename, "w", "UTF-8") as f: json.dump(self.settings(True), f, ensure_ascii=False, indent=2, sort_keys=True) logMessage(u"Settings saved: {0}".format(filename)) def clearSettings(self): if QMessageBox.question(self, "Qgis2threejs", "Are you sure to clear all export settings?", QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok: self.setSettings({}) def pluginSettings(self): from settingsdialog import SettingsDialog dialog = SettingsDialog(self) if dialog.exec_(): self.pluginManager.reloadPlugins() self.pages[ppages.PAGE_DEM].initLayerComboBox() def showMessageBar(self, text, level=QgsMessageBar.INFO): # from src/gui/qgsmessagebaritem.cpp if level == QgsMessageBar.CRITICAL: msgIcon = "/mIconCritical.png" bgColor = "#d65253" elif level == QgsMessageBar.WARNING: msgIcon = "/mIconWarn.png" bgColor = "#ffc800" else: msgIcon = "/mIconInfo.png" bgColor = "#e7f5fe" stylesheet = "QLabel {{ background-color:{0}; }}".format(bgColor) label = self.ui.label_MessageIcon label.setPixmap(QgsApplication.getThemeIcon(msgIcon).pixmap(24)) label.setStyleSheet(stylesheet) label.setVisible(True) label = self.ui.label_Status label.setText(text) label.setStyleSheet(stylesheet) def clearMessageBar(self): self.ui.label_MessageIcon.setVisible(False) self.ui.label_Status.setText("") self.ui.label_Status.setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 0); }") def initTemplateList(self): cbox = self.ui.comboBox_Template cbox.clear() templateDir = QDir(tools.templateDir()) for i, entry in enumerate(templateDir.entryList(["*.html", "*.htm"])): cbox.addItem(entry) config = tools.getTemplateConfig(entry) # get template type templateType = config.get("type", "plain") cbox.setItemData(i, templateType, Qt.UserRole) # set tool tip text desc = config.get("description", "") if desc: cbox.setItemData(i, desc, Qt.ToolTipRole) # select the template of the settings templatePath = self._settings.get("Template") # if no template setting, select the last used template if not templatePath: templatePath = QSettings().value("/Qgis2threejs/lastTemplate", def_vals.template, type=unicode) if templatePath: index = cbox.findText(templatePath) if index != -1: cbox.setCurrentIndex(index) return index return -1 def initObjectTree(self): tree = self.ui.treeWidget tree.clear() # add vector and raster layers into tree widget topItems = {} for id, name in zip(ObjectTreeItem.topItemIds, ObjectTreeItem.topItemNames): item = QTreeWidgetItem(tree, [name]) item.setData(0, Qt.UserRole, id) topItems[id] = item optDEMChecked = False for layer in self.iface.legendInterface().layers(): parentId = ObjectTreeItem.parentIdByLayer(layer) if parentId is None: continue item = QTreeWidgetItem(topItems[parentId], [layer.name()]) isVisible = self._settings.get(parentId, {}).get(layer.id(), {}).get("visible", False) #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) if parentId == ObjectTreeItem.ITEM_OPTDEM and isVisible: optDEMChecked = True for id, item in topItems.iteritems(): if id != ObjectTreeItem.ITEM_OPTDEM or optDEMChecked: tree.expandItem(item) # disable additional DEM item which is selected as main DEM layerId = self._settings.get(ObjectTreeItem.ITEM_DEM, {}).get("comboBox_DEMLayer") if layerId: self.primaryDEMChanged(layerId) def saveProperties(self, item, page): properties = page.properties() parent = item.parent() if parent is None: # top level item self._settings[item.data(0, Qt.UserRole)] = properties else: # layer item parentId = parent.data(0, Qt.UserRole) if parentId not in self._settings: self._settings[parentId] = {} self._settings[parentId][item.data(0, Qt.UserRole)] = properties def setCurrentTreeItemByData(self, data): it = QTreeWidgetItemIterator(self.ui.treeWidget) while it.value(): if it.value().data(0, Qt.UserRole) == data: self.ui.treeWidget.setCurrentItem(it.value()) return True it += 1 return False def currentTemplateChanged(self, index=None): cbox = self.ui.comboBox_Template templateType = cbox.itemData(cbox.currentIndex(), Qt.UserRole) if templateType == self.templateType: return # hide items unsupported by template tree = self.ui.treeWidget for i, id in enumerate(ObjectTreeItem.topItemIds): hidden = (templateType == "sphere" and id != ObjectTreeItem.ITEM_CONTROLS) tree.topLevelItem(i).setHidden(hidden) # set current tree item if templateType == "sphere": tree.setCurrentItem(tree.topLevelItem(ObjectTreeItem.topItemIndex(ObjectTreeItem.ITEM_CONTROLS))) elif self.lastTreeItemData is None or not self.setCurrentTreeItemByData(self.lastTreeItemData): # restore selection tree.setCurrentItem(tree.topLevelItem(ObjectTreeItem.topItemIndex(ObjectTreeItem.ITEM_DEM))) # default selection for plain is DEM # display messages self.clearMessageBar() if templateType != "sphere": # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings() if QGis.QGIS_VERSION_INT >= 20300 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees, so terrain may not appear well.", QgsMessageBar.WARNING) self.templateType = templateType def currentObjectChanged(self, currentItem, previousItem): # save properties of previous item if previousItem and self.currentPage: self.saveProperties(previousItem, self.currentPage) self.currentItem = currentItem self.currentPage = None # hide text browser and all pages self.ui.textBrowser.hide() for page in self.pages.itervalues(): page.hide() parent = currentItem.parent() if parent is None: topItemIndex = currentItem.data(0, Qt.UserRole) pageType = self.topItemPages.get(topItemIndex, ppages.PAGE_NONE) page = self.pages.get(pageType, None) if page is None: self.showDescription(topItemIndex) return page.setup(self._settings.get(topItemIndex)) page.show() else: parentId = parent.data(0, Qt.UserRole) layerId = currentItem.data(0, Qt.UserRole) layer = QgsMapLayerRegistry.instance().mapLayer(unicode(layerId)) if layer is None: return layerType = layer.type() if layerType == QgsMapLayer.RasterLayer: page = self.pages[ppages.PAGE_DEM] page.setup(self._settings.get(parentId, {}).get(layerId, None), layer, False) elif layerType == QgsMapLayer.VectorLayer: page = self.pages[ppages.PAGE_VECTOR] page.setup(self._settings.get(parentId, {}).get(layerId, None), layer) else: return page.show() self.currentPage = page def objectItemChanged(self, item, column): parent = item.parent() if parent is None: return # checkbox of optional layer checked/unchecked if item == self.currentItem: if self.currentPage: # update enablement of property widgets self.currentPage.itemChanged(item) else: # select changed item self.ui.treeWidget.setCurrentItem(item) # set visible property #visible = item.data(0, Qt.CheckStateRole) == Qt.Checked #parentId = parent.data(0, Qt.UserRole) #layerId = item.data(0, Qt.UserRole) #self._settings.get(parentId, {}).get(layerId, {})["visible"] = visible def primaryDEMChanged(self, layerId): tree = self.ui.treeWidget parent = tree.topLevelItem(ObjectTreeItem.topItemIndex(ObjectTreeItem.ITEM_OPTDEM)) tree.blockSignals(True) for i in range(parent.childCount()): item = parent.child(i) isPrimary = item.data(0, Qt.UserRole) == layerId item.setDisabled(isPrimary) tree.blockSignals(False) def showDescription(self, topItemIndex): fragment = {ObjectTreeItem.ITEM_OPTDEM: "additional-dem", ObjectTreeItem.ITEM_POINT: "point", ObjectTreeItem.ITEM_LINE: "line", ObjectTreeItem.ITEM_POLYGON: "polygon"}.get(topItemIndex) url = "http://qgis2threejs.readthedocs.org/en/docs-release/ExportSettings.html" if fragment: url += "#" + fragment html = '<a href="{0}">Online Help</a> about this item'.format(url) self.ui.textBrowser.setHtml(html) self.ui.textBrowser.show() def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except ValueError: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def mapTo3d(self): canvas = self.iface.mapCanvas() mapSettings = canvas.mapSettings() if QGis.QGIS_VERSION_INT >= 20300 else canvas.mapRenderer() world = self._settings.get(ObjectTreeItem.ITEM_WORLD, {}) bs = float(world.get("lineEdit_BaseSize", def_vals.baseSize)) ve = float(world.get("lineEdit_zFactor", def_vals.zExaggeration)) vs = float(world.get("lineEdit_zShift", def_vals.zShift)) return MapTo3D(mapSettings, bs, ve, vs) def progress(self, percentage=None, statusMsg=None): ui = self.ui if percentage is not None: ui.progressBar.setValue(percentage) if percentage == 100: ui.progressBar.setVisible(False) ui.label_Status.setText("") else: ui.progressBar.setVisible(True) if statusMsg is not None: ui.label_Status.setText(statusMsg) ui.label_Status.repaint() QgsApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def run(self): self.endPointSelection() ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename and os.path.exists(filename): if QMessageBox.question(self, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return # export to web (three.js) export_settings = ExportSettings(self.pluginManager, self.localBrowsingMode) export_settings.loadSettings(self.settings()) export_settings.setMapCanvas(self.iface.mapCanvas()) err_msg = export_settings.checkValidity() if err_msg is not None: QMessageBox.warning(self, "Qgis2threejs", err_msg or "Invalid settings") return ui.pushButton_Run.setEnabled(False) ui.toolButton_Settings.setVisible(False) self.clearMessageBar() self.progress(0) if export_settings.exportMode == ExportSettings.PLAIN_MULTI_RES: # update quads and point on map canvas self.createRubberBands(export_settings.baseExtent, export_settings.quadtree()) # export ret = exportToThreeJS(export_settings, self.iface.legendInterface(), self.objectTypeManager, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if not ret: ui.toolButton_Settings.setVisible(True) return self.clearRubberBands() # store last selections settings = QSettings() settings.setValue("/Qgis2threejs/lastTemplate", export_settings.templatePath) settings.setValue("/Qgis2threejs/lastControls", export_settings.controls) # open web browser if not tools.openHTMLFile(export_settings.htmlfilename): ui.toolButton_Settings.setVisible(True) return # close dialog QDialog.accept(self) def reject(self): # save properties of current object item = self.ui.treeWidget.currentItem() if item and self.currentPage: self.saveProperties(item, self.currentPage) self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def help(self): url = "http://qgis2threejs.readthedocs.org/" import webbrowser webbrowser.open(url, new=2) # new=2: new tab if possible def startPointSelection(self): canvas = self.iface.mapCanvas() if self.previousMapTool != self.mapTool: self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.pages[ppages.PAGE_DEM].toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() if self.previousMapTool is not None: self.iface.mapCanvas().setMapTool(self.previousMapTool) def mapToolSet(self, mapTool): return #TODO: unstable if mapTool != self.mapTool and self.currentPage is not None: if self.currentPage.pageType == ppages.PAGE_DEM and self.currentPage.isPrimary: self.currentPage.toolButton_PointTool.setVisible(True) def createRubberBands(self, baseExtent, quadtree): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) quads = quadtree.quads() for quad in quads: geom = baseExtent.subrectangle(quad.rect).geometry() self.rb_quads.addGeometry(geom, None) self.log("Quad count: %d" % len(quads)) if not quadtree.focusRect: return # create a point with rubber band if quadtree.focusRect.width() == 0 or quadtree.focusRect.height() == 0: npt = quadtree.focusRect.center() self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(baseExtent.point(npt)) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = os.path.split(self.ui.lineEdit_OutputFilename.text())[0] if not directory: directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if not filename: return # append .html extension if filename doesn't have either .html or .htm if filename[-5:].lower() != ".html" and filename[-4:].lower() != ".htm": filename += ".html" self.ui.lineEdit_OutputFilename.setText(filename) def log(self, msg): if debug_mode: qDebug(msg)
class QgepProfileMapTool( QgepMapTool ): ''' The map tool used for PROFILE Allows to find the shortest path between several nodes. ''' profileChanged = pyqtSignal( object ) profile = QgepProfile() segmentOffset = 0 selectedPathPoints = [] pathPolyline = [] def __init__( self, canvas, button, networkAnalyzer ): QgepMapTool.__init__(self, canvas, button) settings = QSettings() helperLineColor = settings.value( "/QGEP/HelperLineColor", u'#FFD900' ) highlightColor = settings.value( "/QGEP/HighlightColor", u'#40FF40' ) self.networkAnalyzer = networkAnalyzer # Init rubberband to visualize current status self.rbHelperLine = QgsRubberBand( self.canvas ) self.rbHelperLine.setColor( QColor( helperLineColor ) ) self.rbHelperLine.setWidth( 2 ) self.rbHighlight = QgsRubberBand( self.canvas ) self.rbHighlight.setColor( QColor( highlightColor ) ) self.rbHighlight.setWidth( 5 ) self.profile.setRubberband( self.rbHighlight ) def setActive( self ): ''' activates this map tool ''' self.saveTool = self.canvas.mapTool() self.canvas.setMapTool( self ) def deactivate(self): ''' Called whenever this map tool is deactivated. Used to clean up code ''' QgepMapTool.deactivate(self) self.rubberBand.reset() self.rbHelperLine.reset() self.selectedPathPoints = [] self.pathPolyline = [] def findPath( self, pStart, pEnd ): ''' Tries to find the shortest path between pStart and pEnd. If it finds a path: * The path is visualized with a QgsRubberband * The profile plot is updated to represent the current path @param pStart: The id of the start point of the path @param pEnd: The id of the end point of the path ''' backupCursor = self.canvas.cursor() self.canvas.setCursor(Qt.WaitCursor) #try: ( vertices, edges ) = self.networkAnalyzer.shortestPath( pStart, pEnd ) self.appendProfile( vertices, edges ) # except: # pass self.canvas.setCursor(backupCursor) if len( vertices ) > 0: return True else: return False def appendProfile( self, vertices, edges ): ''' Appends to the current profile @param vertices: A collection of vertices to append @param edges: A collection of edges which connect the vertices ''' self.logger.debug( 'Append profile' ) self.logger.info( ' * ' + `len( vertices )` + ' vertices' ) for v in vertices: self.logger.debug( ' *' + `v`) self.logger.info( ' * ' + `len( edges )` + ' edges') for e in edges: self.logger.debug( ' *' + `e`) # Fetch all the needed edges in one batch edgeLayer = self.networkAnalyzer.getReachLayer() edgeAttrs = edgeLayer.dataProvider().attributeIndexes() edgeIds = [edge['feature'] for p1, p2, edge in edges] edgeFeatures = self.networkAnalyzer.getFeaturesById(edgeLayer, edgeAttrs, edgeIds, True) # We need some additional nodes, where we need to interpolate... interpolateNodesFrom = [edgeFeatures.attrAsUnicode( feat, u'from_obj_id_interpolate' ) for feat in edgeFeatures.asDict().values()] interpolateNodesTo = [edgeFeatures.attrAsUnicode( feat, u'to_obj_id_interpolate' ) for feat in edgeFeatures.asDict().values()] additionalIds = [self.networkAnalyzer.vertexIds[node] for node in interpolateNodesFrom] additionalIds += [self.networkAnalyzer.vertexIds[node] for node in interpolateNodesTo] # Now, fetch the nodes we need nodeLayer = self.networkAnalyzer.getNodeLayer() nodeIds = vertices + additionalIds nodeAttrs = nodeLayer.dataProvider().attributeIndexes() nodeFeatures = self.networkAnalyzer.getFeaturesById(nodeLayer, nodeAttrs, nodeIds, False) if len( vertices ) > 1: self.rubberBand.reset() elem = QgepProfileNodeElement( vertices[0], nodeFeatures, 0 ) self.profile.addElement( vertices[0], elem ) for p1, p2, edge in edges: fromOffset = self.segmentOffset toOffset = self.segmentOffset + edge['weight'] if 'reach' == edge['objType']: if self.profile.hasElement( edge['baseFeature'] ): self.profile[edge['baseFeature']].addSegment( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) else: elem = QgepProfileReachElement( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) self.profile.addElement(elem.objId, elem) elif 'special_structure' == edge['objType']: if self.profile.hasElement( edge['baseFeature'] ): self.profile[edge['baseFeature']].addSegment( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) else: elem = QgepProfileSpecialStructureElement( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) self.profile.addElement(elem.objId, elem) elem = QgepProfileNodeElement( p2, nodeFeatures, toOffset ) self.profile.addElement(p2, elem) self.segmentOffset = toOffset self.profileChanged.emit( self.profile ) # Create rubberband geometry for featId in edgeIds: self.pathPolyline.extend( edgeFeatures[featId].geometry().asPolyline() ) self.rubberBand.addGeometry( QgsGeometry.fromPolyline( self.pathPolyline ), nodeLayer ) self.profileChanged.emit( self.profile ) return True else: return False def mouseMoved( self, event ): ''' Mouse moved: update helper line @param event: The mouse event with coordinates and all ''' if len( self.selectedPathPoints ) > 0: self.rbHelperLine.reset() for point in self.selectedPathPoints: self.rbHelperLine.addPoint( point[1] ) mousePos = self.canvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y() ) self.rbHelperLine.addPoint( mousePos ) def rightClicked( self, event ): ''' Cancel any ongoing path selection @param event: The mouse event with coordinates and all ''' self.selectedPathPoints = [] self.pathPolyline = [] self.rbHelperLine.reset() self.profile.reset() self.segmentOffset = 0 def leftClicked( self, event ): ''' Select startpoint / intermediate point / endpoint @param event: The mouse event with coordinates and all ''' snappedPoint = self.networkAnalyzer.snapPoint( event ) if snappedPoint is not None: if len( self.selectedPathPoints ) > 0: pf = self.findPath( self.selectedPathPoints[-1][0], snappedPoint.snappedAtGeometry ) if pf: self.selectedPathPoints.append( ( snappedPoint.snappedAtGeometry, QgsPoint( snappedPoint.snappedVertex ) ) ) else: msg = self.msgBar.createMessage( 'No path found' ) self.msgBar.pushWidget( msg, QgsMessageBar.WARNING ) else: self.selectedPathPoints.append( ( snappedPoint.snappedAtGeometry, QgsPoint( snappedPoint.snappedVertex ) ) )
class SplitMapTool(QgsMapToolEdit): def __init__(self, canvas, layer, actionMoveVertices, actionAddVertices, actionRemoveVertices, actionMoveSegment, actionLineClose, actionLineOpen, actionMoveLine): super(SplitMapTool, self).__init__(canvas) self.canvas = canvas self.scene = canvas.scene() self.layer = layer self.actionMoveVertices = actionMoveVertices self.actionAddVertices = actionAddVertices self.actionRemoveVertices = actionRemoveVertices self.actionMoveSegment = actionMoveSegment self.actionLineClose = actionLineClose self.actionLineOpen = actionLineOpen self.actionMoveLine = actionMoveLine self.initialize() def initialize(self): try: self.canvas.renderStarting.disconnect(self.mapCanvasChanged) except: pass self.canvas.renderStarting.connect(self.mapCanvasChanged) try: self.layer.editingStopped.disconnect(self.stopCapturing) except: pass self.layer.editingStopped.connect(self.stopCapturing) self.selectedFeatures = self.layer.selectedFeatures() self.rubberBand = None self.tempRubberBand = None self.capturedPoints = [] self.capturing = False self.setCursor(Qt.CrossCursor) self.proj = QgsProject.instance() self.labels = [] self.vertices = [] self.calculator = QgsDistanceArea() self.calculator.setSourceCrs(self.layer.dataProvider().crs(), QgsProject.instance().transformContext()) self.calculator.setEllipsoid( self.layer.dataProvider().crs().ellipsoidAcronym()) self.drawingLine = False self.movingVertices = False self.addingVertices = False self.removingVertices = False self.movingSegment = False self.movingLine = False self.showingVertices = False self.movingVertex = -1 self.movingSegm = -1 self.movingLineInitialPoint = None self.lineClosed = False def restoreAction(self): self.addingVertices = False self.removingVertices = False self.movingVertices = False self.movingSegment = False self.movingLine = False self.showingVertices = False self.drawingLine = True self.movingVertex = -1 self.movingLineInitialPoint = None self.deleteVertices() self.redrawRubberBand() self.redrawTempRubberBand() self.canvas.scene().addItem(self.tempRubberBand) self.redrawActions() def mapCanvasChanged(self): self.redrawAreas() if self.showingVertices: self.redrawVertices() def canvasMoveEvent(self, event): if self.drawingLine and not self.lineClosed: if self.tempRubberBand != None and self.capturing: mapPoint = self.toMapCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPoint) self.redrawAreas(event.pos()) if self.movingVertices and self.movingVertex >= 0: layerPoint = self.toLayerCoordinates(self.layer, event.pos()) self.capturedPoints[self.movingVertex] = layerPoint if self.lineClosed and self.movingVertex == 0: self.capturedPoints[len(self.capturedPoints) - 1] = layerPoint self.redrawRubberBand() self.redrawVertices() self.redrawAreas() if self.movingSegment and self.movingSegm >= 0: currentPoint = self.toLayerCoordinates(self.layer, event.pos()) distance = self.distancePoint(currentPoint, self.movingLineInitialPoint) bearing = self.movingLineInitialPoint.azimuth(currentPoint) self.capturedPoints[self.movingSegm] = self.projectPoint( self.capturedPoints[self.movingSegm], distance, bearing) self.capturedPoints[self.movingSegm + 1] = self.projectPoint( self.capturedPoints[self.movingSegm + 1], distance, bearing) if self.lineClosed: if self.movingSegm == 0: self.capturedPoints[ len(self.capturedPoints) - 1] = self.projectPoint( self.capturedPoints[len(self.capturedPoints) - 1], distance, bearing) elif self.movingSegm == len(self.capturedPoints) - 2: self.capturedPoints[0] = self.projectPoint( self.capturedPoints[0], distance, bearing) self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.movingLineInitialPoint = currentPoint if self.movingLine and self.movingLineInitialPoint != None: currentPoint = self.toLayerCoordinates(self.layer, event.pos()) distance = self.distancePoint(currentPoint, self.movingLineInitialPoint) bearing = self.movingLineInitialPoint.azimuth(currentPoint) for i in range(len(self.capturedPoints)): self.capturedPoints[i] = self.projectPoint( self.capturedPoints[i], distance, bearing) self.redrawRubberBand() self.redrawAreas() self.movingLineInitialPoint = currentPoint def projectPoint(self, point, distance, bearing): rads = bearing * pi / 180.0 dx = distance * sin(rads) dy = distance * cos(rads) return QgsPointXY(point.x() + dx, point.y() + dy) def redrawAreas(self, mousePos=None): self.deleteLabels() if self.capturing and len(self.capturedPoints) > 0: for i in range(len(self.selectedFeatures)): geometry = QgsGeometry(self.selectedFeatures[i].geometry()) movingPoints = list(self.capturedPoints) if mousePos != None: movingPoints.append( self.toLayerCoordinates(self.layer, mousePos)) result, newGeometries, topoTestPoints = geometry.splitGeometry( movingPoints, self.proj.topologicalEditing()) self.addLabel(geometry) if newGeometries != None and len(newGeometries) > 0: for i in range(len(newGeometries)): self.addLabel(newGeometries[i]) def addLabel(self, geometry): area = self.calculator.measureArea(geometry) labelPoint = geometry.pointOnSurface().vertexAt(0) label = QGraphicsTextItem("%.2f" % round(area, 2)) label.setHtml( "<div style=\"color:#ffffff;background:#111111;padding:5px\">" + "%.2f" % round(area, 2) + " " + areaUnits[self.calculator.areaUnits()] + "</div>") point = self.toMapCoordinatesV2(self.layer, labelPoint) label.setPos(self.toCanvasCoordinates(QgsPointXY(point.x(), point.y()))) self.scene.addItem(label) self.labels.append(label) def deleteLabels(self): for i in range(len(self.labels)): self.scene.removeItem(self.labels[i]) self.labels = [] def canvasPressEvent(self, event): if self.movingVertices: for i in range(len(self.capturedPoints)): point = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex = self.toCanvasCoordinates( QgsPointXY(point.x(), point.y())) if self.distancePoint(event.pos(), currentVertex) <= maxDistanceHitTest: self.movingVertex = i break if self.movingSegment: for i in range(len(self.capturedPoints) - 1): vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex1 = self.toCanvasCoordinates( QgsPointXY(vertex1.x(), vertex1.y())) vertex2 = self.toMapCoordinates(self.layer, self.capturedPoints[i + 1]) currentVertex2 = self.toCanvasCoordinates( QgsPointXY(vertex2.x(), vertex2.y())) if self.distancePointLine( event.pos().x(), event.pos().y(), currentVertex1.x(), currentVertex1.y(), currentVertex2.x(), currentVertex2.y()) <= maxDistanceHitTest: self.movingSegm = i break self.movingLineInitialPoint = self.toLayerCoordinates( self.layer, event.pos()) def distancePoint(self, eventPos, vertexPos): return sqrt((eventPos.x() - vertexPos.x())**2 + (eventPos.y() - vertexPos.y())**2) def canvasReleaseEvent(self, event): if self.movingVertices or self.movingSegment or self.movingLine: if event.button() == Qt.RightButton: self.finishOperation() elif self.addingVertices: if event.button() == Qt.LeftButton: self.addVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() elif self.removingVertices: if event.button() == Qt.LeftButton: self.removeVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() else: if event.button() == Qt.LeftButton: if not self.lineClosed: if not self.capturing: self.startCapturing() self.addEndingVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() self.movingVertex = -1 self.movingSegm = -1 self.movingLineInitialPoint = None self.redrawActions() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.stopCapturing() if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: self.finishOperation() event.accept() self.redrawActions() def finishOperation(self): self.doSplit() self.stopCapturing() self.initialize() self.startCapturing() def doSplit(self): if self.capturedPoints != None: self.layer.splitFeatures(self.capturedPoints, self.proj.topologicalEditing()) def startCapturing(self): self.prepareRubberBand() self.prepareTempRubberBand() self.drawingLine = True self.capturing = True self.redrawActions() def prepareRubberBand(self): color = QColor("red") color.setAlphaF(0.78) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBand.setWidth(1) self.rubberBand.setColor(color) self.rubberBand.show() def prepareTempRubberBand(self): color = QColor("red") color.setAlphaF(0.78) self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRubberBand.setWidth(1) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() def redrawRubberBand(self): self.canvas.scene().removeItem(self.rubberBand) self.prepareRubberBand() for i in range(len(self.capturedPoints)): point = self.capturedPoints[i] if point.__class__ == QgsPoint: vertexCoord = self.toMapCoordinatesV2(self.layer, self.capturedPoints[i]) vertexCoord = QgsPointXY(vertexCoord.x(), vertexCoord.y()) else: vertexCoord = self.toMapCoordinates(self.layer, self.capturedPoints[i]) self.rubberBand.addPoint(vertexCoord) def redrawTempRubberBand(self): if self.tempRubberBand != None: self.tempRubberBand.reset(QgsWkbTypes.LineGeometry) self.tempRubberBand.addPoint( self.toMapCoordinates( self.layer, self.capturedPoints[len(self.capturedPoints) - 1])) def stopCapturing(self): self.deleteLabels() self.deleteVertices() if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.drawingLine = False self.movingVertices = False self.showingVertices = False self.capturing = False self.capturedPoints = [] self.canvas.refresh() self.redrawActions() def addEndingVertex(self, canvasPoint): mapPoint = self.toMapCoordinates(canvasPoint) layerPoint = self.toLayerCoordinates(self.layer, canvasPoint) self.rubberBand.addPoint(mapPoint) self.capturedPoints.append(layerPoint) self.tempRubberBand.reset(QgsWkbTypes.LineGeometry) self.tempRubberBand.addPoint(mapPoint) def removeLastVertex(self): if not self.capturing: return rubberBandSize = self.rubberBand.numberOfVertices() tempRubberBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if rubberBandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if rubberBandSize > 1: if tempRubberBandSize > 1: point = self.rubberBand.getPoint(0, rubberBandSize - 2) self.tempRubberBand.movePoint(tempRubberBandSize - 2, point) else: self.tempRubberBand.reset(self.bandType()) del self.capturedPoints[-1] def addVertex(self, pos): newCapturedPoints = [] for i in range(len(self.capturedPoints) - 1): newCapturedPoints.append(self.capturedPoints[i]) vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex1 = self.toCanvasCoordinates( QgsPointXY(vertex1.x(), vertex1.y())) vertex2 = self.toMapCoordinates(self.layer, self.capturedPoints[i + 1]) currentVertex2 = self.toCanvasCoordinates( QgsPointXY(vertex2.x(), vertex2.y())) distance = self.distancePointLine(pos.x(), pos.y(), currentVertex1.x(), currentVertex1.y(), currentVertex2.x(), currentVertex2.y()) if distance <= maxDistanceHitTest: layerPoint = self.toLayerCoordinates(self.layer, pos) newCapturedPoints.append(layerPoint) newCapturedPoints.append(self.capturedPoints[len(self.capturedPoints) - 1]) self.capturedPoints = newCapturedPoints self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.redrawActions() def removeVertex(self, pos): deletedFirst = False deletedLast = False newCapturedPoints = [] for i in range(len(self.capturedPoints)): vertex = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex = self.toCanvasCoordinates( QgsPointXY(vertex.x(), vertex.y())) if not self.distancePoint(pos, currentVertex) <= maxDistanceHitTest: newCapturedPoints.append(self.capturedPoints[i]) elif i == 0: deletedFirst = True elif i == len(self.capturedPoints) - 1: deletedLast = True self.capturedPoints = newCapturedPoints if deletedFirst and deletedLast: self.lineClosed = False self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.redrawActions() if len(self.capturedPoints) <= 2: self.stopRemovingVertices() def startMovingVertices(self): self.stopMovingLine() self.stopAddingVertices() self.stopRemovingVertices() self.stopMovingSegment() self.actionMoveVertices.setChecked(True) self.movingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopMovingVertices(self): self.movingVertices = False self.actionMoveVertices.setChecked(False) self.restoreAction() def startAddingVertices(self): self.stopMovingVertices() self.stopRemovingVertices() self.stopMovingLine() self.stopMovingSegment() self.actionAddVertices.setChecked(True) self.addingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopAddingVertices(self): self.addVertices = False self.actionAddVertices.setChecked(False) self.restoreAction() def startRemovingVertices(self): self.stopMovingVertices() self.stopAddingVertices() self.stopMovingLine() self.stopMovingSegment() self.actionRemoveVertices.setChecked(True) self.removingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopRemovingVertices(self): self.removingVertices = False self.actionRemoveVertices.setChecked(False) self.restoreAction() def startMovingSegment(self): self.stopMovingVertices() self.stopMovingLine() self.stopAddingVertices() self.stopRemovingVertices() self.actionMoveSegment.setChecked(True) self.movingSegment = True self.showingVertices = False self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopMovingSegment(self): self.movingSegment = False self.actionMoveSegment.setChecked(False) self.restoreAction() def startMovingLine(self): self.stopMovingVertices() self.stopAddingVertices() self.stopRemovingVertices() self.stopMovingSegment() self.actionMoveLine.setChecked(True) self.movingLine = True self.showingVertices = False self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawAreas() self.redrawActions() def stopMovingLine(self): self.actionMoveLine.setChecked(False) self.restoreAction() def lineClose(self): self.lineClosed = True self.capturedPoints.append(self.capturedPoints[0]) self.redrawRubberBand() self.redrawTempRubberBand() self.redrawAreas() self.redrawActions() def lineOpen(self): self.lineClosed = False del self.capturedPoints[-1] self.redrawRubberBand() self.redrawTempRubberBand() self.redrawAreas() self.redrawActions() def showVertices(self): for i in range(len(self.capturedPoints)): vertexc = self.toMapCoordinates(self.layer, self.capturedPoints[i]) vertexCoords = self.toCanvasCoordinates( QgsPointXY(vertexc.x(), vertexc.y())) if i == self.movingVertex: vertex = self.scene.addRect(vertexCoords.x() - 5, vertexCoords.y() - 5, 10, 10, QPen(QColor("green")), QBrush(QColor("green"))) self.vertices.append(vertex) elif i == len(self.capturedPoints ) - 1 and self.movingVertex == 0 and self.lineClosed: vertex = self.scene.addRect(vertexCoords.x() - 5, vertexCoords.y() - 5, 10, 10, QPen(QColor("green")), QBrush(QColor("green"))) self.vertices.append(vertex) else: vertex = self.scene.addRect(vertexCoords.x() - 4, vertexCoords.y() - 4, 8, 8, QPen(QColor("red")), QBrush(QColor("red"))) self.vertices.append(vertex) def deleteVertices(self): for i in range(len(self.vertices)): self.scene.removeItem(self.vertices[i]) self.vertices = [] def lineMagnitude(self, x1, y1, x2, y2): return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)) def distancePointLine(self, px, py, x1, y1, x2, y2): magnitude = self.lineMagnitude(x1, y1, x2, y2) if magnitude < 0.00000001: distance = 9999 return distance u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) u = u1 / (magnitude * magnitude) if (u < 0.00001) or (u > 1): ix = self.lineMagnitude(px, py, x1, y1) iy = self.lineMagnitude(px, py, x2, y2) if ix > iy: distance = iy else: distance = ix else: ix = x1 + u * (x2 - x1) iy = y1 + u * (y2 - y1) distance = self.lineMagnitude(px, py, ix, iy) return distance def redrawVertices(self): self.deleteVertices() self.showVertices() def redrawActions(self): self.redrawActionMoveVertices() self.redrawActionAddVertices() self.redrawActionRemoveVertices() self.redrawActionMoveSegment() self.redrawActionLineClose() self.redrawActionLineOpen() self.redrawActionMoveLine() def redrawActionMoveVertices(self): self.actionMoveVertices.setEnabled(False) if len(self.capturedPoints) > 0: self.actionMoveVertices.setEnabled(True) def redrawActionAddVertices(self): self.actionAddVertices.setEnabled(False) if len(self.capturedPoints) >= 2: self.actionAddVertices.setEnabled(True) def redrawActionRemoveVertices(self): self.actionRemoveVertices.setEnabled(False) if len(self.capturedPoints) > 2: self.actionRemoveVertices.setEnabled(True) def redrawActionMoveSegment(self): self.actionMoveSegment.setEnabled(False) if len(self.capturedPoints) > 2: self.actionMoveSegment.setEnabled(True) def redrawActionLineClose(self): self.actionLineClose.setEnabled(False) if not self.lineClosed and len(self.capturedPoints) >= 3: self.actionLineClose.setEnabled(True) def redrawActionLineOpen(self): self.actionLineOpen.setEnabled(False) if self.lineClosed: self.actionLineOpen.setEnabled(True) def redrawActionMoveLine(self): self.actionMoveLine.setEnabled(False) if len(self.capturedPoints) > 0: self.actionMoveLine.setEnabled(True)
class GeodesicMeasureDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent): super(GeodesicMeasureDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.canvas = iface.mapCanvas() self.pointDigitizerDialog = AddMeasurePointWidget(self, iface, parent) qset = QSettings() self.manualEntryButton.setIcon( QIcon(os.path.dirname(__file__) + "/images/manualpoint.png")) self.manualEntryButton.clicked.connect(self.showManualEntryDialog) self.restoreGeometry( qset.value("ShapeTools/MeasureDialogGeometry", QByteArray(), type=QByteArray)) self.closeButton.clicked.connect(self.closeDialog) self.newButton.clicked.connect(self.newDialog) self.saveToLayerButton.clicked.connect(self.saveToLayer) self.saveToLayerButton.setEnabled(False) self.unitsComboBox.addItems(DISTANCE_LABELS) self.tableWidget.setColumnCount(3) self.tableWidget.setSortingEnabled(False) self.tableWidget.setHorizontalHeaderLabels( [tr('Heading To'), tr('Heading From'), tr('Distance')]) self.unitsComboBox.activated.connect(self.unitsChanged) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.lastMotionPt = None self.unitsChanged() self.currentDistance = 0.0 self.pointRb = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.pointRb.setColor(settings.rubberBandColor) self.pointRb.setIconSize(10) self.lineRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.lineRb.setColor(settings.rubberBandColor) self.lineRb.setWidth(3) self.tempRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRb.setColor(settings.rubberBandColor) self.tempRb.setWidth(3) def showManualEntryDialog(self): self.pointDigitizerDialog.show() def ready(self): return self.activeMeasuring def stop(self): self.activeMeasuring = False self.lastMotionPt = None def closeEvent(self, event): self.closeDialog() def closeDialog(self): self.clear() QSettings().setValue("ShapeTools/MeasureDialogGeometry", self.saveGeometry()) self.close() self.pointDigitizerDialog.closeDialog() def newDialog(self): self.clear() self.initGeodLabel() def initGeodLabel(self): label = tr('Ellipsoid: ') + settings.ellipseDescription self.geodLabel.setText(label) def keyPressed(self, key): index = len(self.capturedPoints) if index <= 0: return if key == Qt.Key_Escape: self.endRubberband() if self.motionReady(): if self.lastMotionPt is None: return (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.lastMotionPt) else: if index < 2: return (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 2], self.capturedPoints[index - 1]) distance = self.unitDistance(distance) clipboard = QApplication.clipboard() if key == Qt.Key_1 or key == Qt.Key_F: s = '{:.{prec}f}'.format(startAngle, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Heading to {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_2 or key == Qt.Key_T: s = '{:.{prec}f}'.format(endAngle, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Heading from {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_3 or key == Qt.Key_D: s = '{:.{prec}f}'.format(distance, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Distance {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_4 or key == Qt.Key_A: total = 0.0 num = len(self.capturedPoints) for i in range(1, num): (d, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) total += d total = self.unitDistance(total) # Add in the motion distance if self.motionReady(): total += distance s = '{:.{prec}f}'.format(total, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Total distance {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) else: return def unitsChanged(self): label = "Distance [{}]".format( DISTANCE_LABELS[self.unitsComboBox.currentIndex()]) item = QTableWidgetItem(label) self.tableWidget.setHorizontalHeaderItem(2, item) ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: item = QTableWidgetItem('{:.4f}'.format( self.unitDistance(self.distances[i]))) self.tableWidget.setItem(i, 2, item) i += 1 self.formatTotal() def motionReady(self): if len(self.capturedPoints) > 0 and self.activeMeasuring: return True return False def addPoint(self, pt, button): self.currentDistance = 0 index = len(self.capturedPoints) if index > 0 and pt == self.capturedPoints[index - 1]: # the clicked point is the same as the previous so just ignore it return self.capturedPoints.append(pt) # Add rubber band points canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) ptCanvas = transform.transform(pt.x(), pt.y()) self.pointRb.addPoint(ptCanvas, True) # If there is more than 1 captured point add it to the table if index > 0: self.saveToLayerButton.setEnabled(True) (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.capturedPoints[index]) self.distances.append(distance) self.insertParams(index, distance, startAngle, endAngle) # Add Rubber Band Line linePts = self.getLinePts(distance, self.capturedPoints[index - 1], self.capturedPoints[index]) self.lineRb.addGeometry(QgsGeometry.fromPolylineXY(linePts), None) self.formatTotal() def endRubberband(self): index = len(self.capturedPoints) if index <= 0: return if index == 1: self.newDialog() return if self.motionReady(): if self.lastMotionPt is not None: self.lastMotionPt = None self.tempRb.reset(QgsWkbTypes.LineGeometry) self.tableWidget.setRowCount(self.tableWidget.rowCount() - 1) self.stop() self.currentDistance = 0 self.formatTotal() def inMotion(self, pt): index = len(self.capturedPoints) if index <= 0: return (self.currentDistance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], pt) self.insertParams(index, self.currentDistance, startAngle, endAngle) self.formatTotal() linePts = self.getLinePts(self.currentDistance, self.capturedPoints[index - 1], pt) self.lastMotionPt = pt self.tempRb.setToGeometry(QgsGeometry.fromPolylineXY(linePts), None) def calcParameters(self, pt1, pt2): gline = geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x()) az2 = (gline['azi2'] + 180) % 360.0 if az2 > 180: az2 = az2 - 360.0 az1 = gline['azi1'] # Check to see if the azimuth values should be in the range or 0 to 360 # The default is -180 to 180 if settings.mtAzMode: if az1 < 0: az1 += 360.0 if az2 < 0: az2 += 360 return (gline['s12'], az1, az2) def getLinePts(self, distance, pt1, pt2): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) pt1c = transform.transform(pt1.x(), pt1.y()) pt2c = transform.transform(pt2.x(), pt2.y()) if distance < 10000: return [pt1c, pt2c] gline = geod.InverseLine(pt1.y(), pt1.x(), pt2.y(), pt2.x()) n = int(math.ceil(distance / 10000.0)) if n > 20: n = 20 seglen = distance / n pts = [pt1c] for i in range(1, n): s = seglen * i g = gline.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) ptc = transform.transform(g['lon2'], g['lat2']) pts.append(ptc) pts.append(pt2c) return pts def saveToLayer(self): units = self.unitDesignator() canvasCrs = self.canvas.mapSettings().destinationCrs() fields = QgsFields() fields.append(QgsField("label", QVariant.String)) fields.append(QgsField("value", QVariant.Double)) fields.append(QgsField("units", QVariant.String)) fields.append(QgsField("heading_to", QVariant.Double)) fields.append(QgsField("heading_from", QVariant.Double)) fields.append(QgsField("total_dist", QVariant.Double)) layer = QgsVectorLayer("LineString?crs={}".format(canvasCrs.authid()), "Measurements", "memory") dp = layer.dataProvider() dp.addAttributes(fields) layer.updateFields() num = len(self.capturedPoints) total = 0.0 for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) total += distance total = self.unitDistance(total) for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) pts = self.getLinePts(distance, self.capturedPoints[i - 1], self.capturedPoints[i]) distance = self.unitDistance(distance) feat = QgsFeature(layer.fields()) feat.setAttribute( 0, "{:.{prec}f} {}".format( distance, units, prec=settings.saveToLayerSignificantDigits)) feat.setAttribute(1, distance) feat.setAttribute(2, units) feat.setAttribute(3, startA) feat.setAttribute(4, endA) feat.setAttribute(5, total) feat.setGeometry(QgsGeometry.fromPolylineXY(pts)) dp.addFeatures([feat]) label = QgsPalLayerSettings() label.fieldName = 'label' try: label.placement = QgsPalLayerSettings.Line except Exception: label.placement = QgsPalLayerSettings.AboveLine format = label.format() format.setColor(settings.measureTextColor) format.setNamedStyle('Bold') label.setFormat(format) labeling = QgsVectorLayerSimpleLabeling(label) layer.setLabeling(labeling) layer.setLabelsEnabled(True) renderer = layer.renderer() renderer.symbol().setColor(settings.measureLineColor) renderer.symbol().setWidth(0.5) layer.updateExtents() QgsProject.instance().addMapLayer(layer) def insertParams(self, position, distance, startAngle, endAngle): if position > self.tableWidget.rowCount(): self.tableWidget.insertRow(position - 1) item = QTableWidgetItem('{:.4f}'.format(self.unitDistance(distance))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 2, item) item = QTableWidgetItem('{:.4f}'.format(startAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 0, item) item = QTableWidgetItem('{:.4f}'.format(endAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 1, item) def formatTotal(self): total = self.currentDistance ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: total += self.distances[i] i += 1 self.distanceLineEdit.setText('{:.2f}'.format( self.unitDistance(total))) def updateRBColor(self): self.pointRb.setColor(settings.rubberBandColor) self.lineRb.setColor(settings.rubberBandColor) self.tempRb.setColor(settings.rubberBandColor) def clear(self): self.tableWidget.setRowCount(0) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.currentDistance = 0.0 self.distanceLineEdit.setText('') self.pointRb.reset(QgsWkbTypes.PointGeometry) self.lineRb.reset(QgsWkbTypes.LineGeometry) self.tempRb.reset(QgsWkbTypes.LineGeometry) self.saveToLayerButton.setEnabled(False) self.updateRBColor() def unitDistance(self, distance): units = self.unitsComboBox.currentIndex() if units == 0: # kilometers return distance / 1000.0 elif units == 1: # meters return distance elif units == 2: # centimeters return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceCentimeters) elif units == 3: # miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceMiles) elif units == 4: # yards return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceYards) elif units == 5: # feet return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) elif units == 6: # inches return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) * 12 elif units == 7: # nautical miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceNauticalMiles) def unitDesignator(self): units = self.unitsComboBox.currentIndex() return unitsAbbr[units]
class SimpleIntersectionMapTool(QgsMapTool): def __init__(self, iface): self.iface = iface self.mapCanvas = iface.mapCanvas() QgsMapTool.__init__(self, self.mapCanvas) self.settings = MySettings() self.rubber = QgsRubberBand(self.mapCanvas, QGis.Point) def deactivate(self): self.rubber.reset() QgsMapTool.deactivate(self) def activate(self): QgsMapTool.activate(self) self.rubber.setWidth(self.settings.value("rubberWidth")) self.rubber.setColor(self.settings.value("rubberColor")) self.checkLayer() def canvasMoveEvent(self, mouseEvent): # put the observations within tolerance in the rubber band self.rubber.reset(QGis.Point) match = self.snap_to_intersection(mouseEvent.pos()) if match.type() == QgsPointLocator.Vertex and match.layer() is None: self.rubber.addPoint(match.point()) def canvasPressEvent(self, mouseEvent): self.rubber.reset() match = self.snap_to_intersection(mouseEvent.pos()) if match.type() != QgsPointLocator.Vertex or match.layer() is not None: return layer = self.checkLayer() if layer is None: return f = QgsFeature() initFields = layer.dataProvider().fields() f.setFields(initFields) f.initAttributes(initFields.size()) f.setGeometry(QgsGeometry().fromPoint(match.point())) layer.editBuffer().addFeature(f) layer.triggerRepaint() def snap_to_intersection(self, pos): """ Temporarily override snapping config and snap to vertices and edges of any editable vector layer, to allow selection of node for editing (if snapped to edge, it would offer creation of a new vertex there). """ map_point = self.toMapCoordinates(pos) tol = QgsTolerance.vertexSearchRadius(self.canvas().mapSettings()) snap_type = QgsPointLocator.Type(QgsPointLocator.Edge) snap_layers = [] for layer in self.canvas().layers(): if not isinstance(layer, QgsVectorLayer): continue snap_layers.append(QgsSnappingUtils.LayerConfig( layer, snap_type, tol, QgsTolerance.ProjectUnits)) snap_util = self.canvas().snappingUtils() old_layers = snap_util.layers() old_mode = snap_util.snapToMapMode() old_inter = snap_util.snapOnIntersections() snap_util.setLayers(snap_layers) snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) snap_util.setSnapOnIntersections(True) m = snap_util.snapToMap(map_point) snap_util.setLayers(old_layers) snap_util.setSnapToMapMode(old_mode) snap_util.setSnapOnIntersections(old_inter) return m def checkLayer(self): # check output layer is defined layerid = self.settings.value("simpleIntersectionLayer") layer = QgsMapLayerRegistry.instance().mapLayer(layerid) if not self.settings.value("simpleIntersectionWritePoint") or layer is None: self.iface.messageBar().pushMessage("Intersect It", "You must define an output layer for simple intersections", QgsMessageBar.WARNING, 3) self.mapCanvas.unsetMapTool(self) return None if not layer.isEditable(): self.iface.messageBar().pushMessage("Intersect It", "The output layer <b>%s must be editable</b>" % layer.name(), QgsMessageBar.WARNING, 3) self.mapCanvas.unsetMapTool(self) return None return layer
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 PipelinePlanner: """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 self.iface = iface #create reference to map.Canvas self.canvas = self.iface.mapCanvas() # initialize plugins directory self.plugin_dir = os.path.dirname(__file__) # initialize locale #Initialize QgsMapToolEmitPoint class, and pass map canvas where we will emit the point self.addPipelinePoint = QgsMapToolEmitPoint(self.canvas) #create instance of rubberband class self.rbPipeline = QgsRubberBand(self.canvas) self.rbPipeline.setColor(Qt.red) self.rbPipeline.setWidth(4) locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'PipelinePlanner_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Pipeline Planner') #create instance of results dialog self.dlg = PipelinePlannerDialog() # Check if plugin was started the first time in current QGIS session # Must be set in initGui() to survive plugin reloads self.first_start = None self.dlg = PipelinePlannerDialog() self.dlg.tblImpacts.setColumnWidth(1, 50) self.dlg.tblImpacts.setColumnWidth(2, 225) self.dlg.tblImpacts.setColumnWidth(3, 75) # 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('PipelinePlanner', 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 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 show 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: # Adds plugin icon to Plugins toolbar self.iface.addToolBarIcon(action) if add_to_menu: self.iface.addPluginToVectorMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/pipeline_planner/icon.png' self.add_action(icon_path, text=self.tr(u'Pipeline Planner'), callback=self.run, parent=self.iface.mainWindow()) # will be set False in run() self.first_start = True #connect to mapCanvas click event self.addPipelinePoint.canvasClicked.connect(self.evaluatePipeline) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginVectorMenu(self.tr(u'&Pipeline Planner'), action) self.iface.removeToolBarIcon(action) def run(self): """Run method that performs all the real work""" # set the canvas maptool self.canvas.setMapTool(self.addPipelinePoint) #method to control drawning proces def evaluatePipeline(self, point, button): if button == Qt.LeftButton: self.rbPipeline.addPoint(point) self.rbPipeline.show() elif button == Qt.RightButton: pipeline = self.rbPipeline.asGeometry() # QMessageBox.information(None, "Pipeline", pipeline.asWkt()) self.dlg.tblImpacts.setRowCount(0) #create reference to raptor_buffer layer lyrRaptor = QgsProject.instance().mapLayersByName( "Raptor Buffer")[0] raptors = lyrRaptor.getFeatures(pipeline.boundingBox()) for raptor in raptors: valConstraint = raptor.attribute("recentspec") valID = raptor.attribute("Nest_ID") valStatus = raptor.attribute("recentstat") valDistance = pipeline.distance(raptor.geometry().centroid()) row = self.dlg.tblImpacts.rowCount() if raptor.geometry().intersects(pipeline): self.dlg.tblImpacts.insertRow(row) self.dlg.tblImpacts.setItem( row, 0, QTableWidgetItem(valConstraint)) self.dlg.tblImpacts.setItem(row, 1, QTableWidgetItem(str(valID))) self.dlg.tblImpacts.setItem(row, 2, QTableWidgetItem(valStatus)) self.dlg.tblImpacts.setItem( row, 3, QTableWidgetItem("{:4.5f}".format(valDistance))) self.dlg.show() self.rbPipeline.reset()
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() 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 canvasPressEvent(self, e): ''' When the canvas is pressed the tool finishes its job ''' 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 ''' self.endPoint = self.toMapCoordinates( e.pos() ) 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) 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): ''' Draws a rectangle in the canvas ''' self.rubberBand.reset(QGis.Polygon) x = startPoint.x() y = startPoint.y() point1 = QgsPoint(x - param, y - param) point2 = QgsPoint(x - param, y + param) point3 = QgsPoint(x + param, y + param) point4 = QgsPoint(x + param, y - param) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) self.rubberBand.show() def deactivate(self): ''' Deactivates the tool and hides the rubber band ''' self.rubberBand.hide() QgsMapTool.deactivate(self) 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 MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.snapping = True icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.defaultextent = None self.current_form = None self.last_form = None 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) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.snappingutils = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snappingutils) threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) 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(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) smallmode = roam.config.settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) 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.gpsMarker = GPSMarker(self.canvas) self.gpsMarker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(88, 64, 173, 50)) self.currentfeatureband.setOutlineColour(QColor(88, 64, 173)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.refresh_map.connect(self.refresh_map) 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.gpslabelposition = QLabel("") self.gpslabelaveraging = QLabel('') # averaging self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addWidget(self.gpslabelposition) self.statusbar.addWidget(self.gpslabelaveraging) # averaging self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.update_status_label) self.canvas.scaleChanged.connect(self.update_status_label) self.connectButtons() scalebar_enabled = roam.config.settings.get('scale_bar', False) self.scalebar_enabled = False if scalebar_enabled: roam.utils.warning( "Unsupported feature: Scale bar support not ported to QGIS 3 API yet." ) RoamEvents.raisemessage( "Unsupported feature", "Scale bar support not ported to QGIS 3 API yet", level=RoamEvents.CRITICAL) self.scalebar_enabled = False # self.scalebar = ScaleBarItem(self.canvas) # self.canvas.scene().addItem(self.scalebar) def clear_plugins(self) -> None: """ Clear all the plugin added toolbars from the map interface. """ toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): toolbar.unload() self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames) -> None: """ Add the given plugins to to the mapping interface. Adds the toolbars the plugin exposes as new toolbars for the user. :param pluginnames: The names of the plugins to load. Must already be loaded by the plugin loader """ 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): """ Load the plugin toolbars into the mapping interface. :param toolbars: The list of toolbars class objects to load. :return: """ 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: """ self.snapping = snapping if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ self.snapping = not self.snapping try: self.canvas.mapTool().toggle_snapping() except AttributeError: pass RoamEvents.snappingChanged.emit(self.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_fixed_label(self, fixed, gpsinfo): if not fixed: self.gpslabel.setText("GPS: Acquiring fix") self.gpslabelposition.setText("") self.gpslabelaveraging.setText('') # averaging quality_mappings = { 0: "invalid", 1: "GPS", 2: "DGPS", 3: "PPS", 4: "Real Time Kinematic", 5: "Float RTK", 6: "Estimated", 7: "Manual input mode", 8: "Simulation mode" } 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. """ if not self.gps.connected: return fixtype = self.quality_mappings.get(gpsinfo.quality, "") self.gpslabel.setText( "DOP P:<b>{0:.2f}</b> H:<b>{1:.2f}</b> V:<b>{2:.2f}</b> " "Fix: <b>{3}</b> " "Sats: <b>{4}</b> ".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop, fixtype, gpsinfo.satellitesUsed)) places = roam.config.settings.get("gpsplaces", 8) # if averaging, don't show this if not roam.config.settings.get('gps_averaging', True): self.gpslabelposition.setText("X: <b>{x:.{places}f}</b> " "Y: <b>{y:.{places}f}</b> " "Z: <b>{z}m</b> ".format( x=position.x(), y=position.y(), z=position.z(), places=places)) else: self.gpslabelposition.setText('') # --- averaging ------------------------------------------------------- # if turned on if roam.config.settings.get('gps_averaging', True): time = roam.config.settings.get('gps_averaging_start_time', '') # if currently happening if roam.config.settings.get('gps_averaging_in_action', True): time = datetime.now().replace(microsecond=0) - time.replace( microsecond=0) count = roam.config.settings.get('gps_averaging_measurements', int) avglabel = 'AVG: <b>%s / %s</b>' % (time, count) else: avglabel = '' self.gpslabelaveraging.setText(avglabel) # --------------------------------------------------------------------- def gps_disconnected(self): self.gpslabel.setText("GPS: Not Active") self.gpslabelposition.setText("") self.gpslabelaveraging.setText("") self.gpsMarker.hide() def zoom_to_feature(self, feature): """ Zoom to the given feature in the map. :param feature: :return: """ box = feature.geometry().boundingBox() xmin, xmax, ymin, ymax = box.xMinimum(), box.xMaximum(), box.yMinimum( ), box.yMaximum() xmin -= 5 xmax += 5 ymin -= 5 ymax += 5 box = QgsRectangle(xmin, ymin, xmax, ymax) self.canvas.setExtent(box) self.canvas.refresh() def update_status_label(self, *args) -> None: """ 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) -> None: """ Refresh the map """ self.canvas.refresh() def updatescale(self) -> None: """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) @property def crs(self) -> QgsCoordinateReferenceSystem: """ Get the CRS used that is being used in the canvas :return: The QgsCoordinateReferenceSystem that is used by the canvas """ return self.canvas.mapSettings().destinationCrs() 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.items(): 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 rubber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.values(): 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.currentLayer().startEditing() self.canvas.mapTool().setEditMode(True, feature.geometry(), feature) break self.editfeaturestack.append((form, feature)) self.save_current_form() self.load_form(form) trigger_default_action() def save_current_form(self): self.last_form = self.current_form def restore_last_form(self): self.load_form(self.last_form) 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() if hasattr(tool, "clearBand"): tool.clearBand() 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 smallmode = settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsfixed.connect(self.update_gps_fixed_label) self.gps.gpsposition.connect(self.update_gps_label) self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) self.gpsMarker.setgps(self.gps) self.actionGPS.setgps(gps) 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.lastgpsposition = position.clone() rect = QgsRectangle(QgsPointXY(position), QgsPointXY(position)) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(QgsPointXY(position)): self.zoom_to_location(position) self.gpsMarker.show() self.gpsMarker.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(QgsPointXY(position), QgsPointXY(position)) self.canvas.setExtent(rect) self.canvas.refresh() 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.snappingutils.setConfig(QgsProject.instance().snappingConfig()) 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 = roam.api.utils.layers() 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() red = QgsProject.instance().readNumEntry("Gui", "/CanvasColorRedPart", 255)[0] green = QgsProject.instance().readNumEntry("Gui", "/CanvasColorGreenPart", 255)[0] blue = QgsProject.instance().readNumEntry("Gui", "/CanvasColorBluePart", 255)[0] myColor = QColor(red, green, blue) self.canvas.setCanvasColor(myColor) 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(self.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 = QgsMapToolPan(self.canvas) self.infoTool = InfoTool(self.canvas) self.infoTool.setAction(self.actionInfo) self.zoomInTool.setAction(self.actionZoom_In) self.zoomOutTool.setAction(self.actionZoom_Out) self.panTool.setAction(self.actionPan) 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.toggle_raster_layers) 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. """ if self.defaultextent: 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.clear_capture_tools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) self.current_form = form def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ tool = form.getMaptool()(self.canvas, form.settings) 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(self.show_invalid_geometry_message) def show_invalid_geometry_message(self, message) -> None: """ Shows the message to the user if the there is a invalid geometry capture. :param message: The message to show the user. """ RoamEvents.raisemessage("Invalid geometry capture", message, level=RoamEvents.CRITICAL) if self.canvas.currentLayer() is not None: self.canvas.currentLayer().rollBack() RoamEvents.editgeometry_invalid.emit() def add_new_feature(self, form, geometry: QgsGeometry): """ Add a new new feature to the given layer :param form: The form to use for the new feature. :param geometry: The new geometry to create the feature for. """ # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if geometry.isMultipart(): geometry.convertToMultiType() # Transform the new geometry back into the map layers geometry if it's needed transform = self.canvas.mapSettings().layerTransform(layer) if transform.isValid(): geometry.transform(transform, QgsCoordinateTransform.ReverseTransform) 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, None) self.restore_last_form() def clear_capture_tools(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 toggle_raster_layers(self) -> None: """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh dlg = PickActionDialog(msg="Raster visibility") actions = [ (":/icons/raster_0", "Off", partial(self._set_basemaps_opacity, 0), "photo_off"), (":/icons/raster_25", "25%", partial(self._set_basemaps_opacity, .25), "photo_25"), (":/icons/raster_50", "50%", partial(self._set_basemaps_opacity, .50), "photo_50"), (":/icons/raster_75", "75%", partial(self._set_basemaps_opacity, .75), "photo_75"), (":/icons/raster_100", "100%", partial(self._set_basemaps_opacity, 1), "photo_100"), ] # ":/icons/raster_100"), "100%", self, triggered=partial(self._set_raster_layer_value, 1), # objectName="photo_100") dialog_actions = [] for action in actions: icon = QIcon(action[0]) qaction = QAction(icon, action[1], self, triggered=action[2], objectName=action[3]) dialog_actions.append(qaction) dlg.addactions(dialog_actions) dlg.exec_() def _set_basemaps_opacity(self, value=0) -> None: """ Set the opacity for all basemap raster layers. :param value: The opacity value betwen 0 and 1 """ tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): layer = node.layer() if node.layer().type() == QgsMapLayer.RasterLayer: if value > 0: node.setItemVisibilityChecked(Qt.Checked) renderer = layer.renderer() renderer.setOpacity(value) if value == 0: node.setItemVisibilityChecked(Qt.Unchecked) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ # TODO Review cleanup # self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clear_capture_tools() for action in self.layerbuttons: self.editgroup.removeAction(action)
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 GraphSelectTool(QgsMapToolEmitPoint): mouseMove = pyqtSignal(QgsPointXY) mouseClicked = pyqtSignal(QgsPointXY) selectionChanged = pyqtSignal() def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(QColor(255,0,0,50)) self.rb.setFillColor(QColor(255,0,0,50)) self.rb.setWidth(1) self.drawrubber = False self.rubberPointX = 0 self.rubberPointY = 0 self.layer = None def setLayer(self, layer): self.layer = layer def reset(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, e): self.rubberPointX = e.x() self.rubberPointY = e.y() self.drawrubber= True def canvasReleaseEvent(self, e): myOriginalPoint = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(e.x(), e.y())) self.mouseClicked.emit(myOriginalPoint) self.reset() self.drawrubber = False myOldPoint = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(self.rubberPointX, self.rubberPointY)) rect = QgsRectangle(myOldPoint, myOriginalPoint) if(self.layer.select(rect)): self.selectionChanged.emit() self.canvas.refresh() def canvasMoveEvent(self, e): myOriginalPoint = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(e.x(), e.y())) self.mouseMove.emit(myOriginalPoint) if(self.drawrubber): myPoint1 = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(e.x(),e.y())) myPoint2 = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(self.rubberPointX,e.y())) myPoint3 = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(self.rubberPointX,self.rubberPointY)) myPoint4 = QgsPointXY(self.canvas.getCoordinateTransform().toMapCoordinates(e.x(),self.rubberPointY)) self.reset() #convert screen coordinates to map coordinates self.rb.addPoint( myPoint1, False ) self.rb.addPoint( myPoint2, False ) self.rb.addPoint( myPoint3, False ) self.rb.addPoint( myPoint4, True ) #true - update canvas self.rb.show()
class MeasureAngleTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.middle_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) self.rubber_band_curve = QgsRubberBand(self.canvas) self.rubber_band_curve.setWidth(2) self.rubber_band_curve.setColor(QColor(255, 153, 0, 100)) crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs( crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): self.msglog.logMessage("") self.start_point = self.middle_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.rubber_band_curve.reset() def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point and self.middle_point: angle_start_to_middle = self.distance_calc.bearing( self.middle_point, self.start_point) angle_end_to_middle = self.distance_calc.bearing( self.middle_point, point) angle = degrees(angle_end_to_middle - angle_start_to_middle) if angle < -180: angle = 360 + angle elif angle > 180: angle = angle - 360 anglemsg = QMessageBox(self.parent()) anglemsg.finished.connect(self.deactivate) anglemsg.setWindowTitle("Measure angle tool") anglemsg.setText("Angle: {:.3F} º".format(abs(angle))) anglemsg.exec() self.finish() elif self.start_point: self.middle_point = point self.rubber_band.addPoint(self.middle_point) self.rubber_band_points.addPoint(self.middle_point) else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) if self.start_point and self.middle_point and not self.end_point: angle_start_to_middle = self.distance_calc.bearing( self.middle_point, self.start_point) angle_end_to_middle = self.distance_calc.bearing( self.middle_point, point) angle = degrees(angle_end_to_middle - angle_start_to_middle) if angle < -180: angle = 360 + angle elif angle > 180: angle = angle - 360 self.msglog.logMessage("") self.msglog.logMessage( "Current angle: {:.3F} º".format(abs(angle)), "Measure angle:", 0) self.rubber_band_curve.reset() # get the distance from center to point dist_mid_to_p = sqrt((point.x() - self.middle_point.x()) * (point.x() - self.middle_point.x()) + (point.y() - self.middle_point.y()) * (point.y() - self.middle_point.y())) dist_mid_to_start = sqrt( (self.start_point.x() - self.middle_point.x()) * (self.start_point.x() - self.middle_point.x()) + (self.start_point.y() - self.middle_point.y()) * (self.start_point.y() - self.middle_point.y())) # get angle angle_start = atan2(self.start_point.y() - self.middle_point.y(), self.start_point.x() - self.middle_point.x()) angle_p = atan2(point.y() - self.middle_point.y(), point.x() - self.middle_point.x()) # smaller distance if dist_mid_to_p < dist_mid_to_start: dist = dist_mid_to_p else: dist = dist_mid_to_start y_p = dist * sin(angle_p) x_p = dist * cos(angle_p) y_start = dist * sin(angle_start) x_start = dist * cos(angle_start) circular_ring = QgsCircularString() circular_ring = circular_ring.fromTwoPointsAndCenter( QgsPoint(self.middle_point.x() + x_start / 2, self.middle_point.y() + y_start / 2), QgsPoint(self.middle_point.x() + x_p / 2, self.middle_point.y() + y_p / 2), QgsPoint(self.middle_point.x(), self.middle_point.y()), True) circular_geometry = QgsGeometry(circular_ring) self.rubber_band_curve.addGeometry( circular_geometry, QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem.EpsgCrsId)) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
class DsgLineTool(QgsMapTool): lineCreated = pyqtSignal(QgsGeometry) def __init__(self, canvas): """ Constructor """ super(DsgLineTool, self).__init__(canvas) self.canvas = canvas self.rubberBand = None self.reset() def deactivate(self): """ Deativates this tool """ self.canvas.scene().removeItem(self.rubberBand) super(DsgLineTool, self).deactivate() def defineRubberBand(self): """ Defines the rubber band style """ settings = QSettings() myRed = int(settings.value("/qgis/default_measure_color_red", 222)) myGreen = int(settings.value("/qgis/default_measure_color_green", 155)) myBlue = int(settings.value("/qgis/default_measure_color_blue", 67)) self.rubberBand = QgsRubberBand(self.canvas) self.rubberBand.setColor(QColor(myRed, myGreen, myBlue, 100)) self.rubberBand.setWidth(3) def reset(self): """ Resets the tool """ if self.rubberBand: self.rubberBand.reset(QgsWkbTypes.LineGeometry) self.isEmittingPoint = False self.defineRubberBand() def canvasPressEvent(self, e): """ Reimplementation to add a point to the rubber band or reset it """ if self.isEmittingPoint: point = self.snapPoint(e.pos()) self.rubberBand.addPoint(point, True) else: self.reset() self.isEmittingPoint = True def canvasReleaseEvent(self, e): """ Reimplementation to add a vertex to the rubber band or to finish the rubber band according to the button used """ point = self.snapPoint(e.pos()) if e.button() == Qt.RightButton: geom = self.rubberBand.asGeometry() self.reset() self.lineCreated.emit(geom) elif e.button() == Qt.LeftButton: self.isEmittingPoint = True self.rubberBand.addPoint(point, True) def canvasMoveEvent(self, e): """ Reimplementation to move the rubber band """ if not self.isEmittingPoint: return point = self.snapPoint(e.pos()) self.rubberBand.movePoint(point) def snapPoint(self, p): """ Reimplementation to make use of the snap """ m = self.canvas.snappingUtils().snapToMap(p) if m.isValid(): return m.point() else: return self.canvas.getCoordinateTransform().toMapCoordinates(p)
class MeasureAreaTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.middle_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) crs = self.canvas.mapSettings().destinationCrs() self.area_calc = QgsDistanceArea() self.area_calc.setSourceCrs(crs, QgsProject.instance().transformContext()) self.area_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): """ Reset log message and rubber band""" self.msglog.logMessage("") self.start_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point and event.button() == Qt.RightButton: multipoint = self.rubber_band.asGeometry() area = self.area_calc.measureArea(multipoint) anglemsg = QMessageBox(self.parent()) anglemsg.finished.connect(self.deactivate) anglemsg.setWindowTitle("Measure area tool") anglemsg.setText("Area: {} ".format( self.area_calc.formatArea(area, 3, QgsUnitTypes.AreaSquareMeters, True))) anglemsg.exec() self.finish() elif self.start_point: self.rubber_band.addPoint(point) self.rubber_band_points.addPoint(point) else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) multipoint = self.rubber_band.asGeometry() area = self.area_calc.measureArea(multipoint) self.msglog.logMessage("") self.msglog.logMessage( "Current area: {} ".format( self.area_calc.formatArea(area, 3, QgsUnitTypes.AreaSquareMeters, True)), "Measure Area:", 0) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
class Qgis2threejsDialog(QDialog): STYLE_MAX_COUNT = 3 def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.apiChanged22 = False # not QgsApplication.prefixPath().startswith("C:/OSGeo4W") # QGis.QGIS_VERSION_INT >= 20200 # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) # DEM tab ui.toolButton_switchFocusMode.setVisible(False) ui.toolButton_PointTool.setVisible(False) ui.progressBar.setVisible(False) self.switchFocusMode(True) ui.toolButton_Browse.clicked.connect(self.browseClicked) ui.radioButton_Simple.toggled.connect(self.samplingModeChanged) ui.horizontalSlider_Resolution.valueChanged.connect(self.calculateResolution) ui.spinBox_Height.valueChanged.connect(self.updateQuads) ui.toolButton_switchFocusMode.clicked.connect(self.switchFocusModeClicked) ui.toolButton_PointTool.clicked.connect(self.startPointSelection) # Vector tab ui.treeWidget_VectorLayers.setHeaderLabel("Vector layers") self.initVectorStyleWidgets() ui.treeWidget_VectorLayers.currentItemChanged.connect(self.currentVectorLayerChanged) ui.treeWidget_VectorLayers.itemChanged.connect(self.vectorLayerItemChanged) ui.comboBox_ObjectType.currentIndexChanged.connect(self.objectTypeSelectionChanged) self.bar = None self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.currentVectorLayer = None self.vectorPropertiesDict = {} self.objectTypeManager = ObjectTypeManager() # set map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) self.connect(self.mapTool, SIGNAL("rectangleCreated()"), self.rectangleSelected) # self.mapTool = PointMapTool(iface.mapCanvas()) # QObject.connect(self.mapTool, SIGNAL("pointSelected()"), self.pointSelected) iface.mapCanvas().mapToolSet.connect(self.mapToolSet) self.startPointSelection() def exec_(self): ui = self.ui messages = [] # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings() if self.apiChanged22 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees", "Terrain may not appear well.") # show message if there are no dem layer no_demlayer = ui.comboBox_DEMLayer.count() == 0 ui.pushButton_Run.setEnabled(not no_demlayer) if no_demlayer: self.showMessageBar("No DEM layer", "Load 1-band raster layer with GDAL provider.", QgsMessageBar.WARNING) return QDialog.exec_(self) def showMessageBar(self, title, text, level=QgsMessageBar.INFO): if self.bar is None: self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) ui = self.ui margins = ui.gridLayout.getContentsMargins() vl = ui.gridLayout.takeAt(0) ui.gridLayout.setContentsMargins(0,0,0,0) ui.gridLayout.addWidget(self.bar, 0, 0) ui.gridLayout.addItem(vl, 1, 0) ui.verticalLayout.setContentsMargins(margins[0], margins[1] / 2, margins[2], margins[3]) self.bar.pushMessage(title, text, level=level) def initDEMLayerList(self, layerId=None): # list 1 band raster layers self.ui.comboBox_DEMLayer.clear() for id, layer in QgsMapLayerRegistry().instance().mapLayers().items(): if layer.type() == QgsMapLayer.RasterLayer and layer.providerType() == "gdal" and layer.bandCount() == 1: self.ui.comboBox_DEMLayer.addItem(layer.name(), id) # select the last selected layer if layerId is not None: index = self.ui.comboBox_DEMLayer.findData(layerId) if index != -1: self.ui.comboBox_DEMLayer.setCurrentIndex(index) return index return -1 def initVectorLayerTree(self, vectorPropertiesDict): self.vectorPropertiesDict = vectorPropertiesDict tree = self.ui.treeWidget_VectorLayers tree.clear() # add vector layers into tree widget self.treeTopItems = topItems = {QGis.Point:QTreeWidgetItem(tree, ["Point"]), QGis.Line:QTreeWidgetItem(tree, ["Line"]), QGis.Polygon:QTreeWidgetItem(tree, ["Polygon"])} self.vlItems = {} for layer in self.iface.legendInterface().layers(): if layer.type() != QgsMapLayer.VectorLayer: continue geometry_type = layer.geometryType() if geometry_type in [QGis.Point, QGis.Line, QGis.Polygon]: self.vlItems[layer.id()] = item = QTreeWidgetItem(topItems[geometry_type], [layer.name()]) if layer.id() in self.vectorPropertiesDict: isVisible = self.vectorPropertiesDict[layer.id()]["visible"] else: isVisible = False #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) #item.setDisabled(True) #item.setData(0, Qt.CheckStateRole, Qt.Unchecked) for item in topItems.values(): tree.expandItem(item) self.setVectorStylesEnabled(False) def initVectorStyleWidgets(self): self.colorWidget = StyleWidget(StyleWidget.COLOR) self.ui.verticalLayout_Styles.addWidget(self.colorWidget) self.heightWidget = StyleWidget(StyleWidget.HEIGHT) self.ui.verticalLayout_zCoordinate.addWidget(self.heightWidget) self.styleWidgets = [] for i in range(self.STYLE_MAX_COUNT): widget = StyleWidget() widget.setVisible(False) self.ui.verticalLayout_Styles.addWidget(widget) self.styleWidgets.append(widget) def currentVectorLayerChanged(self, currentItem, previousItem): # save properties of previous item if previousItem is not None: layerid = previousItem.data(0, Qt.UserRole) if layerid is not None: self.saveVectorProperties(layerid) layerid = currentItem.data(0, Qt.UserRole) if layerid is None: self.currentVectorLayer = None return self.currentVectorLayer = layer = QgsMapLayerRegistry().instance().mapLayer(layerid) if layer is None: return for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].hide() obj_types = self.objectTypeManager.objectTypeNames(layer.geometryType()) ui = self.ui ui.comboBox_ObjectType.blockSignals(True) ui.comboBox_ObjectType.clear() ui.comboBox_ObjectType.addItems(obj_types) ui.comboBox_ObjectType.blockSignals(False) # set up property widgets self.objectTypeSelectionChanged() if layerid in self.vectorPropertiesDict: # restore properties self.restoreVectorProperties(layerid) self.setVectorStylesEnabled(currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def vectorLayerItemChanged(self, item, column): # update style form enablement currentItem = self.ui.treeWidget_VectorLayers.currentItem() if currentItem: self.setVectorStylesEnabled(currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def objectTypeSelectionChanged(self, idx=None): layer = self.currentVectorLayer try: ve = float(ui.lineEdit_zFactor.text()) except: ve = 1 mapTo3d = MapTo3D(self.iface.mapCanvas(), verticalExaggeration=ve) self.objectTypeManager.setupForm(self, mapTo3d, layer, layer.geometryType(), self.ui.comboBox_ObjectType.currentIndex()) def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def setVectorStylesEnabled(self, enabled): self.ui.comboBox_ObjectType.setEnabled(enabled) self.ui.label_ObjectType.setEnabled(enabled) self.colorWidget.setEnabled(enabled) self.heightWidget.setEnabled(enabled) for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].setEnabled(enabled) def saveVectorProperties(self, layerid): properties = {} layer = QgsMapLayerRegistry().instance().mapLayer(layerid) itemIndex = self.ui.comboBox_ObjectType.currentIndex() properties["itemindex"] = itemIndex properties["typeitem"] = self.objectTypeManager.objectTypeItem(layer.geometryType(), itemIndex) properties["visible"] = self.vlItems[self.currentVectorLayer.id()].data(0, Qt.CheckStateRole) == Qt.Checked properties["color"] = self.colorWidget.values() properties["height"] = self.heightWidget.values() for i in range(self.STYLE_MAX_COUNT): if self.styleWidgets[i].isVisible(): properties[i] = self.styleWidgets[i].values() self.vectorPropertiesDict[layerid] = properties def restoreVectorProperties(self, layerid): properties = self.vectorPropertiesDict[layerid] self.ui.comboBox_ObjectType.setCurrentIndex(properties["itemindex"]) self.colorWidget.setValues(properties["color"]) self.heightWidget.setValues(properties["height"]) for i in range(self.STYLE_MAX_COUNT): if i in properties: self.styleWidgets[i].setValues(properties[i]) def calculateResolution(self, v=None): extent = self.iface.mapCanvas().extent() renderer = self.iface.mapCanvas().mapRenderer() size = 100 * self.ui.horizontalSlider_Resolution.value() self.ui.label_Resolution.setText("about {0} x {0} px".format(size)) # calculate resolution and size width, height = renderer.width(), renderer.height() s = (size * size / float(width * height)) ** 0.5 if s < 1: width = int(width * s) height = int(height * s) xres = extent.width() / width yres = extent.height() / height self.ui.lineEdit_HRes.setText(str(xres)) self.ui.lineEdit_VRes.setText(str(yres)) self.ui.lineEdit_Width.setText(str(width + 1)) self.ui.lineEdit_Height.setText(str(height + 1)) def progress(self, percentage): self.ui.progressBar.setValue(percentage) self.ui.progressBar.setVisible(percentage != 100) def run(self): ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename != "" and QFileInfo(filename).exists() and QMessageBox.question(None, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return self.endPointSelection() item = ui.treeWidget_VectorLayers.currentItem() if item: self.saveVectorProperties(item.data(0, Qt.UserRole)) ui.pushButton_Run.setEnabled(False) self.progress(0) canvas = self.iface.mapCanvas() htmlfilename = ui.lineEdit_OutputFilename.text() demlayerid = ui.comboBox_DEMLayer.itemData(ui.comboBox_DEMLayer.currentIndex()) mapTo3d = MapTo3D(canvas, verticalExaggeration=float(ui.lineEdit_zFactor.text())) if self.ui.radioButton_Simple.isChecked(): dem_width = int(ui.lineEdit_Width.text()) dem_height = int(ui.lineEdit_Height.text()) context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode, dem_width, dem_height) htmlfilename = runSimple(htmlfilename, context, self.progress) else: context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode) htmlfilename = runAdvanced(htmlfilename, context, self, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if htmlfilename is None: return self.clearRubberBands() if not tools.openHTMLFile(htmlfilename): return QDialog.accept(self) def reject(self): self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def startPointSelection(self): canvas = self.iface.mapCanvas() self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.ui.toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() self.iface.mapCanvas().setMapTool(self.previousMapTool) def rectangleSelected(self): ui = self.ui ui.radioButton_Advanced.setChecked(True) rect = self.mapTool.rectangle() toRect = rect.width() and rect.height() self.switchFocusMode(toRect) ui.lineEdit_xmin.setText(str(rect.xMinimum())) ui.lineEdit_ymin.setText(str(rect.yMinimum())) ui.lineEdit_xmax.setText(str(rect.xMaximum())) ui.lineEdit_ymax.setText(str(rect.yMaximum())) quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(rect, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), rect.center()) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def pointSelected(self): # set values of controls self.ui.lineEdit_CenterX.setText(str(self.mapTool.point.x())) self.ui.lineEdit_CenterY.setText(str(self.mapTool.point.y())) self.ui.radioButton_Advanced.setChecked(True) quadtree = QuadTree(self.iface.mapCanvas().extent(), self.mapTool.point, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), self.mapTool.point) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def mapToolSet(self, mapTool): if mapTool != self.mapTool: self.ui.toolButton_PointTool.setVisible(True) def createQuadTree(self): ui = self.ui try: c = map(float, [ui.lineEdit_xmin.text(), ui.lineEdit_ymin.text(), ui.lineEdit_xmax.text(), ui.lineEdit_ymax.text()]) except: return None quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(QgsRectangle(c[0], c[1], c[2], c[3]), ui.spinBox_Height.value()) return quadtree def createRubberBands(self, quads, point=None): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) for quad in quads: points = [] extent = quad.extent points.append(QgsPoint(extent.xMinimum(), extent.yMinimum())) points.append(QgsPoint(extent.xMinimum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMinimum())) self.rb_quads.addGeometry(QgsGeometry.fromPolygon([points]), None) self.log(extent.toString()) self.log("Quad count: %d" % len(quads)) # create a point with rubber band if point: self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(point) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = self.ui.lineEdit_OutputFilename.text() if directory == "": directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if filename != "": self.ui.lineEdit_OutputFilename.setText(filename) def samplingModeChanged(self): ui = self.ui isSimpleMode = ui.radioButton_Simple.isChecked() simple_widgets = [ui.horizontalSlider_Resolution, ui.lineEdit_Width, ui.lineEdit_Height, ui.lineEdit_HRes, ui.lineEdit_VRes] for w in simple_widgets: w.setEnabled(isSimpleMode) isAdvancedMode = not isSimpleMode advanced_widgets = [ui.spinBox_Height, ui.lineEdit_xmin, ui.lineEdit_ymin, ui.lineEdit_xmax, ui.lineEdit_ymax, ui.toolButton_switchFocusMode] for w in advanced_widgets: w.setEnabled(isAdvancedMode) def updateQuads(self, v=None): quadtree = self.createQuadTree() if quadtree: self.createRubberBands(quadtree.quads(), quadtree.focusRect.center()) else: self.clearRubberBands() def switchFocusModeClicked(self): self.switchFocusMode(not self.ui.label_xmin.isVisible()) def switchFocusMode(self, toRect): ui = self.ui toPoint = not toRect ui.label_xmin.setVisible(toRect) ui.label_ymin.setVisible(toRect) ui.lineEdit_xmin.setVisible(toRect) ui.lineEdit_ymin.setVisible(toRect) suffix = "max" if toRect else "" ui.label_xmax.setText("x" + suffix) ui.label_ymax.setText("y" + suffix) mode = "point" if toRect else "rectangle" ui.toolButton_switchFocusMode.setText("To " + mode + " selection") selection = "area" if toRect else "point" action = "Stroke a rectangle" if toRect else "Click" ui.label_Focus.setText("Focus {0} ({1} on map canvas to set values)".format(selection, action)) def log(self, msg): if debug_mode: qDebug(msg)
def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): ''' draw the result ''' resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_path_id = -1 i = 0 count = len(rows) ids = args['ids'].split(',') args['last_id'] = ids[len(ids) - 1] pcts = args['pcts'].split(',') pct_idx = 0 for row in rows: cur2 = con.cursor() args['result_path_id'] = row[1] args['result_node_id'] = row[2] args['result_edge_id'] = row[3] args['result_cost'] = row[4] if args['result_path_id'] != cur_path_id: cur_path_id = args['result_path_id'] if rubberBand: resultPathsRubberBands.append(rubberBand) rubberBand = None rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) rubberBand.setColor(QColor(255, 0, 0, 128)) rubberBand.setWidth(4) query2 = "" if i < (count - 1): args['result_next_path_id'] = rows[i + 1][1] args['result_next_node_id'] = rows[i + 1][2] if args['result_next_path_id'] != args['result_path_id']: pct_idx += 1 elif i == (count - 1): pct_idx = len(pcts) - 1 args['current_pct'] = pcts[pct_idx] if i == 0 and args['result_node_id'] == -1: query2 = """ SELECT ST_AsText(%(transform_s)sST_Line_Substring(%(geometry)s, %(current_pct)s, 1.0)%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_next_node_id)s AND %(id)s = %(result_edge_id)s UNION SELECT ST_AsText(%(transform_s)sST_Line_Substring(ST_Reverse(%(geometry)s), 1.0 - %(current_pct)s, 1.0)%(transform_e)s) FROM %(edge_table)s WHERE %(source)s = %(result_next_node_id)s AND %(id)s = %(result_edge_id)s; """ % args elif i < (count - 1) and ( args['result_path_id'] != args['result_next_path_id'] ) and (args['result_node_id'] == args['result_next_node_id']): # round trip case query2 = """ SELECT ST_AsText(ST_LineMerge(ST_Collect(ARRAY[ ( SELECT ST_AsText(%(transform_s)sST_Line_Substring(%(geometry)s, 0.0, %(current_pct)s)%(transform_e)s) FROM %(edge_table)s WHERE %(source)s = %(result_node_id)s AND %(id)s = %(result_edge_id)s UNION SELECT ST_AsText(%(transform_s)sST_Line_Substring(ST_Reverse(%(geometry)s), 0.0, 1.0 - %(current_pct)s)%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_node_id)s AND %(id)s = %(result_edge_id)s ), ( SELECT ST_AsText(%(transform_s)sST_Reverse(ST_Line_Substring(%(geometry)s, 0.0, %(current_pct)s))%(transform_e)s) FROM %(edge_table)s WHERE %(source)s = %(result_node_id)s AND %(id)s = %(result_edge_id)s UNION SELECT ST_AsText(%(transform_s)sST_Reverse(ST_Line_Substring(ST_Reverse(%(geometry)s), 0.0, 1.0 - %(current_pct)s))%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_node_id)s AND %(id)s = %(result_edge_id)s )]))); """ % args elif i == (count - 1) and ( (args['result_edge_id'] == -1) or (str(args['result_edge_id']) == args['last_id'])): if args['result_edge_id'] != -1: query2 = """ SELECT ST_AsText(%(transform_s)sST_Line_Substring(%(geometry)s, 0.0, %(current_pct)s)%(transform_e)s) FROM %(edge_table)s WHERE %(source)s = %(result_node_id)s AND %(id)s = %(result_edge_id)s UNION SELECT ST_AsText(%(transform_s)sST_Line_Substring(ST_Reverse(%(geometry)s), 0.0, 1.0 - %(current_pct)s)%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_node_id)s AND %(id)s = %(result_edge_id)s; """ % args else: break else: query2 = """ SELECT ST_AsText(%(transform_s)s%(geometry)s%(transform_e)s) FROM %(edge_table)s WHERE %(source)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d UNION SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; """ % args ##Utils.logMessage(query2) cur2.execute(query2) row2 = cur2.fetchone() ##Utils.logMessage(str(row2[0])) assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) i = i + 1 if rubberBand: resultPathsRubberBands.append(rubberBand) rubberBand = None
class DsgLineTool(QgsMapTool): lineCreated = pyqtSignal(QgsGeometry) def __init__(self, canvas): """ Constructor """ super(DsgLineTool, self).__init__(canvas) self.canvas = canvas self.rubberBand = None self.reset() def deactivate(self): """ Deativates this tool """ self.canvas.scene().removeItem(self.rubberBand) super(DsgLineTool, self).deactivate() def defineRubberBand(self): """ Defines the rubber band style """ settings = QSettings() myRed = int(settings.value("/qgis/default_measure_color_red", 222)) myGreen = int(settings.value("/qgis/default_measure_color_green", 155)) myBlue = int(settings.value("/qgis/default_measure_color_blue", 67)) self.rubberBand = QgsRubberBand(self.canvas) self.rubberBand.setColor(QColor(myRed, myGreen, myBlue, 100)) self.rubberBand.setWidth(3) def reset(self): """ Resets the tool """ if self.rubberBand: self.rubberBand.reset(QGis.Line) self.isEmittingPoint = False self.defineRubberBand() def canvasPressEvent(self, e): """ Reimplementation to add a point to the rubber band or reset it """ if self.isEmittingPoint: point = self.snapPoint(e.pos()) self.rubberBand.addPoint(point, True) else: self.reset() self.isEmittingPoint = True def canvasReleaseEvent(self, e): """ Reimplementation to add a vertex to the rubber band or to finish the rubber band according to the button used """ point = self.snapPoint(e.pos()) if e.button() == Qt.RightButton: geom = self.rubberBand.asGeometry() self.reset() self.lineCreated.emit(geom) elif e.button() == Qt.LeftButton: self.isEmittingPoint = True self.rubberBand.addPoint(point, True) def canvasMoveEvent(self, e): """ Reimplementation to move the rubber band """ if not self.isEmittingPoint: return point = self.snapPoint(e.pos()) self.rubberBand.movePoint(point) def snapPoint(self, p): """ Reimplementation to make use of the snap """ m = self.canvas.snappingUtils().snapToMap(p) if m.isValid(): return m.point() else: return self.canvas.getCoordinateTransform().toMapCoordinates(p)
class QgepMapToolDigitizeDrainageChannel(QgsMapTool): ''' This is used to digitize a drainage channel. It lets you digitize two points and then creates a polygon based on these two points by adding an orthogonal offset at each side. Input: x==============x Output: ---------------- | | ---------------- Usage: Connect to the signals deactivated() and geometryDigitized() If geometryDigitized() is called you can use the member variable geometry which will contain a rectangle polygon deactivated() will be emited after a right click ''' geometryDigitized = pyqtSignal() def __init__(self, iface, layer): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.layer = layer self.rubberband = QgsRubberBand(iface.mapCanvas(), QGis.Line) self.rubberband.setColor(QColor("#ee5555")) self.rubberband.setWidth(2) self.firstPoint = None self.messageBarItem = None self.geometry = None def activate(self): """ Map tool is activated """ QgsMapTool.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) msgtitle = self.tr('Digitizing Drainage Channel') msg = self.tr('Digitize start and end point. Rightclick to abort.') self.messageBarItem = QgsMessageBar.createMessage(msgtitle, msg) self.iface.messageBar().pushItem(self.messageBarItem) def deactivate(self): """ Map tool is deactivated """ QgsMapTool.deactivate(self) self.iface.messageBar().popWidget(self.messageBarItem) try: self.iface.mapCanvas().scene().removeItem(self.rubberband) del self.rubberband except AttributeError: # Called repeatedly... bail out pass self.canvas.unsetCursor() def canvasMoveEvent(self, event): """ Mouse is moved: Update rubberband :param event: coordinates etc. """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.movePoint(mousepos) def canvasReleaseEvent(self, event): """ Canvas is released. This means: * start digitizing * stop digitizing (create a rectangle * if the Ctrl-modifier is pressed, ask for the rectangle width :param event: coordinates etc. """ if event.button() == Qt.RightButton: self.deactivate() else: mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) if self.firstPoint: # If the first point was set before, we are doing the second one lp1 = self.rubberband.asGeometry().asPolyline()[0] lp2 = self.rubberband.asGeometry().asPolyline()[1] width = 0.2 if QApplication.keyboardModifiers() & Qt.ControlModifier: dlg = QDialog() dlg.setLayout(QGridLayout()) dlg.layout().addWidget(QLabel(self.tr('Enter width'))) txt = QLineEdit('0.2') dlg.layout().addWidget(txt) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(bb) bb.accepted.connect(dlg.accept) bb.rejected.connect(dlg.reject) if dlg.exec_(): try: width = float(txt.text()) except ValueError: width = 0.2 length = math.sqrt(math.pow(lp1.x() - lp2.x(), 2) + math.pow(lp1.y() - lp2.y(), 2)) xd = lp2.x() - lp1.x() yd = lp2.y() - lp1.y() pt1 = QgsPoint(lp1.x() + width * (yd / length), lp1.y() - width * (xd / length)) pt2 = QgsPoint(lp1.x() - width * (yd / length), lp1.y() + width * (xd / length)) pt3 = QgsPoint(lp2.x() - width * (yd / length), lp2.y() + width * (xd / length)) pt4 = QgsPoint(lp2.x() + width * (yd / length), lp2.y() - width * (xd / length)) self.geometry = QgsGeometry.fromPolygon([[pt1, pt2, pt3, pt4, pt1]]) self.geometryDigitized.emit() self.firstPoint = mousepos
class MeasureDistanceTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs( crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): self.msglog.logMessage("") self.start_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point: self.end_point = point self.rubber_band.addPoint(self.end_point) self.rubber_band_points.addPoint(self.end_point) distance = self.distance_calc.measureLine( [self.start_point, self.end_point]) bearing = self.distance_calc.bearing(self.start_point, point) distancemsg = QMessageBox(self.parent()) distancemsg.finished.connect(self.deactivate) distancemsg.setWindowTitle("Measure tool") distancemsg.setText("Distance: {:.3F} m. Bearing: {:.3F} º".format( distance, degrees(bearing))) distancemsg.exec() self.finish() else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) distance = self.distance_calc.measureLine( [self.start_point, point]) bearing = self.distance_calc.bearing(self.start_point, point) self.msglog.logMessage("") self.msglog.logMessage( "Current distance: {:.3F} m. Bearing: {:.3F} º".format( distance, degrees(bearing)), "Measure distance:", 0) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
class PolygonMapTool(QgsMapToolEmitPoint): def __init__(self, MapExplorer): self.mainWindow = MapExplorer self.canvas = MapExplorer.mapCanvas self.rasterlayer = MapExplorer.layer QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, True) self.rubberBand.setColor(QColor(255, 0, 0)) self.rubberBand.setWidth(1) self.tmpRubberBand = QgsRubberBand(self.canvas, True) self.tmpRubberBand.setColor(QColor(255, 0, 0)) self.tmpRubberBand.setWidth(1) self.categories = [] self.reset() self.methodsDict = { "最小距离": self.MinDistance, "最大似然": self.MaxLikelihood, "马氏距离": self.Mahalanobis, "支持向量机": self.SVM, "神经网络": self.ANN, "随机森林": self.RandomForest, "AdaBoost": self.AdaBoost } def reset(self): self.isEmittingPoint = False self.chooseComplete = False self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) self.tmpRubberBand.reset(True) def canvasReleaseEvent(self, e): current_point = self.toMapCoordinates(e.pos()) if e.button() == Qt.LeftButton: self.isEmittingPoint = True self.tmpRubberBand.addPoint(current_point, True) elif e.button() == Qt.RightButton: if self.tmpRubberBand.numberOfVertices() > 2: lineString = self.tmpRubberBand.asGeometry( ) # type:QgsGeometry self.tmpRubberBand.reset(True) self.rubberBand.addGeometry( QgsGeometry.convertToType(lineString, QgsWkbTypes.PolygonGeometry)) self.rubberBand.setColor(QColor(255, 0, 0, 50)) self.rubberBand.show() self.isEmittingPoint = False self.chooseComplete = True def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.tmpRubberBand.removeLastPoint(doUpdate=False) point = self.toMapCoordinates(e.pos()) self.tmpRubberBand.addPoint(point, True) def keyReleaseEvent(self, e): if self.isEmittingPoint: if e.key() == Qt.Key_Backspace: self.tmpRubberBand.removeLastPoint(doUpdate=True) if self.chooseComplete: if e.key() + 1 == Qt.Key_Enter: # 为啥会差一呢 self.saveMask() self.reset() reply = QMessageBox.question(None, '提示', '继续加入类别?', QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.No: for category in self.categories: maskFile = f'mask/{category["name"]}_clip_mask.shp' sampleFile = f'samples/{category["name"]}.tif' if os.path.exists(sampleFile): os.remove(sampleFile) cmd = f'gdalwarp -of GTiff -cutline {maskFile} -cl {category["name"]}_clip_mask -crop_to_cutline -dstnodata 0 {self.rasterlayer.dataProvider().dataSourceUri()} {sampleFile}' os.system(cmd) dlg = Select_Dialog() dlg.exec_() self.methodsDict[dlg.chosen]() def saveMask(self): dlg = QInputDialog() dlg.setModal(True) text, okPressed = dlg.getText(None, "类别标记", "输入该样本类别名:", QLineEdit.Normal, "") while text == '': QMessageBox.warning(None, '输入警告', '未输入任何内容!') text, okPressed = dlg.getText(None, "类别标记", "输入该样本类别名:", QLineEdit.Normal, "") if okPressed: self.chooseComplete = False color = QColorDialog.getColor() maskLayer = QgsVectorLayer("Polygon", "clip_mask", "memory") maskLayer.setCrs(self.rasterlayer.crs()) pr = maskLayer.dataProvider() # type:QgsVectorDataProvider pr.addAttributes([QgsField("类别名", QVariant.String)]) maskLayer.updateFields() fet = QgsFeature() print(self.rubberBand.asGeometry()) fet.setGeometry(self.rubberBand.asGeometry()) fet.setAttributes([text]) pr.addFeatures([fet]) maskLayer.updateExtents() maskFile = f'mask/{text}_clip_mask.shp' if os.path.exists(maskFile): os.remove(maskFile) # 文件存在的话就替换一下 QgsVectorFileWriter.writeAsVectorFormat( layer=maskLayer, fileName=maskFile, fileEncoding='utf-8', destCRS=self.rasterlayer.crs(), driverName="ESRI Shapefile") self.categories.append({"name": text, "color": color}) def MinDistance(self): count_categories = len(self.categories) # 类别数 raster_source = self.rasterlayer.dataProvider().dataSourceUri( ) # 源图像地址 raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset bandCount = raster_dataset.RasterCount raster_arr = raster_dataset.ReadAsArray() all_md = np.zeros(shape=(count_categories, raster_arr.shape[1], raster_arr.shape[2]), dtype=np.float64) for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray # shape of pixels:(bandCount,number of sample pixels) mean = np.mean(pixels, axis=1) # shape of mean:(bandCount,) raster_arr_minus_mean = raster_arr - mean.reshape(bandCount, 1, 1) md = (raster_arr_minus_mean**2).sum(axis=0) all_md[i, :, :] = md min_md = np.argmin(all_md, axis=0) classification_result = np.zeros(shape=(min_md.shape[0], min_md.shape[1], 3), dtype=np.int) for i in range(min_md.shape[0]): for j in range(min_md.shape[1]): classification_result[i, j, :] = self.categories[min_md[ i, j]]["color"].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def MaxLikelihood(self): count_categories = len(self.categories) # 类别数 raster_source = self.rasterlayer.dataProvider().dataSourceUri( ) # 源图像地址 raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset bandCount = raster_dataset.RasterCount raster_arr = raster_dataset.ReadAsArray() all_md = np.zeros(shape=(count_categories, raster_arr.shape[1], raster_arr.shape[2]), dtype=np.float64) for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray # shape of pixels:(bandCount,number of sample pixels) cov = np.cov(m=pixels, rowvar=True, bias=False) # shape of cov:(bandCount,bandCount) mean = np.mean(pixels, axis=1) # shape of mean:(bandCount,) raster_arr_minus_mean = raster_arr - mean.reshape(bandCount, 1, 1) tmp = np.tensordot(np.linalg.inv(cov), raster_arr_minus_mean, axes=[[1], [0]]) md = (tmp * raster_arr_minus_mean).sum(axis=0) * (-0.5) all_md[i, :, :] = md max_md = np.argmax(all_md, axis=0) classification_result = np.zeros(shape=(max_md.shape[0], max_md.shape[1], 3), dtype=np.int) for i in range(max_md.shape[0]): for j in range(max_md.shape[1]): classification_result[i, j, :] = self.categories[max_md[ i, j]]["color"].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def Mahalanobis(self): count_categories = len(self.categories) # 类别数 raster_source = self.rasterlayer.dataProvider().dataSourceUri( ) # 源图像地址 raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset bandCount = raster_dataset.RasterCount raster_arr = raster_dataset.ReadAsArray() all_md = np.zeros(shape=(count_categories, raster_arr.shape[1], raster_arr.shape[2]), dtype=np.float64) for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray # shape of pixels:(bandCount,number of sample pixels) cov = np.cov(m=pixels, rowvar=True, bias=False) # shape of cov:(bandCount,bandCount) mean = np.mean(pixels, axis=1) # shape of mean:(bandCount,) raster_arr_minus_mean = raster_arr - mean.reshape(bandCount, 1, 1) tmp = np.tensordot(np.linalg.inv(cov), raster_arr_minus_mean, axes=[[1], [0]]) md = (tmp * raster_arr_minus_mean).sum(axis=0) all_md[i, :, :] = md min_md = np.argmin(all_md, axis=0) classification_result = np.zeros(shape=(min_md.shape[0], min_md.shape[1], 3), dtype=np.int) for i in range(min_md.shape[0]): for j in range(min_md.shape[1]): classification_result[i, j, :] = self.categories[min_md[ i, j]]["color"].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def SVM(self): count_categories = len(self.categories) X = [] Y = [] for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray X.append(pixels) Y.append([i] * pixels.shape[1]) X = np.concatenate(X, axis=1) Y = np.concatenate(Y) # clf = svm.SVC(decision_function_shape='ovo',C=1,kernel='rbf',degree=3,gamma='auto') clf = svm.SVC(decision_function_shape='ovo') time_start = time.time() clf.fit(X.transpose(), Y) time_end = time.time() print("totally cost", time_end - time_start) raster_source = self.rasterlayer.dataProvider().dataSourceUri() raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset raster_arr = raster_dataset.ReadAsArray() row = raster_arr.shape[1] col = raster_arr.shape[2] classification_result = np.zeros(shape=(row, col, 3)) predicts = clf.predict( raster_arr.reshape((raster_arr.shape[0], -1)).transpose()) for i in range(row): for j in range(col): classification_result[i, j, :] = self.categories[predicts[ i * col + j]]['color'].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def ANN(self): count_categories = len(self.categories) raster_source = self.rasterlayer.dataProvider().dataSourceUri() raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset raster_arr = raster_dataset.ReadAsArray() row = raster_arr.shape[1] col = raster_arr.shape[2] mean = np.mean(raster_arr, axis=(1, 2)) sigma = np.std(raster_arr, axis=(1, 2)) raster_arr = (raster_arr - mean.reshape( mean.shape[0], 1, 1)) / sigma.reshape(sigma.shape[0], 1, 1) X = [] Y = [] for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray X.append(pixels) Y.append([i] * pixels.shape[1]) X = np.concatenate(X, axis=1) Y = np.concatenate(Y) X = (X - mean.reshape(mean.shape[0], 1)) / sigma.reshape( sigma.shape[0], 1) # 标准化 clf = MLPClassifier(solver='adam', alpha=1e-5, hidden_layer_sizes=(30, 30), shuffle=True, random_state=1) time_start = time.time() clf.fit(X.transpose(), Y) time_end = time.time() print("totally cost", time_end - time_start) classification_result = np.zeros(shape=(row, col, 3)) predicts = clf.predict( raster_arr.reshape((raster_arr.shape[0], -1)).transpose()) for i in range(row): for j in range(col): classification_result[i, j, :] = self.categories[predicts[ i * col + j]]['color'].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def RandomForest(self): count_categories = len(self.categories) X = [] Y = [] for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray X.append(pixels) Y.append([i] * pixels.shape[1]) X = np.concatenate(X, axis=1) Y = np.concatenate(Y) clf = RandomForestClassifier() time_start = time.time() clf.fit(X.transpose(), Y) time_end = time.time() print("totally cost", time_end - time_start) raster_source = self.rasterlayer.dataProvider().dataSourceUri() raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset raster_arr = raster_dataset.ReadAsArray() row = raster_arr.shape[1] col = raster_arr.shape[2] classification_result = np.zeros(shape=(row, col, 3)) predicts = clf.predict( raster_arr.reshape((raster_arr.shape[0], -1)).transpose()) for i in range(row): for j in range(col): classification_result[i, j, :] = self.categories[predicts[ i * col + j]]['color'].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def AdaBoost(self): count_categories = len(self.categories) X = [] Y = [] for i in range(count_categories): category = self.categories[i] path = f'samples/{category["name"]}.tif' dataset_arr = gdal.Open(path).ReadAsArray() mask = np.all(dataset_arr == 0, axis=0) pixels = dataset_arr[:, ~mask] # type:np.ndarray X.append(pixels) Y.append([i] * pixels.shape[1]) X = np.concatenate(X, axis=1) Y = np.concatenate(Y) clf = AdaBoostClassifier(n_estimators=50, random_state=0) time_start = time.time() clf.fit(X.transpose(), Y) time_end = time.time() print("totally cost", time_end - time_start) raster_source = self.rasterlayer.dataProvider().dataSourceUri() raster_dataset = gdal.Open(raster_source) # type:gdal.Dataset raster_arr = raster_dataset.ReadAsArray() row = raster_arr.shape[1] col = raster_arr.shape[2] classification_result = np.zeros(shape=(row, col, 3)) predicts = clf.predict( raster_arr.reshape((raster_arr.shape[0], -1)).transpose()) for i in range(row): for j in range(col): classification_result[i, j, :] = self.categories[predicts[ i * col + j]]['color'].getRgb()[:3] driver = gdal.GetDriverByName('GTiff') save_path, format = QFileDialog.getSaveFileName( None, '存储分类结果', '', '*.tif') out_ds = driver.Create(save_path, raster_dataset.RasterXSize, raster_dataset.RasterYSize, 3, gdal.GDT_Byte) out_ds.SetProjection(raster_dataset.GetProjection()) out_ds.SetGeoTransform(raster_dataset.GetGeoTransform()) for i in range(3): out_band = out_ds.GetRasterBand(i + 1) out_band.WriteArray(classification_result[:, :, i]) out_band.ComputeStatistics(False) out_ds.FlushCache() self.mainWindow.loadMap(save_path, self.categories) def deactivate(self): super(PolygonMapTool, self).deactivate() self.deactivated.emit() self.reset()
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 GeodesicMeasureDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent): super(GeodesicMeasureDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.canvas = iface.mapCanvas() settings = QSettings() self.restoreGeometry( settings.value("ShapeTools/MeasureDialogGeometry", QByteArray(), type=QByteArray)) self.closeButton.clicked.connect(self.closeDialog) self.newButton.clicked.connect(self.newDialog) self.saveToLayerButton.clicked.connect(self.saveToLayer) self.saveToLayerButton.setEnabled(False) self.unitsComboBox.addItems(UNITS) self.tableWidget.setColumnCount(3) self.tableWidget.setSortingEnabled(False) self.tableWidget.setHorizontalHeaderLabels( ['Heading To', 'Heading From', 'Distance']) self.unitsComboBox.activated.connect(self.unitsChanged) self.capturedPoints = [] self.distances = [] self.geod = Geodesic.WGS84 self.activeMeasuring = True self.unitsChanged() self.currentDistance = 0.0 color = QColor(222, 167, 67, 150) self.pointRb = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.pointRb.setColor(color) self.pointRb.setIconSize(10) self.lineRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.lineRb.setColor(color) self.lineRb.setWidth(3) self.tempRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRb.setColor(color) self.tempRb.setWidth(3) def ready(self): return self.activeMeasuring def stop(self): self.activeMeasuring = False def closeEvent(self, event): self.closeDialog() def closeDialog(self): self.clear() QSettings().setValue("ShapeTools/MeasureDialogGeometry", self.saveGeometry()) self.close() def newDialog(self): self.clear() def unitsChanged(self): label = "Distance [{}]".format( UNITS[self.unitsComboBox.currentIndex()]) item = QTableWidgetItem(label) self.tableWidget.setHorizontalHeaderItem(2, item) ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: item = QTableWidgetItem('{:.4f}'.format( self.unitDistance(self.distances[i]))) self.tableWidget.setItem(i, 2, item) i += 1 self.formatTotal() def motionReady(self): if len(self.capturedPoints) > 0 and self.activeMeasuring: return True return False def addPoint(self, pt, button): self.currentDistance = 0 index = len(self.capturedPoints) if index > 0 and pt == self.capturedPoints[index - 1]: # the clicked point is the same as the previous so just ignore it return self.capturedPoints.append(pt) # Add rubber band points canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) ptCanvas = transform.transform(pt.x(), pt.y()) self.pointRb.addPoint(ptCanvas, True) # If there is more than 1 point add it to the table if index > 0: self.saveToLayerButton.setEnabled(True) (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.capturedPoints[index]) self.distances.append(distance) self.insertParams(index, distance, startAngle, endAngle) # Add Rubber Band Line linePts = self.getLinePts(distance, self.capturedPoints[index - 1], self.capturedPoints[index]) self.lineRb.addGeometry(QgsGeometry.fromPolylineXY(linePts), None) self.formatTotal() def inMotion(self, pt): index = len(self.capturedPoints) if index <= 0: return (self.currentDistance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], pt) self.insertParams(index, self.currentDistance, startAngle, endAngle) self.formatTotal() linePts = self.getLinePts(self.currentDistance, self.capturedPoints[index - 1], pt) self.tempRb.setToGeometry(QgsGeometry.fromPolylineXY(linePts), None) def calcParameters(self, pt1, pt2): l = self.geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x()) az2 = (l['azi2'] + 180) % 360.0 if az2 > 180: az2 = az2 - 360.0 l2 = self.geod.Inverse(pt2.y(), pt2.x(), pt1.y(), pt1.x()) return (l['s12'], l['azi1'], az2) def getLinePts(self, distance, pt1, pt2): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) pt1c = transform.transform(pt1.x(), pt1.y()) pt2c = transform.transform(pt2.x(), pt2.y()) if distance < 10000: return [pt1c, pt2c] l = self.geod.InverseLine(pt1.y(), pt1.x(), pt2.y(), pt2.x()) n = int(math.ceil(distance / 10000.0)) if n > 20: n = 20 seglen = distance / n pts = [pt1c] for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) ptc = transform.transform(g['lon2'], g['lat2']) pts.append(ptc) pts.append(pt2c) return pts def saveToLayer(self): units = self.unitDesignator() canvasCrs = self.canvas.mapSettings().destinationCrs() fields = QgsFields() fields.append(QgsField("label", QVariant.String)) fields.append(QgsField("value", QVariant.Double)) fields.append(QgsField("units", QVariant.String)) fields.append(QgsField("heading_to", QVariant.Double)) fields.append(QgsField("heading_from", QVariant.Double)) layer = QgsVectorLayer("LineString?crs={}".format(canvasCrs.authid()), "Measurements", "memory") dp = layer.dataProvider() dp.addAttributes(fields) layer.updateFields() num = len(self.capturedPoints) for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) pts = self.getLinePts(distance, self.capturedPoints[i - 1], self.capturedPoints[i]) distance = self.unitDistance(distance) feat = QgsFeature(layer.fields()) feat.setAttribute(0, "{:.2f} {}".format(distance, units)) feat.setAttribute(1, distance) feat.setAttribute(2, units) feat.setAttribute(3, startA) feat.setAttribute(4, endA) feat.setGeometry(QgsGeometry.fromPolylineXY(pts)) dp.addFeatures([feat]) label = QgsPalLayerSettings() label.fieldName = 'label' label.placement = QgsPalLayerSettings.AboveLine labeling = QgsVectorLayerSimpleLabeling(label) layer.setLabeling(labeling) layer.setLabelsEnabled(True) layer.updateExtents() QgsProject.instance().addMapLayer(layer) def insertParams(self, position, distance, startAngle, endAngle): if position > self.tableWidget.rowCount(): self.tableWidget.insertRow(position - 1) item = QTableWidgetItem('{:.4f}'.format(self.unitDistance(distance))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 2, item) item = QTableWidgetItem('{:.4f}'.format(startAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 0, item) item = QTableWidgetItem('{:.4f}'.format(endAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 1, item) def formatTotal(self): total = self.currentDistance ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: total += self.distances[i] i += 1 self.distanceLineEdit.setText('{:.2f}'.format( self.unitDistance(total))) def clear(self): self.tableWidget.setRowCount(0) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.currentDistance = 0.0 self.distanceLineEdit.setText('') self.pointRb.reset(QgsWkbTypes.PointGeometry) self.lineRb.reset(QgsWkbTypes.LineGeometry) self.tempRb.reset(QgsWkbTypes.LineGeometry) self.saveToLayerButton.setEnabled(False) def unitDistance(self, distance): units = self.unitsComboBox.currentIndex() if units == 0: # meters return distance elif units == 1: # kilometers return distance / 1000.0 elif units == 2: # feet return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) elif units == 3: # yards return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceYards) elif units == 4: # miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceMiles) else: # nautical miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceNauticalMiles) def unitDesignator(self): units = self.unitsComboBox.currentIndex() if units == 0: # meters return 'm' elif units == 1: # kilometers return 'km' elif units == 2: # feet return 'ft' elif units == 3: # yards return 'yd' elif units == 4: # miles return 'mi' else: # nautical miles return 'nm'
class QgepMapToolAddFeature(QgsMapTool): """ Base class for adding features """ def __init__(self, iface, layer): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.layer = layer self.rubberband = QgsRubberBand(iface.mapCanvas(), layer.geometryType()) self.rubberband.setColor(QColor("#ee5555")) self.rubberband.setWidth(2) self.tempRubberband = QgsRubberBand(iface.mapCanvas(), layer.geometryType()) self.tempRubberband.setColor(QColor("#ee5555")) self.tempRubberband.setWidth(2) self.tempRubberband.setLineStyle(Qt.DotLine) def activate(self): """ When activating the map tool """ QgsMapTool.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) def deactivate(self): """ On deactivating the map tool """ QgsMapTool.deactivate(self) self.canvas.unsetCursor() # pylint: disable=no-self-use def isZoomTool(self): """ This is no zoom tool """ return False # =========================================================================== # Events # =========================================================================== def canvasReleaseEvent(self, event): """ Called when a mouse button is :param event: :return: """ if event.button() == Qt.RightButton: self.rightClicked(event) else: self.leftClicked(event) def leftClicked(self, event): """ When the canvas is left clicked we add a new point to the rubberband. :type event: QMouseEvent """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) self.tempRubberband.reset() def rightClicked(self, _): """ On a right click we create a new feature from the existing rubberband and show the add dialog """ f = QgsFeature(self.layer.pendingFields()) f.setGeometry(self.rubberband.asGeometry()) dlg = self.iface.getFeatureForm(self.layer, f) dlg.setIsAddDialog(True) dlg.exec_() self.rubberband.reset() self.tempRubberband.reset() def canvasMoveEvent(self, event): """ When the mouse is moved the rubberband needs to be updated :param event: The coordinates etc. """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.tempRubberband.movePoint(mousepos)
class VideoWidget(QVideoWidget): def __init__(self, parent=None): ''' Constructor ''' super(VideoWidget, self).__init__(parent) self.surface = VideoWidgetSurface(self) self.Tracking_RubberBand = QRubberBand(QRubberBand.Rectangle, self) self.Censure_RubberBand = QRubberBand(QRubberBand.Rectangle, self) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(QColor(Qt.blue))) self.Tracking_RubberBand.setPalette(pal) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(QColor(Qt.black))) self.Censure_RubberBand.setPalette(pal) self.var_currentMouseMoveEvent = None self._interaction = InteractionState() self._filterSatate = FilterState() self.setUpdatesEnabled(True) self.snapped = False self.zoomed = False self._isinit = False self.gt = None self.drawCesure = [] self.poly_coordinates, self.drawPtPos, self.drawLines, self.drawRuler, self.drawPolygon = [], [], [], [], [] self.poly_RubberBand = QgsRubberBand( iface.mapCanvas(), True) # Polygon type # set rubber band style color = QColor(176, 255, 128) self.poly_RubberBand.setColor(color) color.setAlpha(190) self.poly_RubberBand.setStrokeColor(color) self.poly_RubberBand.setWidth(3) self.parent = parent.parent() self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.setAttribute(Qt.WA_NoSystemBackground) self.setAttribute(Qt.WA_PaintOnScreen) self.setAttribute(Qt.WA_OpaquePaintEvent) self.setAttribute(Qt.WA_DeleteOnClose) palette = self.palette() palette.setColor(QPalette.Background, Qt.black) self.setPalette(palette) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.offset, self.origin, self.pressPos, self.dragPos = QPoint( ), QPoint(), QPoint(), QPoint() self.tapTimer = QBasicTimer() self.zoomPixmap, self.maskPixmap = QPixmap(), QPixmap() def removeLastLine(self): ''' Remove Last Line Objects ''' if len(self.drawLines) > 0: for pt in range(len(self.drawLines) - 1, -1, -1): del self.drawLines[pt] try: if self.drawLines[pt - 1][0] is None: break except Exception: None self.UpdateSurface() AddDrawLineOnMap(self.drawLines) return def removeLastSegmentLine(self): ''' Remove Last Segment Line Objects ''' if len(self.drawLines) > 0: del self.drawLines[-1] self.UpdateSurface() AddDrawLineOnMap(self.drawLines) return def removeAllLines(self): ''' Resets Line List ''' self.drawLines = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawLineOnMap() def ResetDrawRuler(self): ''' Resets Ruler List ''' self.drawRuler = [] def removeAllCensure(self): ''' Remove All Censure Objects ''' self.drawCesure = [] def removeLastCensured(self): ''' Remove Last Censure Objects ''' if len(self.drawCesure) > 0: del self.drawCesure[-1] def removeLastPoint(self): ''' Remove All Point Drawer Objects ''' if len(self.drawPtPos) > 0: del self.drawPtPos[-1] self.UpdateSurface() RemoveLastDrawPointOnMap() return def removeAllPoint(self): ''' Remove All Point Drawer Objects ''' self.drawPtPos = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawPointOnMap() return def removeAllPolygon(self): ''' Remove All Polygon Drawer Objects ''' self.drawPolygon = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawPolygonOnMap() def removeLastPolygon(self): ''' Remove Last Polygon Drawer Objects ''' if len(self.drawPolygon) > 0: for pt in range(len(self.drawPolygon) - 1, -1, -1): del self.drawPolygon[pt] try: if self.drawPolygon[pt - 1][0] is None: break except Exception: None self.UpdateSurface() # remove last index layer RemoveLastDrawPolygonOnMap() def currentMouseMoveEvent(self, event): self.var_currentMouseMoveEvent = event def keyPressEvent(self, event): ''' Exit fullscreen ''' if event.key() == Qt.Key_Escape and self.isFullScreen(): self.setFullScreen(False) event.accept() elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt: self.setFullScreen(not self.isFullScreen()) event.accept() else: super(VideoWidget, self).keyPressEvent(event) def mouseDoubleClickEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return if(not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): return if self.gt is not None and self._interaction.lineDrawer: self.drawLines.append([None, None, None]) self.UpdateSurface() return if self.gt is not None and self._interaction.ruler: self.drawRuler.append([None, None, None]) self.UpdateSurface() return if self.gt is not None and self._interaction.polygonDrawer: self.drawPolygon.append([None, None, None]) AddDrawPolygonOnMap(self.poly_coordinates) # Empty RubberBand for _ in range(self.poly_RubberBand.numberOfVertices()): self.poly_RubberBand.removeLastPoint() # Empty List self.poly_coordinates = [] self.UpdateSurface() return self.setFullScreen(not self.isFullScreen()) event.accept() def videoSurface(self): ''' Return video Surface ''' return self.surface def UpdateSurface(self): ''' Update Video Surface ''' self.surface.widget.update() def sizeHint(self): ''' This property holds the recommended size for the widget ''' return self.surface.surfaceFormat().sizeHint() def GetCurrentFrame(self): ''' Return current frame QImage ''' return self.surface.image def SetInvertColor(self, value): ''' Set Invert color filter ''' self._filterSatate.invertColorFilter = value def SetObjectTracking(self, value): ''' Set Object Tracking ''' self._interaction.objectTracking = value def SetRuler(self, value): ''' Set Ruler ''' self._interaction.ruler = value def SetCensure(self, value): ''' Set Censure Video Parts ''' self._interaction.censure = value def SetGray(self, value): ''' Set gray scale ''' self._filterSatate.grayColorFilter = value def SetMirrorH(self, value): ''' Set Horizontal Mirror ''' self._filterSatate.MirroredHFilter = value def SetEdgeDetection(self, value): ''' Set Canny Edge filter ''' self._filterSatate.edgeDetectionFilter = value def SetAutoContrastFilter(self, value): ''' Set Automatic Contrast filter ''' self._filterSatate.contrastFilter = value def SetMonoFilter(self, value): ''' Set mono filter ''' self._filterSatate.monoFilter = value def RestoreFilters(self): ''' Remove and restore all video filters ''' self._filterSatate.clear() def RestoreDrawer(self): ''' Remove and restore all Drawer Options ''' self._interaction.clear() def paintEvent(self, event): ''' Paint Event ''' self.gt = GetGCPGeoTransform() self.painter = QPainter(self) self.painter.setRenderHint(QPainter.HighQualityAntialiasing) if (self.surface.isActive()): videoRect = self.surface.videoRect() if not videoRect.contains(event.rect()): region = event.region() region.subtracted(QRegion(videoRect)) brush = self.palette().window() for rect in region.rects(): self.painter.fillRect(rect, brush) try: self.painter = self.surface.paint(self.painter) except Exception: None else: self.painter.fillRect(event.rect(), self.palette().window()) try: SetImageSize(self.surface.currentFrame.width(), self.surface.currentFrame.height()) except Exception: None # Draw On Video draw.drawOnVideo(self.drawPtPos, self.drawLines, self.drawPolygon, self.drawRuler, self.drawCesure, self.painter, self.surface, self.gt) # Magnifier Glass if self.zoomed and self._interaction.magnifier: draw.drawMagnifierOnVideo(self.width(), self.height( ), self.maskPixmap, self.dragPos, self.zoomPixmap, self.surface, self.painter, self.offset) self.painter.end() return def resizeEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ QWidget.resizeEvent(self, event) self.zoomed = False self.surface.updateVideoRect() def mouseMoveEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return # check if the point is on picture (not in black borders) if(not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): return if self._interaction.pointDrawer or self._interaction.polygonDrawer or self._interaction.lineDrawer or self._interaction.ruler: self.setCursor(QCursor(Qt.CrossCursor)) # Cursor Coordinates if self.gt is not None: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) txt = "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.3f" % Longitude) + "</span>" txt += "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.3f" % Latitude) + "</span>" if hasElevationModel(): txt += "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.0f" % Altitude) + "</span>" else: txt += "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>-</span>" self.parent.lb_cursor_coord.setText(txt) else: self.parent.lb_cursor_coord.setText("<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>" + "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>") if not event.buttons(): return if not self.Tracking_RubberBand.isHidden(): self.Tracking_RubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) if not self.Censure_RubberBand.isHidden(): self.Censure_RubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) if not self.zoomed: delta = event.pos() - self.pressPos if not self.snapped: self.pressPos = event.pos() self.pan(delta) self.tapTimer.stop() return else: threshold = 10 self.snapped &= delta.x() < threshold self.snapped &= delta.y() < threshold self.snapped &= delta.x() > -threshold self.snapped &= delta.y() > -threshold else: self.dragPos = event.pos() self.surface.updateVideoRect() def pan(self, delta): """ Pan Action (Magnifier method)""" self.offset += delta self.surface.updateVideoRect() def timerEvent(self, _): """ Time Event (Magnifier method)""" if not self.zoomed: self.activateMagnifier() self.surface.updateVideoRect() def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return if event.button() == Qt.LeftButton: self.snapped = True self.pressPos = self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(100, self) if(not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): self.UpdateSurface() return # point drawer if self.gt is not None and self._interaction.pointDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) pointIndex = len(self.drawPtPos) + 1 AddDrawPointOnMap(pointIndex, Longitude, Latitude, Altitude) self.drawPtPos.append([Longitude, Latitude, Altitude]) # polygon drawer if self.gt is not None and self._interaction.polygonDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.poly_RubberBand.addPoint(QgsPointXY(Longitude, Latitude)) self.poly_coordinates.extend(QgsPointXY(Longitude, Latitude)) self.drawPolygon.append([Longitude, Latitude, Altitude]) # line drawer if self.gt is not None and self._interaction.lineDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawLines.append([Longitude, Latitude, Altitude]) AddDrawLineOnMap(self.drawLines) if self._interaction.objectTracking: self.origin = event.pos() self.Tracking_RubberBand.setGeometry( QRect(self.origin, QSize())) self.Tracking_RubberBand.show() if self._interaction.censure: self.origin = event.pos() self.Censure_RubberBand.setGeometry( QRect(self.origin, QSize())) self.Censure_RubberBand.show() # Ruler drawer if self.gt is not None and self._interaction.ruler: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawRuler.append([Longitude, Latitude, Altitude]) # if not called, the paint event is not triggered. self.UpdateSurface() def activateMagnifier(self): """ Activate Magnifier Glass """ self.zoomed = True self.tapTimer.stop() self.surface.updateVideoRect() def SetMagnifier(self, value): """ Set Magnifier Glass """ self._interaction.magnifier = value def SetPointDrawer(self, value): """ Set Point Drawer """ self._interaction.pointDrawer = value def SetLineDrawer(self, value): """ Set Line Drawer """ self._interaction.lineDrawer = value def SetPolygonDrawer(self, value): """ Set Polygon Drawer """ self._interaction.polygonDrawer = value def mouseReleaseEvent(self, _): """ :type event: QMouseEvent :param event: :return: """ if self._interaction.censure: geom = self.Tracking_RubberBand.geometry() self.Censure_RubberBand.hide() self.drawCesure.append([geom]) if self._interaction.objectTracking: geom = self.Tracking_RubberBand.geometry() bbox = (geom.x(), geom.y(), geom.width(), geom.height()) frame = convertQImageToMat(self.GetCurrentFrame()) self.Tracking_RubberBand.hide() self.tracker = cv2.TrackerBoosting_create() self.tracker.clear() ok = self.tracker.init(frame, bbox) if ok: self._isinit = True else: self._isinit = False def leaveEvent(self, _): self.parent.lb_cursor_coord.setText("") self.setCursor(QCursor(Qt.ArrowCursor))
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 DsgLineTool(QgsMapTool): lineCreated = pyqtSignal(QgsGeometry) def __init__(self, canvas): super(DsgLineTool, self).__init__(canvas) self.canvas = canvas self.rubberBand = None self.reset() def deactivate(self): self.canvas.scene().removeItem(self.rubberBand) super(DsgLineTool, self).deactivate() def defineRubberBand(self): settings = QSettings() myRed = int(settings.value("/qgis/default_measure_color_red", 222)) myGreen = int(settings.value("/qgis/default_measure_color_green", 155)) myBlue = int(settings.value("/qgis/default_measure_color_blue", 67)) self.rubberBand = QgsRubberBand(self.canvas) self.rubberBand.setColor(QColor(myRed, myGreen, myBlue, 100)) self.rubberBand.setWidth(3) def reset(self): if self.rubberBand: self.rubberBand.reset(QGis.Line) self.isEmittingPoint = False self.defineRubberBand() def canvasPressEvent(self, e): if self.isEmittingPoint: point = self.snapPoint(e.pos()) self.rubberBand.addPoint(point, True) else: self.reset() self.isEmittingPoint = True def canvasReleaseEvent(self, e): point = self.snapPoint(e.pos()) if e.button() == Qt.RightButton: geom = self.rubberBand.asGeometry() self.reset() self.lineCreated.emit(geom) elif e.button() == Qt.LeftButton: self.isEmittingPoint = True self.rubberBand.addPoint(point, True) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return point = self.snapPoint(e.pos()) self.rubberBand.movePoint(point) def snapPoint(self, p): m = self.canvas.snappingUtils().snapToMap(p) if m.isValid(): return m.point() else: return self.canvas.getCoordinateTransform().toMapCoordinates(p)
class Qgis2threejsDialog(QDialog): STYLE_MAX_COUNT = 3 def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.apiChanged22 = False # not QgsApplication.prefixPath().startswith("C:/OSGeo4W") # QGis.QGIS_VERSION_INT >= 20200 # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) # DEM tab ui.toolButton_switchFocusMode.setVisible(False) ui.toolButton_PointTool.setVisible(False) ui.progressBar.setVisible(False) self.switchFocusMode(True) ui.toolButton_Browse.clicked.connect(self.browseClicked) ui.radioButton_Simple.toggled.connect(self.samplingModeChanged) ui.horizontalSlider_Resolution.valueChanged.connect( self.calculateResolution) ui.spinBox_Height.valueChanged.connect(self.updateQuads) ui.toolButton_switchFocusMode.clicked.connect( self.switchFocusModeClicked) ui.toolButton_PointTool.clicked.connect(self.startPointSelection) # Vector tab ui.treeWidget_VectorLayers.setHeaderLabel("Vector layers") self.initVectorStyleWidgets() ui.treeWidget_VectorLayers.currentItemChanged.connect( self.currentVectorLayerChanged) ui.treeWidget_VectorLayers.itemChanged.connect( self.vectorLayerItemChanged) ui.comboBox_ObjectType.currentIndexChanged.connect( self.objectTypeSelectionChanged) self.bar = None self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.currentVectorLayer = None self.vectorPropertiesDict = {} self.objectTypeManager = ObjectTypeManager() # set map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) self.connect(self.mapTool, SIGNAL("rectangleCreated()"), self.rectangleSelected) # self.mapTool = PointMapTool(iface.mapCanvas()) # QObject.connect(self.mapTool, SIGNAL("pointSelected()"), self.pointSelected) iface.mapCanvas().mapToolSet.connect(self.mapToolSet) self.startPointSelection() def exec_(self): ui = self.ui messages = [] # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings( ) if self.apiChanged22 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees", "Terrain may not appear well.") # show message if there are no dem layer no_demlayer = ui.comboBox_DEMLayer.count() == 0 ui.pushButton_Run.setEnabled(not no_demlayer) if no_demlayer: self.showMessageBar( "No DEM layer", "Load 1-band raster layer with GDAL provider.", QgsMessageBar.WARNING) return QDialog.exec_(self) def showMessageBar(self, title, text, level=QgsMessageBar.INFO): if self.bar is None: self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) ui = self.ui margins = ui.gridLayout.getContentsMargins() vl = ui.gridLayout.takeAt(0) ui.gridLayout.setContentsMargins(0, 0, 0, 0) ui.gridLayout.addWidget(self.bar, 0, 0) ui.gridLayout.addItem(vl, 1, 0) ui.verticalLayout.setContentsMargins(margins[0], margins[1] / 2, margins[2], margins[3]) self.bar.pushMessage(title, text, level=level) def initDEMLayerList(self, layerId=None): # list 1 band raster layers self.ui.comboBox_DEMLayer.clear() for id, layer in QgsMapLayerRegistry().instance().mapLayers().items(): if layer.type() == QgsMapLayer.RasterLayer and layer.providerType( ) == "gdal" and layer.bandCount() == 1: self.ui.comboBox_DEMLayer.addItem(layer.name(), id) # select the last selected layer if layerId is not None: index = self.ui.comboBox_DEMLayer.findData(layerId) if index != -1: self.ui.comboBox_DEMLayer.setCurrentIndex(index) return index return -1 def initVectorLayerTree(self, vectorPropertiesDict): self.vectorPropertiesDict = vectorPropertiesDict tree = self.ui.treeWidget_VectorLayers tree.clear() # add vector layers into tree widget self.treeTopItems = topItems = { QGis.Point: QTreeWidgetItem(tree, ["Point"]), QGis.Line: QTreeWidgetItem(tree, ["Line"]), QGis.Polygon: QTreeWidgetItem(tree, ["Polygon"]) } self.vlItems = {} for layer in self.iface.legendInterface().layers(): if layer.type() != QgsMapLayer.VectorLayer: continue geometry_type = layer.geometryType() if geometry_type in [QGis.Point, QGis.Line, QGis.Polygon]: self.vlItems[layer.id()] = item = QTreeWidgetItem( topItems[geometry_type], [layer.name()]) if layer.id() in self.vectorPropertiesDict: isVisible = self.vectorPropertiesDict[ layer.id()]["visible"] else: isVisible = False #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) #item.setDisabled(True) #item.setData(0, Qt.CheckStateRole, Qt.Unchecked) for item in topItems.values(): tree.expandItem(item) self.setVectorStylesEnabled(False) def initVectorStyleWidgets(self): self.colorWidget = StyleWidget(StyleWidget.COLOR) self.ui.verticalLayout_Styles.addWidget(self.colorWidget) self.heightWidget = StyleWidget(StyleWidget.HEIGHT) self.ui.verticalLayout_zCoordinate.addWidget(self.heightWidget) self.styleWidgets = [] for i in range(self.STYLE_MAX_COUNT): widget = StyleWidget() widget.setVisible(False) self.ui.verticalLayout_Styles.addWidget(widget) self.styleWidgets.append(widget) def currentVectorLayerChanged(self, currentItem, previousItem): # save properties of previous item if previousItem is not None: layerid = previousItem.data(0, Qt.UserRole) if layerid is not None: self.saveVectorProperties(layerid) layerid = currentItem.data(0, Qt.UserRole) if layerid is None: self.currentVectorLayer = None return self.currentVectorLayer = layer = QgsMapLayerRegistry().instance( ).mapLayer(layerid) if layer is None: return for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].hide() obj_types = self.objectTypeManager.objectTypeNames( layer.geometryType()) ui = self.ui ui.comboBox_ObjectType.blockSignals(True) ui.comboBox_ObjectType.clear() ui.comboBox_ObjectType.addItems(obj_types) ui.comboBox_ObjectType.blockSignals(False) # set up property widgets self.objectTypeSelectionChanged() if layerid in self.vectorPropertiesDict: # restore properties self.restoreVectorProperties(layerid) self.setVectorStylesEnabled( currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def vectorLayerItemChanged(self, item, column): # update style form enablement currentItem = self.ui.treeWidget_VectorLayers.currentItem() if currentItem: self.setVectorStylesEnabled( currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def objectTypeSelectionChanged(self, idx=None): layer = self.currentVectorLayer try: ve = float(ui.lineEdit_zFactor.text()) except: ve = 1 mapTo3d = MapTo3D(self.iface.mapCanvas(), verticalExaggeration=ve) self.objectTypeManager.setupForm( self, mapTo3d, layer, layer.geometryType(), self.ui.comboBox_ObjectType.currentIndex()) def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def setVectorStylesEnabled(self, enabled): self.ui.comboBox_ObjectType.setEnabled(enabled) self.ui.label_ObjectType.setEnabled(enabled) self.colorWidget.setEnabled(enabled) self.heightWidget.setEnabled(enabled) for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].setEnabled(enabled) def saveVectorProperties(self, layerid): properties = {} layer = QgsMapLayerRegistry().instance().mapLayer(layerid) itemIndex = self.ui.comboBox_ObjectType.currentIndex() properties["itemindex"] = itemIndex properties["typeitem"] = self.objectTypeManager.objectTypeItem( layer.geometryType(), itemIndex) properties["visible"] = self.vlItems[ self.currentVectorLayer.id()].data(0, Qt.CheckStateRole) == Qt.Checked properties["color"] = self.colorWidget.values() properties["height"] = self.heightWidget.values() for i in range(self.STYLE_MAX_COUNT): if self.styleWidgets[i].isVisible(): properties[i] = self.styleWidgets[i].values() self.vectorPropertiesDict[layerid] = properties def restoreVectorProperties(self, layerid): properties = self.vectorPropertiesDict[layerid] self.ui.comboBox_ObjectType.setCurrentIndex(properties["itemindex"]) self.colorWidget.setValues(properties["color"]) self.heightWidget.setValues(properties["height"]) for i in range(self.STYLE_MAX_COUNT): if i in properties: self.styleWidgets[i].setValues(properties[i]) def calculateResolution(self, v=None): extent = self.iface.mapCanvas().extent() renderer = self.iface.mapCanvas().mapRenderer() size = 100 * self.ui.horizontalSlider_Resolution.value() self.ui.label_Resolution.setText("about {0} x {0} px".format(size)) # calculate resolution and size width, height = renderer.width(), renderer.height() s = (size * size / float(width * height))**0.5 if s < 1: width = int(width * s) height = int(height * s) xres = extent.width() / width yres = extent.height() / height self.ui.lineEdit_HRes.setText(str(xres)) self.ui.lineEdit_VRes.setText(str(yres)) self.ui.lineEdit_Width.setText(str(width + 1)) self.ui.lineEdit_Height.setText(str(height + 1)) def progress(self, percentage): self.ui.progressBar.setValue(percentage) self.ui.progressBar.setVisible(percentage != 100) def run(self): ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename != "" and QFileInfo( filename).exists() and QMessageBox.question( None, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return self.endPointSelection() item = ui.treeWidget_VectorLayers.currentItem() if item: self.saveVectorProperties(item.data(0, Qt.UserRole)) ui.pushButton_Run.setEnabled(False) self.progress(0) canvas = self.iface.mapCanvas() htmlfilename = ui.lineEdit_OutputFilename.text() demlayerid = ui.comboBox_DEMLayer.itemData( ui.comboBox_DEMLayer.currentIndex()) mapTo3d = MapTo3D(canvas, verticalExaggeration=float( ui.lineEdit_zFactor.text())) if self.ui.radioButton_Simple.isChecked(): dem_width = int(ui.lineEdit_Width.text()) dem_height = int(ui.lineEdit_Height.text()) context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode, dem_width, dem_height) htmlfilename = runSimple(htmlfilename, context, self.progress) else: context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode) htmlfilename = runAdvanced(htmlfilename, context, self, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if htmlfilename is None: return self.clearRubberBands() if not tools.openHTMLFile(htmlfilename): return QDialog.accept(self) def reject(self): self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def startPointSelection(self): canvas = self.iface.mapCanvas() self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.ui.toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() self.iface.mapCanvas().setMapTool(self.previousMapTool) def rectangleSelected(self): ui = self.ui ui.radioButton_Advanced.setChecked(True) rect = self.mapTool.rectangle() toRect = rect.width() and rect.height() self.switchFocusMode(toRect) ui.lineEdit_xmin.setText(str(rect.xMinimum())) ui.lineEdit_ymin.setText(str(rect.yMinimum())) ui.lineEdit_xmax.setText(str(rect.xMaximum())) ui.lineEdit_ymax.setText(str(rect.yMaximum())) quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(rect, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), rect.center()) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def pointSelected(self): # set values of controls self.ui.lineEdit_CenterX.setText(str(self.mapTool.point.x())) self.ui.lineEdit_CenterY.setText(str(self.mapTool.point.y())) self.ui.radioButton_Advanced.setChecked(True) quadtree = QuadTree(self.iface.mapCanvas().extent(), self.mapTool.point, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), self.mapTool.point) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def mapToolSet(self, mapTool): if mapTool != self.mapTool: self.ui.toolButton_PointTool.setVisible(True) def createQuadTree(self): ui = self.ui try: c = map(float, [ ui.lineEdit_xmin.text(), ui.lineEdit_ymin.text(), ui.lineEdit_xmax.text(), ui.lineEdit_ymax.text() ]) except: return None quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(QgsRectangle(c[0], c[1], c[2], c[3]), ui.spinBox_Height.value()) return quadtree def createRubberBands(self, quads, point=None): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) for quad in quads: points = [] extent = quad.extent points.append(QgsPoint(extent.xMinimum(), extent.yMinimum())) points.append(QgsPoint(extent.xMinimum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMinimum())) self.rb_quads.addGeometry(QgsGeometry.fromPolygon([points]), None) self.log(extent.toString()) self.log("Quad count: %d" % len(quads)) # create a point with rubber band if point: self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(point) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = self.ui.lineEdit_OutputFilename.text() if directory == "": directory = QDir.homePath() filename = QFileDialog.getSaveFileName( self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if filename != "": self.ui.lineEdit_OutputFilename.setText(filename) def samplingModeChanged(self): ui = self.ui isSimpleMode = ui.radioButton_Simple.isChecked() simple_widgets = [ ui.horizontalSlider_Resolution, ui.lineEdit_Width, ui.lineEdit_Height, ui.lineEdit_HRes, ui.lineEdit_VRes ] for w in simple_widgets: w.setEnabled(isSimpleMode) isAdvancedMode = not isSimpleMode advanced_widgets = [ ui.spinBox_Height, ui.lineEdit_xmin, ui.lineEdit_ymin, ui.lineEdit_xmax, ui.lineEdit_ymax, ui.toolButton_switchFocusMode ] for w in advanced_widgets: w.setEnabled(isAdvancedMode) def updateQuads(self, v=None): quadtree = self.createQuadTree() if quadtree: self.createRubberBands(quadtree.quads(), quadtree.focusRect.center()) else: self.clearRubberBands() def switchFocusModeClicked(self): self.switchFocusMode(not self.ui.label_xmin.isVisible()) def switchFocusMode(self, toRect): ui = self.ui toPoint = not toRect ui.label_xmin.setVisible(toRect) ui.label_ymin.setVisible(toRect) ui.lineEdit_xmin.setVisible(toRect) ui.lineEdit_ymin.setVisible(toRect) suffix = "max" if toRect else "" ui.label_xmax.setText("x" + suffix) ui.label_ymax.setText("y" + suffix) mode = "point" if toRect else "rectangle" ui.toolButton_switchFocusMode.setText("To " + mode + " selection") selection = "area" if toRect else "point" action = "Stroke a rectangle" if toRect else "Click" ui.label_Focus.setText( "Focus {0} ({1} on map canvas to set values)".format( selection, action)) def log(self, msg): if debug_mode: qDebug(msg)
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 Gban: def __init__(self, iface): locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( os.path.dirname(__file__), 'i18n', 'gban_{}.qm'.format(locale)) self.translator = None if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.iface = iface self.canvas = self.iface.mapCanvas() self.exclusive = QActionGroup( self.iface.mainWindow() ) self.actions = [] self.menu = '&Gban' self.toolbar = self.iface.addToolBar('Gban') self.toolbar.setObjectName('Gban') #Select tool initialization self.tool = QgsMapToolEmitPoint(self.canvas) self.tool.canvasClicked.connect(self.doReverseGeocoding) self.tool.deactivated.connect(self.uncheckReverseGeocoding) self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor( QColor(255, 0, 0) ) self.rb.setWidth( 5 ) # Network configuration self.manager = QgsNetworkAccessManager.instance() def unload(self): for action in self.actions: self.iface.removePluginMenu('&Gban', action) self.iface.removeToolBarIcon(action) del self.toolbar def tr(self, message): return QCoreApplication.translate('Gban', message) def add_action( self, icon_path, text, callback, enabled_flag=True, checkable=False, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) action.setCheckable(checkable) 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) if checkable: self.exclusive.addAction( action ) self.actions.append(action) return action def initGui(self): icon_path = ":/plugins/qgeric/resources/icon_geocode.png" self.add_action( icon_path, text=self.tr("Geocoding"), callback=self.geocoding, parent=self.iface.mainWindow() ) icon_path = ":/plugins/qgeric/resources/icon_reversegeocode.png" self.add_action( icon_path, checkable = True, text=self.tr("Reverse geocoding"), callback=self.reverseGeocoding, parent=self.iface.mainWindow() ) def geocoding(self): self.rb.reset( QGis.Point ) address, ok = QInputDialog.getText(self.iface.mainWindow(), self.tr("Address"), self.tr("Input address to geocode:")) if ok and address: self.doGeocoding(address) def doGeocoding(self, address): address = unicodedata.normalize('NFKD', unicode(address)).encode('ASCII', 'ignore') url = "http://api-adresse.data.gouv.fr/search/?q="+address.replace(" ", "%20") result = self.request(url) try: data = json.loads(result) features = data["features"] if len(features) > 0: feature_list = [] for feature in features: feature_list.append(feature["properties"]["label"]+" - "+str(round(feature["properties"]["score"]*100))+"%") feature, ok = QInputDialog.getItem(self.iface.mainWindow(), self.tr("Result"), "", feature_list) if ok: index = feature_list.index(feature) x = features[index]["geometry"]["coordinates"][0] y = features[index]["geometry"]["coordinates"][1] point_4326 = QgsPoint(x, y) transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem(4326), self.canvas.mapSettings().destinationCrs()) point_2154 = transform.transform(point_4326) self.rb.addPoint(point_2154) self.iface.mapCanvas().setCenter(point_2154) self.iface.mapCanvas().refresh() else: QMessageBox.information(self.iface.mainWindow(), self.tr("Result"), self.tr("No result.")) except ValueError: QMessageBox.critical(self.iface.mainWindow(), self.tr("Error"), self.tr("An error occured. Check your network settings (proxy).")) def reverseGeocoding(self): self.canvas.setMapTool(self.tool) def doReverseGeocoding(self, point_orig): transform = QgsCoordinateTransform(self.canvas.mapSettings().destinationCrs(), QgsCoordinateReferenceSystem(4326)) point_4326 = transform.transform(point_orig) url = "http://api-adresse.data.gouv.fr/reverse/?lon="+str(point_4326.x())+"&lat="+str(point_4326.y()) result = self.request(url) try: data = json.loads(result) if len(data["features"]) > 0: address = data["features"][0]["properties"]["label"] clicked = QMessageBox.information(self.iface.mainWindow(), self.tr("Result"), address, QDialogButtonBox.Ok, QDialogButtonBox.Save) if clicked == QDialogButtonBox.Save: QApplication.clipboard().setText(address) else: QMessageBox.information(self.iface.mainWindow(), self.tr("Result"), self.tr("No result.")) except ValueError: QMessageBox.critical(self.iface.mainWindow(), self.tr("Error"), self.tr("An error occured. Check your network settings (proxy).")) def uncheckReverseGeocoding(self): self.exclusive.checkedAction().setChecked(False) def request(self, url): ''' prepare the request and return the result of the reply ''' request = QNetworkRequest(QUrl(url)) reply = self.manager.get(request) reply.deleteLater() evloop = QEventLoop() reply.finished.connect(evloop.quit) evloop.exec_(QEventLoop.ExcludeUserInputEvents) return unicode(reply.readAll())
def btnPreview_Click(self): self.priviewClickFlag = not self.priviewClickFlag if not self.priviewClickFlag: QgisHelper.ClearRubberBandInCanvas(define._canvas) return if self.comboBox.currentIndex() == ProtectionAreaType.Primary: rBand = QgsRubberBand(define._canvas, QGis.Polygon) for point in self.area.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() elif self.comboBox.currentIndex() == ProtectionAreaType.Secondary: rBand = QgsRubberBand(define._canvas, QGis.Polygon) if isinstance(self.area, SecondaryObstacleAreaWithManyPoints): for point in self.area.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() else: for point in self.area.area.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() elif self.comboBox.currentIndex( ) == ProtectionAreaType.PrimaryAndSecondary: rBand = QgsRubberBand(define._canvas, QGis.Polygon) for point in self.area.primaryArea.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() rBand = QgsRubberBand(define._canvas, QGis.Polygon) for point in self.area.secondaryArea1.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() rBand = QgsRubberBand(define._canvas, QGis.Polygon) for point in self.area.secondaryArea2.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() else: for obstacleArea in self.area: if isinstance(obstacleArea, PrimaryObstacleArea): rBand = QgsRubberBand(define._canvas, QGis.Polygon) for point in obstacleArea.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() else: rBand = QgsRubberBand(define._canvas, QGis.Polygon) if isinstance(obstacleArea, SecondaryObstacleAreaWithManyPoints): for point in obstacleArea.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() else: for point in obstacleArea.area.PreviewArea.method_14(): rBand.addPoint(point) rBand.setFillColor(QtGui.QColor(46, 64, 142, 100)) rBand.setBorderColor(QtGui.QColor(0, 10, 238)) rBand.show() define._canvas.refresh()
class RubberBandResultRenderer(): def __init__(self): self.iface = iface self.srs_wgs84 = QgsCoordinateReferenceSystem(4326) self.transformation = QgsCoordinateTransform(self.srs_wgs84, self.srs_wgs84) self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor(QColor('magenta')) self.rb.setIconSize(12) self.features_rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.features_rb.setColor(QColor('green')) self.features_rb.setIconSize(12) self.features_rb.setWidth(3) def show_point(self, point, center=False): #check srs if self.need_transform(): point = self.transform_point(point) self.rb.addPoint(point) if center: self.center_to_point(point) def clear(self): self.rb.reset(QGis.Point) def need_transform(self): return self.iface.mapCanvas().mapRenderer().destinationCrs().postgisSrid() != 4326 def transform_point(self, point): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transform(point) except: print 'Error on transform!' # DEBUG! need message??? return def transform_bbox(self, bbox): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transformBoundingBox(bbox) except: print 'Error on transform!' # DEBUG! need message??? return def transform_geom(self, geom): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: geom.transform(self.transformation) return geom except: print 'Error on transform!' # DEBUG! need message??? return def center_to_point(self, point): canvas = self.iface.mapCanvas() new_extent = QgsRectangle(canvas.extent()) new_extent.scale(1, point) canvas.setExtent(new_extent) canvas.refresh() def zoom_to_bbox(self, bbox): if self.need_transform(): bbox = self.transform_bbox(bbox) self.iface.mapCanvas().setExtent(bbox) self.iface.mapCanvas().refresh() def show_feature(self, geom): if self.need_transform(): geom = self.transform_geom(geom) self.features_rb.setToGeometry(geom, None) def clear_feature(self): self.features_rb.reset(QGis.Point)
class DivisionFusion: """QGIS Plugin Implementation.""" def __init__(self, iface, ACA): """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 self.iface = iface self.CFG = None self.UTI = None self.DFS = None self.DBJ = None self.ELM = None self.ACA = ACA self.TPG = None # 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', 'DivisionFusion_{}.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) # Create the dialog (after translation) and keep reference self.dlg = DivisionFusionDialog(parent=iface.mainWindow()) #self.dlg.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&DivisionFusion') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'DivisionFusion') self.toolbar.setObjectName(u'DivisionFusion') #self.dlg.setMinimumSize(QSize(464, 465)) #self.dlg.setMaximumSize(QSize(371, 372)) #print(self.dlg.size()) #self.dlg.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.eventos = EventoDivision(iface.mapCanvas(), self) self.VentanaAreas = VentanaAreas(self) self.VentanaFusion = VentanaFusionV3(iface, self) self.VentanaClaves = VentanaClavesV3(iface, self) self.rubberMarca = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self.rubberMarca.setFillColor(QColor(255, 255, 0, 12)) self.rubberMarca.setStrokeColor(QColor(255, 150, 0, 255)) self.rubberMarca.setWidth(2) self.listaNuevosPredios = [] self.cargoPredio = False self.listaColores = [] self.rubbersAreas = [] self.dlg.btnDibujarCortes.setEnabled(False) self.dlg.btnEditarCortes.setEnabled(False) self.dlg.btnEliminarCortes.setEnabled(False) self.dlg.btnApagarHerramientas.setEnabled(False) self.dlg.btnConfirmarCortes.setEnabled(False) self.dlg.btnDeshacerTodo.setEnabled(False) self.dlg.btnCancelarSub.setEnabled(False) self.dlg.btnDeshacerCortes.setEnabled(False) self.dlg.btnLlamarCalcular.setEnabled(False) self.dlg.btnFusionar.clicked.connect(self.preguntarFusion) self.dlg.btnCargarPredio.clicked.connect(self.pasarAModoDivision) self.dlg.btnConfirmarCortes.clicked.connect(self.confirmarCortes) self.dlg.btnDibujarCortes.clicked.connect(self.encenderModoDividir) self.dlg.btnEliminarCortes.clicked.connect(self.encenderModoEliminar) self.dlg.btnApagarHerramientas.clicked.connect(self.apagarHerramientas) self.dlg.btnDeshacerTodo.clicked.connect(self.rollBack) self.dlg.btnCancelarSub.clicked.connect(self.cancelarSubdivision) self.dlg.btnDeshacerCortes.clicked.connect(self.vaciarLineasCorte) self.dlg.btnLlamarCalcular.clicked.connect(self.irAreas) self.dlg.btnEditarCortes.clicked.connect(self.encenderModoEditar) self.geomsAreas = [] #self.p # 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('DivisionFusion', 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 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 show 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 initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/DivisionFusion/icon.png' self.add_action(icon_path, text=self.tr(u'DivisionFusion'), callback=self.run, parent=self.iface.mainWindow()) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u'&DivisionFusion'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def run(self): """Run method that performs all the real work""" # show the dialog for x in iface.mapNavToolToolBar().actions(): if x.objectName() == 'mActionPan': x.trigger() self.dlg.show() self.UTI.strechtTabla(self.VentanaFusion.dlg.tabComp) # Run the dialog event loop iface.mapCanvas().setMapTool(self.eventos) result = self.dlg.exec_() # See if OK was pressed self.dlg.btnFusionar.setEnabled(True) self.dlg.comboPredios.setEnabled(True) self.dlg.btnCargarPredio.setEnabled(True) self.dlg.btnDibujarCortes.setEnabled(False) self.dlg.btnEditarCortes.setEnabled(False) self.dlg.btnEliminarCortes.setEnabled(False) self.dlg.btnApagarHerramientas.setEnabled(False) self.dlg.btnConfirmarCortes.setEnabled(False) self.dlg.btnDeshacerTodo.setEnabled(False) if result: # Do something useful here - delete the line containing pass and # substitute with your code. pass #------------------------------------------------------------------------------ #Preguntamos si quiere fusionar def preguntarFusion(self): if iface.activeLayer() != None: seleccion = self.iface.activeLayer().selectedFeatures() self.VentanaFusion.dlg.close() if self.validarCuentaSeleccion(): #Si la seleccion es valida self.VentanaFusion.dlg.show() self.VentanaFusion.llenarTablaComp(seleccion[0], seleccion[1]) self.dlg.btnCargarPredio.setEnabled(False) else: self.UTI.mostrarAlerta( 'La fusion requiere la seleccion de exactamente 2 predios contiguos', QMessageBox().Critical, 'Error de fusion') #------------------------------------------------------------------ #Validamosla seleccion def validarCuentaSeleccion(self): seleccion = self.iface.activeLayer().selectedFeatures() if len(seleccion) != 2: #Cuando tenemos seleccionados no 2 elementos self.UTI.mostrarAlerta( 'La fusion requiere la seleccion de exactamente 2 predios contiguos', QMessageBox().Critical, 'Error de fusion') return False else: if not self.validarContiguedad(): self.UTI.mostrarAlerta( 'Los predios seleccionados no son contiguos', QMessageBox().Critical, 'Error de fusion') return False else: return True #----------------------------------------------------------------------------- def validarContiguedad(self): #Aqui checamos si dos predios son contiguos seleccion = self.iface.activeLayer().selectedFeatures() pred1 = seleccion[0].geometry().buffer(0.0000001, 1) pred2 = seleccion[1].geometry().buffer(0.0000001, 1) #Checamos que el area de interseccion sea un valor considerable area = pred1.intersection(pred2).area() return area > 0.0000000001 #----------------------------------------------------------------------------------- def fusionarPredios(self, eleccion): #Aqui fusionamos los predios #OBtener las capas capaManzana = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('manzana')) capaPuntos = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.num')) capaPredios = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.geom')) capaConstru = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('construcciones')) capaCondH = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('horizontales.geom')) #Puntos y eleccion de victima seleccion = self.iface.activeLayer().selectedFeatures() listaPuntos = [None, None] #Definimos el predio 'victima if eleccion == 0: victima = 1 else: victima = 0 #eliminamos el predio no elegido self.eliminarNoElegido(seleccion[victima]) #Eliminar punto del predio no elegido for punto in capaPuntos.getFeatures(): if punto.geometry().intersects(seleccion[victima].geometry( )): #Bsucamos el punto del predio victima capaPuntos.startEditing() capaPuntos.dataProvider().deleteFeatures([punto.id()]) capaPuntos.triggerRepaint() capaPuntos.commitChanges() break #Generar geometria del predio fusionado rango = len(seleccion) geomFusion = seleccion[0].geometry() featFusion = QgsFeature() capaPredios.startEditing() #Generar geometria del predio fusionado for i in range(0, rango): self.iface.activeLayer().dataProvider().deleteFeatures( [seleccion[i].id()]) geomFusion = geomFusion.combine(seleccion[i].geometry()) #self.geom listaFinal = [] listaPrimera = [] featFusion.setGeometry(geomFusion) #Renombrar construcciones cuentaConstruccion = 1 capaConstru.startEditing() #--------Renombrar construcciones---- for construccion in capaConstru.getFeatures(): geomConstru = construccion.geometry() if geomConstru.buffer(-0.0000001, 1).intersects( geomFusion ): #Sacamos las construcciones dentro del predio fusionado listaPrimera.append(construccion) else: if self.contarIntegraciones( geomConstru.buffer(-0.0000001, 1), 'predios.geom' ) == 0: #Obtenemos los aleros del predio fusionado if geomConstru.buffer( 0.0000001, 1).intersection(geomFusion).area() > 0.000000001: #print(geomConstru.buffer(0.0000001, 1).intersection(geomFusion).area()) listaPrimera.append(construccion) for construccion in listaPrimera: geomConstru = construccion.geometry() seIncluye = True for condominio in capaCondH.getFeatures(): geomHori = condominio.geometry() if geomConstru.buffer(-0.0000001, 1).intersects(geomHori): seIncluye = False break if seIncluye: listaFinal.append(construccion) supConst = 0 #Renombramos ls volumenes for construccion in listaFinal: geomConstru = construccion.geometry() construccion['nom_volumen'] = 'V' + str(cuentaConstruccion) niveles = construccion['num_niveles'] if niveles <= 0: niveles = 1 capaConstru.updateFeature(construccion) cuentaConstruccion += 1 supConst += (geomConstru.area() * niveles) #---------------------------------------------- #Estableer atributos del predio fusionado idx1 = capaPredios.fields().lookupField('sup_terr') idx2 = capaPredios.fields().lookupField('sup_contruccion') attr = seleccion[eleccion].attributes() attr[idx1] = geomFusion.area() attr[idx2] = supConst featFusion.setAttributes(attr) capaPredios.updateFeature(featFusion) capaPredios.dataProvider().addFeatures([featFusion]) capaPredios.triggerRepaint() capaPredios.commitChanges() self.UTI.mostrarAlerta('Fusion completada con exito', QMessageBox().Information, 'Fusion de predios') self.dlg.close() #----------------------------------------------------------------------------------- #Aqui contmaos cuantas geometrias de una capa toca una geomtrias especifica def contarIntegraciones(self, geometria, nombreCapa): capa = QgsProject.instance().mapLayersByName(nombreCapa)[0] cuenta = 0 for feat in capa.getFeatures(): if geometria.intersects(feat.geometry()): cuenta += 1 return cuenta #--------------------------------------------------------------------------- #Poinemos en lista de eliminados el predio no elegido def eliminarNoElegido(self, feat): campos = {} campos['wkt'] = feat.geometry().asWkt() campos['srid'] = 32614 campos['tabla'] = 'e_predio' atributos = {} capaPredios = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.geom')) nombresAtrbutos = capaPredios.fields() nombres = [campo.name() for campo in nombresAtrbutos] for x in range(0, len(nombres)): atributo = feat.attributes()[x] if str(feat.attributes()[x]) == "NULL": atributo = None atributos[str(nombres[x])] = atributo campos['attr'] = atributos campos['nuevo'] = False campos['eliminado'] = True listaTemp = QSettings().value('listaEliminada') listaTemp.append(campos) QSettings().setValue('listaEliminada', listaTemp) #-------------------------------------------------------------------------- #Activamos el modo de division def pasarAModoDivision(self): clave = self.dlg.comboPredios.currentText( ) #Obtenemos la clave del predio a editar if clave == '': self.UTI.mostrarAlerta( 'Primero debes cargar una manzana de la seccion de consulta', QMessageBox().Critical, 'Error de cargado de predio') return capaPredios = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.geom')) #capaPredios.setReadOnly(False) capaPredios.startEditing() self.predioEnDivision = None for predio in capaPredios.getFeatures( ): #Obtenemos el feature a dividr con base en la clave elegida if predio.attributes()[1] == clave: self.predioEnDivision = predio break if self.predioEnDivision != None: self.atributosAHeredar = self.predioEnDivision.attributes( ) #Obtenemos los atributos a heredar self.atributosAHeredar[1] = None #Ponemos le id como vacio self.rubberMarca.reset( QgsWkbTypes.PolygonGeometry) #Borramos la geometria amarillita self.UTI.mostrarAlerta( "Dibuja las lineas de corte sobre el predio indicado.\nRecuerda que los cortes no pueden estar sobre construcciones o condiminios.\nAsegurate que las lineas atraviesen el predio por completo\nNOTA: Las lineas solo afectaran al predio seleccionado.", QMessageBox().Information, "Subdivision de predios") self.cargoPredio = True self.encenderModoDividir() #Encendemos modo dividir capaPredios.commitChanges() self.geomEnDivision = self.predioEnDivision.geometry() n = 0 #Obtenemos todos los vertices de la geometria en division ver = self.geomEnDivision.vertexAt(0) listaVertices = [] while (ver != QgsPoint(0, 0)): n += 1 ver = self.geomEnDivision.vertexAt(n) listaVertices.append(ver) listaVertices.append(listaVertices[0]) rangoVertices = len(listaVertices) for x in range(0, rangoVertices - 2): vertice = listaVertices[x] self.rubberMarca.addPoint(QgsPointXY(vertice.x(), vertice.y()), True) self.rubberMarca.show( ) #Aqui pintamos de amarillito la geometria en division self.dlg.btnFusionar.setEnabled(False) self.dlg.btnCancelarSub.setEnabled(True) self.dlg.btnCargarPredio.setEnabled(False) self.dlg.comboPredios.setEnabled(False) self.dlg.comboPredios.setEnabled(False) self.dlg.btnConfirmarCortes.setEnabled(True) self.dlg.btnDeshacerTodo.setEnabled(True) self.dlg.btnDeshacerCortes.setEnabled(True) self.dlg.btnLlamarCalcular.setEnabled(True) else: self.UTI.mostrarAlerta('El predio no fue encontrado', QMessageBox().Critical, 'Error de cargado de predio') #------------------------------------------------------------------------- def cancelarSubdivision(self): mensaje = "El modo de division se apagara, el progreso se perdera\nDeseas continuar?" respuesta = QMessageBox.question(iface.mainWindow(), "Cancelar subdivision", mensaje, QMessageBox.Yes, QMessageBox.No) #Si el usuario acepta.... if respuesta == QMessageBox.Yes: self.vaciarRubbers() self.predioEnDivision = None self.geomEnDivision = None self.modoDividir = False self.modoEliminar = False self.apagarHerramientas() self.dlg.btnCancelarSub.setEnabled(False) self.dlg.btnCargarPredio.setEnabled(True) self.dlg.comboPredios.setEnabled(True) self.dlg.btnFusionar.setEnabled(True) self.dlg.btnConfirmarCortes.setEnabled(False) self.dlg.btnDeshacerCortes.setEnabled(False) self.dlg.btnLlamarCalcular.setEnabled(False) self.dlg.btnApagarHerramientas.setEnabled(False) self.dlg.btnEliminarCortes.setEnabled(False) self.dlg.btnDibujarCortes.setEnabled(False) self.dlg.btnEditarCortes.setEnabled(False) self.dlg.btnDeshacerTodo.setEnabled(False) #----------------------------------------------------------------------------------- #-------------------------------------------- def confirmarCortes(self): #Aqui cehcamos que los cortes esten en orden #cuentaCortes = 0 rango = len(self.eventos.relaciones) - 1 #for i in range(0, rango): # geom = self.eventos.relaciones[i].geom # if geom != None: # cuentaCortes += 1 #Obtenemos la cantidad de corte #print('cuentaCortes ', cuentaCortes) geoTemp = QgsGeometry.fromWkt(self.geomEnDivision.asWkt()) cuentaSalida = self.subdividirPredio( geoTemp, True ) - 1 #Aqui enviamos una geomtria temporal, para ven en cuantos cortes quedara if cuentaSalida >= 2: listaNoTocar = [] capaPredios = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.geom')) capaCondH = QgsProject.instance().mapLayersByName( 'horizontales.geom')[0] capaCondV = QgsProject.instance().mapLayersByName('verticales')[0] capaConstru = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('construcciones')) #Obtenemos los features que no deben tocarse for feat in capaConstru.getFeatures(): geom = feat.geometry().buffer(-0.0000002, 1) if geom.intersects(self.geomEnDivision): listaNoTocar.append(geom) for feat in capaCondH.getFeatures(): geom = feat.geometry().buffer(-0.0000002, 1) if geom.intersects(self.geomEnDivision): listaNoTocar.append(geom) for feat in capaCondV.getFeatures(): geom = feat.geometry().buffer(-0.0000002, 1) if geom.intersects(self.geomEnDivision): listaNoTocar.append(geom) bandera = True #Aqui checamos que cada linea no toque lo no tocable for i in range(0, rango): for comp in listaNoTocar: geom = self.eventos.relaciones[i].geom if geom != None: if geom.buffer(0.0000001, 1).intersects(comp): bandera = False self.eventos.relaciones[i].rubber.setStrokeColor( QColor(255, 0, 0, 255)) else: self.eventos.relaciones[i].rubber.setStrokeColor( QColor(0, 61, 240, 255)) iface.mapCanvas().refresh() if bandera: #Si todo esta en orden self.eventos.rubberPunto.reset(QgsWkbTypes.PointGeometry) self.apagarHerramientas() #mostramos mensaje de confirmacion mensaje = "La cantidad de predios que quedaran como resultado, es de: " + str( cuentaSalida) + "\nDeseas continua?" respuesta = QMessageBox.question( iface.mainWindow(), "Nota de subdivision de predio", mensaje, QMessageBox.Yes, QMessageBox.No) #Si el usuario acepta.... if respuesta == QMessageBox.Yes: self.subdividirPredio(self.geomEnDivision, False) self.rubberMarca.reset( QgsWkbTypes.PolygonGeometry) #Quitamos lo amarillito for i in range(0, rango): self.eventos.relaciones[i].rubber.reset( QgsWkbTypes.LineGeometry) self.eventos.relaciones[i].vaciarMarcadores() self.eventos.recargarRelaciones() capaPredios.startEditing() capaPredios.dataProvider().deleteFeatures( [self.predioEnDivision.id()]) capaPredios.triggerRepaint() capaPredios.commitChanges() self.VentanaAreas.close() self.limpiarAreas() self.vaciarRubbers() self.UTI.mostrarAlerta( "La division ha sido realizada con exito\nLos cambios se guardaran hasta que asignes las claves.", QMessageBox().Information, "Subdivision completa, Parte 1 de 2") self.irAClaves( ) #Mostramos la ventana que llena las claves iface.actionSelect().trigger() else: self.UTI.mostrarAlerta( "Las lineas de division no deben atravesar construcciones ni condominios\nLas lineas rojas presentan un corte invalido", QMessageBox().Critical, "Error en subdivision") else: print('cuentasalida ', cuentaSalida) self.UTI.mostrarAlerta( "Primero debes dibujar las lineas de corte\nAsegurate que las lineas atraviesen por completo el predio", QMessageBox().Critical, "Error en subdivision") ################################################################################## def vaciarLineasCorte(self): rango = len(self.eventos.relaciones) - 1 for i in range(0, rango): self.eventos.relaciones[i].rubber.reset(QgsWkbTypes.LineGeometry) self.eventos.relaciones[i].vaciarMarcadores() self.eventos.recargarRelaciones() #------------------------------------------------------------------------------------- def subdividirPredio(self, geometria, modoPre): #Subdividir capaHori = QgsProject.instance().mapLayersByName( 'horizontales.geom')[0] capaPredio = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.geom')) capaVert = QgsProject.instance().mapLayersByName('verticales')[0] self.listaNuevosPredios = [] self.geomsAreas = [] listaParam = [] #La lista que llega al fileteo rango = len(self.eventos.relaciones) - 1 for i in range(0, rango): geom = self.eventos.relaciones[i].geom if geom != None: listaParam.append( geom.asPolyline()) #Generamos los polyline van a partir salida = self.filetear( geometria, listaParam) #Mandamos filetear el poligono con las lineas if modoPre: #Si es modoPre, es solo para visaulizar cuantos poligonos quedarian... for cadaUno in salida: self.geomsAreas.append(cadaUno) return len(salida) else: #Si no, efectuamos realmente el corte for cadaUno in salida: nuevoFeat = QgsFeature() nuevoFeat.setGeometry(cadaUno) #Agregamos cada predio a la capa y le ponemos sus atributos self.asignarAtributos(nuevoFeat) capaPredio.startEditing() capaPredio.dataProvider().addFeatures([nuevoFeat]) capaPredio.triggerRepaint() capaPredio.commitChanges() self.listaNuevosPredios.append(nuevoFeat.geometry().asWkt()) self.apagarHerramientas() self.dlg.btnCargarPredio.setEnabled(True) ################################################################################## #Metodo para filetear los predios con lineas #OJO: Ni se te ocurra moverle aqui karnal, seguramente la vas a pifiar :) def filetear(self, poligono, lineas): listaActuales = [] #Lista de las geometrias en espera de ser cortadas listaActuales.append( poligono) #Agregamos el poligono a cortar en la lista de espera listaSiguiente = [ poligono ] #Agregamos le poligono a cortar en la lista de la siguiente interacion for linea in lineas: #Iteramos cada linea de corte listaActuales = listaSiguiente #Igualamos la lista de corte a la iteracion anterior listaSiguiente = [ ] #Vaciamos la lista que dara lugar a la siguiente iteracion de corte for actual in listaActuales: #Checamos cada geometria en espera partida = actual.splitGeometry( linea, True ) #Aplicamos el split geometry de qgis, vamos partiendo el poligono, tut, tut, tut if len(partida[1] ) == 0: #Esto ocurre cuando el corte no hizo nada listaSiguiente.append( actual ) #Asi que nos quedamos con la geometria tal cual esta else: #Cuando si hubo corte parte1 = partida[1][ 0] #obtenemos la geometria resultante del corte, VER DOCUMENTACION DE SPLITGEOMETRY PARA ENTENDER parte2 = actual.difference( parte1 ) #Al poligono que se corto, le quitamos la parte previa al corte anterior #De esta forma nos quedamos con la geometria partida en 2 if parte1.area( ) > 0.0: #Checamos que el area de esta parte sea mayor que 0, por motivos de los decimales listaSiguiente.append( parte1 ) #Si es asi, significa un fragmento que puede ser cortado por otra linea if parte2.area() > 0.0: #Lo mismo listaSiguiente.append(parte2) temporal = poligono #Obtenemos una geometria igualita al poligono entrannte for geomSal in listaSiguiente: #Obtenemos las diferencia respeto a los poligonos obtenidos y el entrante, lo que resulte, tambien es parte del corte temporal = temporal.difference(geomSal.buffer(0.0000001, 1)) #El buffer tu sabes por que es, si llegaste hasta aqui y no sabes por que le ponemos buffer, mejor quita los cambios que hiciste colega, seguramente la pifiaste :) listaSiguiente.append(temporal) #Aqui ponemos todas las geometrias return listaSiguiente #Retornamos la lista #------------------------------------------------------------------------- def encenderModoDividir(self): #Activamos el modo de dividir self.eventos.modoDividir = True self.eventos.modoEliminar = False self.eventos.modoEditar = False self.dlg.btnDibujarCortes.setEnabled(False) self.dlg.btnEditarCortes.setEnabled(True) self.dlg.btnEliminarCortes.setEnabled(True) self.dlg.btnApagarHerramientas.setEnabled(True) self.dlg.btnConfirmarCortes.setEnabled(True) self.dlg.btnDeshacerTodo.setEnabled(True) self.dlg.btnCargarPredio.setEnabled(False) iface.mapCanvas().setCursor(self.DBJ.eventos.cursorRedondo) #---------------------------------------------------------------------------- def encenderModoEditar(self): #Activamos el modo de dividir self.eventos.modoDividir = False self.eventos.modoEliminar = False self.eventos.modoEditar = True self.dlg.btnDibujarCortes.setEnabled(True) self.dlg.btnEditarCortes.setEnabled(False) self.dlg.btnEliminarCortes.setEnabled(True) self.dlg.btnApagarHerramientas.setEnabled(True) self.dlg.btnConfirmarCortes.setEnabled(True) self.dlg.btnDeshacerTodo.setEnabled(True) self.dlg.btnCargarPredio.setEnabled(False) iface.mapCanvas().setCursor(self.DBJ.eventos.cursorRedondo) #---------------------------------------------------------------------------- def encenderModoEliminar(self): #Activamos el modo de eliminar self.eventos.modoDividir = False self.eventos.modoEliminar = True self.eventos.modoEditar = False self.dlg.btnDibujarCortes.setEnabled(True) self.dlg.btnEditarCortes.setEnabled(True) self.dlg.btnEliminarCortes.setEnabled(False) self.dlg.btnApagarHerramientas.setEnabled(True) self.dlg.btnConfirmarCortes.setEnabled(True) self.dlg.btnCargarPredio.setEnabled(False) iface.mapCanvas().setCursor(self.DBJ.eventos.cursorCuadro) #---------------------------------------------------------------------------- def apagarHerramientas(self): #Apagamos las herramientas self.eventos.modoDividir = False self.eventos.modoEliminar = False self.eventos.modoEditar = False self.dlg.btnDibujarCortes.setEnabled(True) self.dlg.btnEliminarCortes.setEnabled(True) self.dlg.btnEditarCortes.setEnabled(True) self.dlg.btnApagarHerramientas.setEnabled(False) self.dlg.btnConfirmarCortes.setEnabled(True) self.dlg.btnCargarPredio.setEnabled(False) iface.mapCanvas().setCursor(self.DBJ.eventos.cursorCruz) #----------------------------------------------------------------- def rollBack(self): #Deshacemos cambios mensaje = "Todos los cambio se perderan, el area de trabajo se limpiara... deseas continuar?" respuesta = QMessageBox.question(iface.mainWindow(), "Cancelar cambios", mensaje, QMessageBox.Yes, QMessageBox.No) #Si el usuario acepta.... if respuesta == QMessageBox.Yes: self.eventos.modoDividir = False self.eventos.modoEliminar = False self.dlg.close() self.VentanaClaves.dlg.close() self.vaciarRubbers() self.predioEnDivision = None self.geomEnDivision = None self.VentanaAreas.close() self.UTI.limpiarCanvas() #------------------------------------------------------------ def vaciarRubbers(self): rango = len(self.eventos.relaciones) - 1 for i in range(0, rango): self.eventos.relaciones[i].rubber.reset(QgsWkbTypes.LineGeometry) #self.eventos.relaciones[i].geom = None self.eventos.relaciones[i].vaciarMarcadores() self.eventos.recargarRelaciones() self.rubberMarca.reset(QgsWkbTypes.PolygonGeometry) #self.eventos.vaciarMarcadores() #----------------------------------------------------------------- def asignarAtributos(self, feat): #Asignamos los atributos capaPredios = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('predios.geom')) campos = capaPredios.fields() nombres = [campo.name() for campo in campos] listaAsignacion = [] for nombre in nombres: idx = capaPredios.fields().lookupField(nombre) if nombre == 'sup_terr': attr = feat.geometry().area() elif nombre == 'sup_contruccion': attr = self.calcularSupConst(feat) elif nombre == 'clave': attr = '' elif nombre == 'cve_cat': attr = '' elif nombre == 'id': attr = '' else: attr = self.predioEnDivision.attributes()[idx] listaAsignacion.append(attr) capaPredios.startEditing() feat.setAttributes(listaAsignacion) capaPredios.triggerRepaint() capaPredios.commitChanges() #------------------------------------------------------------ def calcularSupConst(self, feat): #calculamos superficie de construccion capaConstru = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('construcciones')) capaCondH = QgsProject.instance().mapLayer( self.ACA.obtenerIdCapa('horizontales.geom')) listaFinal = [] listaPrimera = [] #Renombrar construcciones cuentaConstruccion = 1 capaConstru.startEditing() geomFeat = feat.geometry() #--------Renombrar construcciones---- for construccion in capaConstru.getFeatures(): geomConstru = construccion.geometry() if geomConstru.buffer(-0.0000001, 1).intersects( geomFeat ): #Sacamos las construcciones dentro del predio fusionado listaPrimera.append(construccion) else: if self.contarIntegraciones( geomConstru.buffer(-0.0000001, 1), 'predios.geom' ) == 0: #Obtenemos los aleros del predio fusionado if geomConstru.buffer( 0.0000001, 1).intersection(geomFeat).area() > 0.000000001: #print(geomConstru.buffer(0.0000001, 1).intersection(geomFusion).area()) listaPrimera.append(construccion) for construccion in listaPrimera: geomConstru = construccion.geometry() seIncluye = True for condominio in capaCondH.getFeatures(): geomHori = condominio.geometry() if geomConstru.buffer(-0.0000001, 1).intersects(geomHori): seIncluye = False break if seIncluye: listaFinal.append(construccion) supConst = 0 #Renombramos ls volumenes for construccion in listaFinal: geomConstru = construccion.geometry() construccion['nom_volumen'] = 'V' + str(cuentaConstruccion) niveles = construccion['num_niveles'] if niveles <= 0: niveles = 1 capaConstru.updateFeature(construccion) cuentaConstruccion += 1 supConst += (geomConstru.area() * niveles) return supConst #-------------------------------------------------------------------------------------------------- def irAClaves(self): #Pintamos la ventanita de asignar clavees self.VentanaClaves.rellenarClaves() self.VentanaClaves.llenar(False) self.VentanaClaves.dlg.show() #self.dlg.close() self.dlg.btnDibujarCortes.setEnabled(False) self.dlg.btnEditarCortes.setEnabled(False) self.dlg.btnEliminarCortes.setEnabled(False) self.dlg.btnApagarHerramientas.setEnabled(False) self.dlg.btnConfirmarCortes.setEnabled(False) self.dlg.btnCargarPredio.setEnabled(False) self.dlg.btnDeshacerCortes.setEnabled(False) self.dlg.btnLlamarCalcular.setEnabled(False) self.dlg.btnCancelarSub.setEnabled(False) self.dlg.comboPredios.setEnabled(False) self.enClaves = True #------------------------------------------------------------------------- def irAreas(self): self.limpiarAreas() self.rubberMarca.reset(QgsWkbTypes.PolygonGeometry) geoTemp = QgsGeometry.fromWkt(self.geomEnDivision.asWkt()) self.subdividirPredio(geoTemp, True) self.listaColores = [] for geom in self.geomsAreas: if geom.area() <= 0: continue newRub = self.eventos.crearNuevoRubberPoly() n = 0 #Obtenemos todos los vertices de la geometria en division ver = geom.vertexAt(0) listaVertices = [] while (ver != QgsPoint(0, 0)): n += 1 ver = geom.vertexAt(n) listaVertices.append(ver) listaVertices.append(listaVertices[0]) rangoVertices = len(listaVertices) for x in range(0, rangoVertices - 2): vertice = listaVertices[x] newRub.addPoint(QgsPointXY(vertice.x(), vertice.y()), True) newRub.show() self.rubbersAreas.append(newRub) self.VentanaAreas.mostrarAreas() #for geom in self.geomsAreas: #-------------------------------------------------------------------- def limpiarAreas(self): for rub in self.rubbersAreas: rub.reset(QgsWkbTypes.PolygonGeometry) self.rubbersAreas = [] #------------------------------------------------------------------ def quitarAreas(self): self.limpiarAreas() n = 0 #Obtenemos todos los vertices de la geometria en division if self.geomEnDivision == None: return ver = self.geomEnDivision.vertexAt(0) listaVertices = [] while (ver != QgsPoint(0, 0)): n += 1 ver = self.geomEnDivision.vertexAt(n) listaVertices.append(ver) listaVertices.append(listaVertices[0]) rangoVertices = len(listaVertices) for x in range(0, rangoVertices - 2): vertice = listaVertices[x] self.rubberMarca.addPoint(QgsPointXY(vertice.x(), vertice.y()), True) self.rubberMarca.show( ) #Aqui pintamos de amarillito la geometria en division #-------------------------------------------------------------------------- def closeEvent(self, evnt): if self.enClaves: evnt.ignore()
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 Qgis2threejsDialog(QDialog): STYLE_MAX_COUNT = 4 def __init__(self, iface, properties=None): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.templateType = None self.currentItem = None self.currentPage = None topItemCount = len(ObjectTreeItem.topItemNames) if properties is None: self.properties = [None] * topItemCount for i in range(ObjectTreeItem.ITEM_OPTDEM, topItemCount): self.properties[i] = {} else: self.properties = properties # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") ui.progressBar.setVisible(False) ui.label_MessageIcon.setVisible(False) ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) # set up map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) #self.mapTool = PointMapTool(iface.mapCanvas()) # set up the template combo box self.initTemplateList() self.ui.comboBox_Template.currentIndexChanged.connect(self.currentTemplateChanged) # set up the properties pages self.pages = {} self.pages[ppages.PAGE_WORLD] = ppages.WorldPropertyPage(self) self.pages[ppages.PAGE_CONTROLS] = ppages.ControlsPropertyPage(self) self.pages[ppages.PAGE_DEM] = ppages.DEMPropertyPage(self) self.pages[ppages.PAGE_VECTOR] = ppages.VectorPropertyPage(self) container = ui.propertyPagesContainer for page in self.pages.itervalues(): page.hide() container.addWidget(page) # build object tree self.topItemPages = {ObjectTreeItem.ITEM_WORLD: ppages.PAGE_WORLD, ObjectTreeItem.ITEM_CONTROLS: ppages.PAGE_CONTROLS, ObjectTreeItem.ITEM_DEM: ppages.PAGE_DEM} self.initObjectTree() self.ui.treeWidget.currentItemChanged.connect(self.currentObjectChanged) self.ui.treeWidget.itemChanged.connect(self.objectItemChanged) self.currentTemplateChanged() # update item visibility ui.toolButton_Browse.clicked.connect(self.browseClicked) #iface.mapCanvas().mapToolSet.connect(self.mapToolSet) # to show button to enable own map tool self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.objectTypeManager = ObjectTypeManager() def showMessageBar(self, text, level=QgsMessageBar.INFO): # from src/gui/qgsmessagebaritem.cpp if level == QgsMessageBar.CRITICAL: msgIcon = "/mIconCritical.png" bgColor = "#d65253" elif level == QgsMessageBar.WARNING: msgIcon = "/mIconWarn.png" bgColor = "#ffc800" else: msgIcon = "/mIconInfo.png" bgColor = "#e7f5fe" stylesheet = "QLabel {{ background-color:{0}; }}".format(bgColor) label = self.ui.label_MessageIcon label.setPixmap(QgsApplication.getThemeIcon(msgIcon).pixmap(24)) label.setStyleSheet(stylesheet) label.setVisible(True) label = self.ui.label_Status label.setText(text) label.setStyleSheet(stylesheet) def clearMessageBar(self): self.ui.label_MessageIcon.setVisible(False) self.ui.label_Status.setText("") self.ui.label_Status.setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 0); }") def initTemplateList(self): cbox = self.ui.comboBox_Template cbox.clear() templateDir = QDir(tools.templateDir()) for i, entry in enumerate(templateDir.entryList(["*.html", "*.htm"])): cbox.addItem(entry) config = tools.getTemplateConfig(templateDir.filePath(entry)) # get template type templateType = config.get("type", "plain") cbox.setItemData(i, templateType, Qt.UserRole) # set tool tip text desc = config.get("description", "") if desc: cbox.setItemData(i, desc, Qt.ToolTipRole) # select the last used template templateName = QSettings().value("/Qgis2threejs/lastTemplate", "", type=unicode) if templateName: index = cbox.findText(templateName) if index != -1: cbox.setCurrentIndex(index) return index return -1 def initObjectTree(self): tree = self.ui.treeWidget tree.clear() # add vector and raster layers into tree widget topItems = [] for index, itemName in enumerate(ObjectTreeItem.topItemNames): item = QTreeWidgetItem(tree, [itemName]) item.setData(0, Qt.UserRole, index) topItems.append(item) optDEMChecked = False for layer in self.iface.legendInterface().layers(): layerType = layer.type() if layerType not in (QgsMapLayer.VectorLayer, QgsMapLayer.RasterLayer): continue parentId = None if layerType == QgsMapLayer.VectorLayer: geometry_type = layer.geometryType() if geometry_type in [QGis.Point, QGis.Line, QGis.Polygon]: parentId = ObjectTreeItem.ITEM_POINT + geometry_type # - QGis.Point elif layerType == QgsMapLayer.RasterLayer and layer.providerType() == "gdal" and layer.bandCount() == 1: parentId = ObjectTreeItem.ITEM_OPTDEM if parentId is None: continue item = QTreeWidgetItem(topItems[parentId], [layer.name()]) isVisible = self.properties[parentId].get(layer.id(), {}).get("visible", False) #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) if parentId == ObjectTreeItem.ITEM_OPTDEM and isVisible: optDEMChecked = True for item in topItems: if item.data(0, Qt.UserRole) != ObjectTreeItem.ITEM_OPTDEM or optDEMChecked: tree.expandItem(item) def saveProperties(self, item, page): properties = page.properties() parent = item.parent() if parent is None: # top item: properties[topItemIndex] self.properties[item.data(0, Qt.UserRole)] = properties else: # layer item: properties[topItemIndex][layerId] topItemIndex = parent.data(0, Qt.UserRole) self.properties[topItemIndex][item.data(0, Qt.UserRole)] = properties if debug_mode: qDebug(str(self.properties)) def currentTemplateChanged(self, index=None): cbox = self.ui.comboBox_Template templateType = cbox.itemData(cbox.currentIndex(), Qt.UserRole) if templateType == self.templateType: return tree = self.ui.treeWidget for i, name in enumerate(ObjectTreeItem.topItemNames): hidden = (templateType == "sphere" and name != "Controls") tree.topLevelItem(i).setHidden(hidden) itemToBeSelected = ObjectTreeItem.ITEM_CONTROLS if templateType == "sphere" else ObjectTreeItem.ITEM_DEM tree.setCurrentItem(tree.topLevelItem(itemToBeSelected)) self.clearMessageBar() if templateType != "sphere": # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings() if QGis.QGIS_VERSION_INT >= 20300 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees, so terrain will not appear well.", QgsMessageBar.WARNING) self.templateType = templateType def currentObjectChanged(self, currentItem, previousItem): # save properties of previous item if previousItem and self.currentPage: self.saveProperties(previousItem, self.currentPage) self.currentItem = currentItem self.currentPage = None # hide all pages for page in self.pages.itervalues(): page.hide() parent = currentItem.parent() if parent is None: topItemIndex = currentItem.data(0, Qt.UserRole) pageType = self.topItemPages.get(topItemIndex, ppages.PAGE_NONE) page = self.pages.get(pageType, None) if page is None: return page.setup(self.properties[topItemIndex]) page.show() else: parentId = parent.data(0, Qt.UserRole) layerId = currentItem.data(0, Qt.UserRole) if layerId is None: return layer = QgsMapLayerRegistry.instance().mapLayer(layerId) if layer is None: return layerType = layer.type() if layerType == QgsMapLayer.RasterLayer: page = self.pages[ppages.PAGE_DEM] page.setup(self.properties[parentId].get(layerId, None), layer, False) elif layerType == QgsMapLayer.VectorLayer: page = self.pages[ppages.PAGE_VECTOR] page.setup(self.properties[parentId].get(layerId, None), layer) else: return page.show() self.currentPage = page def objectItemChanged(self, item, column): parent = item.parent() if parent is None: return if item == self.currentItem: if self.currentPage: # update enablement of property widgets self.currentPage.itemChanged(item) else: # select changed item self.ui.treeWidget.setCurrentItem(item) # set visible property #visible = item.data(0, Qt.CheckStateRole) == Qt.Checked #parentId = parent.data(0, Qt.UserRole) #layerId = item.data(0, Qt.UserRole) #self.properties[parentId].get(layerId, {})["visible"] = visible def primaryDEMChanged(self, layerId): tree = self.ui.treeWidget parent = tree.topLevelItem(ObjectTreeItem.ITEM_OPTDEM) tree.blockSignals(True) for i in range(parent.childCount()): item = parent.child(i) isPrimary = item.data(0, Qt.UserRole) == layerId item.setDisabled(isPrimary) tree.blockSignals(False) def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def progress(self, percentage, statusMsg=None): ui = self.ui ui.progressBar.setValue(percentage) if percentage == 100: ui.progressBar.setVisible(False) ui.label_Status.setText("") else: ui.progressBar.setVisible(True) if statusMsg is not None: ui.label_Status.setText(statusMsg) ui.label_Status.repaint() def run(self): ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename != "" and QFileInfo(filename).exists() and QMessageBox.question(None, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return self.endPointSelection() # save properties of current object item = self.ui.treeWidget.currentItem() if item and self.currentPage: self.saveProperties(item, self.currentPage) ui.pushButton_Run.setEnabled(False) self.clearMessageBar() self.progress(0) canvas = self.iface.mapCanvas() cbox = self.ui.comboBox_Template templateName = cbox.currentText() templateType = cbox.itemData(cbox.currentIndex(), Qt.UserRole) htmlfilename = ui.lineEdit_OutputFilename.text() # world properties world = self.properties[ObjectTreeItem.ITEM_WORLD] or {} verticalExaggeration = world.get("lineEdit_zFactor", 1.5) verticalShift = world.get("lineEdit_zShift", 0) # export to javascript (three.js) mapTo3d = MapTo3D(canvas, verticalExaggeration=float(verticalExaggeration), verticalShift=float(verticalShift)) context = OutputContext(templateName, templateType, mapTo3d, canvas, self.properties, self, self.objectTypeManager, self.localBrowsingMode) htmlfilename = exportToThreeJS(htmlfilename, context, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if htmlfilename is None: return self.clearRubberBands() # store last selections settings = QSettings() settings.setValue("/Qgis2threejs/lastTemplate", templateName) settings.setValue("/Qgis2threejs/lastControls", context.controls) # open browser if not tools.openHTMLFile(htmlfilename): return QDialog.accept(self) def reject(self): # save properties of current object item = self.ui.treeWidget.currentItem() if item and self.currentPage: self.saveProperties(item, self.currentPage) self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def startPointSelection(self): canvas = self.iface.mapCanvas() if self.previousMapTool != self.mapTool: self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.pages[ppages.PAGE_DEM].toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() if self.previousMapTool is not None: self.iface.mapCanvas().setMapTool(self.previousMapTool) def mapToolSet(self, mapTool): return #TODO: unstable if mapTool != self.mapTool and self.currentPage is not None: if self.currentPage.pageType == ppages.PAGE_DEM and self.currentPage.isPrimary: self.currentPage.toolButton_PointTool.setVisible(True) def createRubberBands(self, quads, point=None): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) for quad in quads: points = [] extent = quad.extent points.append(QgsPoint(extent.xMinimum(), extent.yMinimum())) points.append(QgsPoint(extent.xMinimum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMinimum())) self.rb_quads.addGeometry(QgsGeometry.fromPolygon([points]), None) self.log(extent.toString()) self.log("Quad count: %d" % len(quads)) # create a point with rubber band if point: self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(point) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = os.path.split(self.ui.lineEdit_OutputFilename.text())[0] if directory == "": directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if filename != "": self.ui.lineEdit_OutputFilename.setText(filename) def log(self, msg): if debug_mode: qDebug(msg)
class VoGISProfilToolMainDialog(QDialog): def __init__(self, interface, settings): self.settings = settings self.iface = interface self.selectingVisibleRasters = False self.thread = None QDialog.__init__(self, interface.mainWindow()) # Set up the user interface from Designer. self.ui = Ui_VoGISProfilToolMain() self.ui.setupUi(self) if self.settings.onlyHektoMode is True: self.ui.IDC_widRaster.hide() self.adjustSize() validator = QIntValidator(-32768, 32768, self) self.ui.IDC_tbNoDataExport.setValidator(validator) self.ui.IDC_tbFromX.setText('-30000') self.ui.IDC_tbFromY.setText('240000') self.ui.IDC_tbToX.setText('-20000') self.ui.IDC_tbToY.setText('230000') self.__addRastersToGui() self.__addPolygonsToGui() for line_lyr in self.settings.mapData.lines.lines(): self.ui.IDC_cbLineLayers.addItem(line_lyr.name, line_lyr) if self.settings.mapData.lines.count() < 1: self.ui.IDC_rbDigi.setChecked(True) self.ui.IDC_rbShapeLine.setEnabled(False) #Einstellungen fuer Linie zeichen self.action = QAction(QIcon(":/plugins/vogisprofiltoolmain/icons/icon.png"), "VoGIS-Profiltool", self.iface.mainWindow()) self.action.setWhatsThis("VoGIS-Profiltool") self.canvas = self.iface.mapCanvas() self.tool = ProfiletoolMapTool(self.canvas, self.action) self.savedTool = self.canvas.mapTool() self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) if QGis.QGIS_VERSION_INT >= 10900: #self.rubberband.setBrushStyle() self.rubberband.setLineStyle(Qt.SolidLine) self.rubberband.setWidth(4.0) self.rubberband.setColor(QColor(0, 255, 0)) #http://www.qgis.org/api/classQgsRubberBand.html#a6f7cdabfcf69b65dfc6c164ce2d01fab self.pointsToDraw = [] self.dblclktemp = None self.drawnLine = None def accept(self): try: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "ACCEPTED") #QgsMessageLog.logMessage('nodata: {0}'.format(self.settings.nodata_value), 'VoGis') self.settings.nodata_value = int(self.ui.IDC_tbNoDataExport.text()) QgsMessageLog.logMessage('maindlg: nodata: {0}'.format(self.settings.nodata_value), 'VoGis') if self.settings.onlyHektoMode is True and self.settings.mapData.rasters.count() > 0: self.settings.onlyHektoMode = False if self.settings.onlyHektoMode is False: if self.settings.mapData.rasters.count() < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", u"Keine Raster vorhanden. Zum Hektometrieren Dialog neu öffnen.") #return retVal = QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Rasterebene vorhanden oder sichtbar! Nur hektometrieren?', None, QApplication.UnicodeUTF8), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if retVal == QMessageBox.No: return else: self.settings.onlyHektoMode = True self.settings.createHekto = True if self.__getSettingsFromGui() is False: return if self.settings.onlyHektoMode is False: if len(self.settings.mapData.rasters.selectedRasters()) < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "Kein Raster selektiert!") #msg = #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", msg) QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Kein Raster selektiert!', None, QApplication.UnicodeUTF8)) return QgsMessageLog.logMessage('modeLine!=line: {0}'.format(self.settings.modeLine != enumModeLine.line), 'VoGis') QgsMessageLog.logMessage('customLine is None: {0}'.format(self.settings.mapData.customLine is None), 'VoGis') if self.settings.modeLine != enumModeLine.line and self.settings.mapData.customLine is None: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Profillinie vorhanden!', None, QApplication.UnicodeUTF8)) return if len(self.settings.mapData.polygons.selected_polygons()) > 0 and len(self.settings.mapData.rasters.selectedRasters()) > 1: raster_names = list(raster.name for raster in self.settings.mapData.rasters.selectedRasters()) sel_raster, ok_clicked = QInputDialog.getItem( self.iface.mainWindow(), u'DHM?', u'Welches DHM soll zur Flächenverschneidung verwendet werden?', raster_names, 0, False ) if ok_clicked is False: return self.settings.intersection_dhm_idx = raster_names.index(sel_raster) #self.rubberband.reset(self.polygon) #QDialog.accept(self) QApplication.setOverrideCursor(Qt.WaitCursor) create_profile = CreateProfile(self.iface, self.settings) thread = QThread(self) create_profile.moveToThread(thread) create_profile.finished.connect(self.profiles_finished) create_profile.error.connect(self.profiles_error) create_profile.progress.connect(self.profiles_progress) thread.started.connect(create_profile.create) thread.start(QThread.LowestPriority) self.thread = thread self.create_profile = create_profile self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) except: QApplication.restoreOverrideCursor() ex = u'{0}'.format(traceback.format_exc()) msg = 'Unexpected ERROR:\n\n{0}'.format(ex[:2000]) QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", msg) def profiles_finished(self, profiles, intersections): QApplication.restoreOverrideCursor() self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) #self.create_profile.deleteLater() self.thread.quit() self.thread.wait() #self.thread.deleteLater() #QGIS 2.0 http://gis.stackexchange.com/a/58754 http://gis.stackexchange.com/a/57090 self.iface.mainWindow().statusBar().showMessage('VoGIS-Profiltool, {0} Profile'.format(len(profiles))) QgsMessageLog.logMessage(u'Profile Count: {0}'.format(len(profiles)), 'VoGis') if len(profiles) < 1: QApplication.restoreOverrideCursor() QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Es konnten keine Profile erstellt werden.', None, QApplication.UnicodeUTF8)) return dlg = VoGISProfilToolPlotDialog(self.iface, self.settings, profiles, intersections) dlg.show() #result = self.dlg.exec_() dlg.exec_() def profiles_error(self, exception_string): QApplication.restoreOverrideCursor() QgsMessageLog.logMessage(u'Error during profile creation: {0}'.format(exception_string), 'VoGis') QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", exception_string) def profiles_progress(self, msg): self.iface.mainWindow().statusBar().showMessage(msg) self.ui.IDC_lblCreateStatus.setText(msg) #QgsMessageLog.logMessage(msg, 'VoGis') QApplication.processEvents() def reject(self): if not self.thread is None: if self.thread.isRunning(): self.create_profile.abort() return self.rubberband.reset(self.polygon) QDialog.reject(self) def selectVisibleRasters(self): self.refreshRasterList() self.selectingVisibleRasters = True extCanvas = self.iface.mapCanvas().extent() #alle raster in den einstellunge deselektieren for r in self.settings.mapData.rasters.rasters(): r.selected = False #alle raster in der ListView deselektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) item.setCheckState(Qt.Unchecked) #Raster im Extent selektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) if QGis.QGIS_VERSION_INT < 10900: raster = item.data(Qt.UserRole).toPyObject() else: raster = item.data(Qt.UserRole) for r in self.settings.mapData.rasters.rasters(): if extCanvas.intersects(r.grid.extent()): if r.id == raster.id: r.selected = True item.setCheckState(Qt.Checked) self.selectingVisibleRasters = False def lineLayerChanged(self, idx): if self.ui.IDC_rbShapeLine.isChecked() is False: self.ui.IDC_rbShapeLine.setChecked(True) if QGis.QGIS_VERSION_INT < 10900: lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex()).toPyObject()) else: lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) lyr = lineLyr.line #QgsMessageLog.logMessage('{0}'.format(lyr.selectedFeatureCount()), 'VoGis') #QgsMessageLog.logMessage('{0}'.format(dir(lyr)), 'VoGis') if hasattr(lyr, 'selectedFeatureCount'): if(lyr.selectedFeatureCount() < 1): self.ui.IDC_chkOnlySelectedFeatures.setChecked(False) else: self.ui.IDC_chkOnlySelectedFeatures.setChecked(True) def valueChangedEquiDistance(self, val): if self.ui.IDC_rbEquiDistance.isChecked() is False: self.ui.IDC_rbEquiDistance.setChecked(True) def valueChangedVertexCount(self, val): if self.ui.IDC_rbVertexCount.isChecked() is False: self.ui.IDC_rbVertexCount.setChecked(True) def lvRasterItemChanged(self, item): if self.selectingVisibleRasters is True: return if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: raster_lyr = item_data.toPyObject() else: raster_lyr = item_data self.settings.mapData.rasters.getById(raster_lyr.id).selected = selected def lvPolygonItemChanged(self, item): if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: poly_lyr = item_data.toPyObject() else: poly_lyr = item_data self.settings.mapData.polygons.getById(poly_lyr.id).selected = selected def refreshRasterList(self): legend = self.iface.legendInterface() avail_lyrs = legend.layers() raster_coll = RasterCollection() for lyr in avail_lyrs: if legend.isLayerVisible(lyr): lyr_type = lyr.type() lyr_name = unicodedata.normalize('NFKD', unicode(lyr.name())).encode('ascii', 'ignore') if lyr_type == 1: if lyr.bandCount() < 2: new_raster = Raster(lyr.id(), lyr_name, lyr) raster_coll.addRaster(new_raster) self.settings.mapData.rasters = raster_coll self.__addRastersToGui() def __addRastersToGui(self): self.ui.IDC_listRasters.clear() check = Qt.Unchecked if self.settings.mapData.rasters.count() == 1: check = Qt.Checked self.settings.mapData.rasters.rasters()[0].selected = True for raster_lyr in self.settings.mapData.rasters.rasters(): item = QListWidgetItem(raster_lyr.name) item.setData(Qt.UserRole, raster_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listRasters.addItem(item) def __addPolygonsToGui(self): self.ui.IDC_listPolygons.clear() check = Qt.Unchecked for poly_lyr in self.settings.mapData.polygons.polygons(): item = QListWidgetItem(poly_lyr.name) item.setData(Qt.UserRole, poly_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listPolygons.addItem(item) def drawLine(self): if self.ui.IDC_rbDigi.isChecked() is False: self.ui.IDC_rbDigi.setChecked(True) self.dblckltemp = None self.rubberband.reset(self.polygon) self.__cleanDigi() self.__activateDigiTool() self.canvas.setMapTool(self.tool) def __createDigiFeature(self, pnts): u = Util(self.iface) f = u.createQgLineFeature(pnts) self.settings.mapData.customLine = f def __lineFinished(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) self.pointsToDraw.append(newPoint) #launch analyses self.iface.mainWindow().statusBar().showMessage(str(self.pointsToDraw)) if len(self.pointsToDraw) < 2: self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint self.drawnLine = None QMessageBox.warning(self, "VoGIS-Profiltool", QApplication.translate('code', 'Profillinie digitalisieren abgebrochen!', None, QApplication.UnicodeUTF8)) self.drawnLine = self.__createDigiFeature(self.pointsToDraw) self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint def __cleanDigi(self): self.pointsToDraw = [] self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.savedTool) def __activateDigiTool(self): QObject.connect(self.tool, SIGNAL("moved"), self.__moved) QObject.connect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.connect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.connect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) QObject.connect(self.tool, SIGNAL("deactivate"), self.__deactivateDigiTool) def __deactivateDigiTool(self): QObject.disconnect(self.tool, SIGNAL("moved"), self.__moved) QObject.disconnect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.disconnect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.disconnect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) if QGis.QGIS_VERSION_INT < 10900: self.iface.mainWindow().statusBar().showMessage(QString("")) else: self.iface.mainWindow().statusBar().showMessage('') def __moved(self, position): if len(self.pointsToDraw) > 0: mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) self.rubberband.reset(self.polygon) newPnt = QgsPoint(mapPos.x(), mapPos.y()) if QGis.QGIS_VERSION_INT < 10900: for i in range(0, len(self.pointsToDraw)): self.rubberband.addPoint(self.pointsToDraw[i]) self.rubberband.addPoint(newPnt) else: pnts = self.pointsToDraw + [newPnt] self.rubberband.setToGeometry(QgsGeometry.fromPolyline(pnts),None) def __rightClicked(self, position): self.__lineFinished(position) def __leftClicked(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) #if self.selectionmethod == 0: if newPoint == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointsToDraw) == 0: self.rubberband.reset(self.polygon) self.pointsToDraw.append(newPoint) def __doubleClicked(self, position): pass #not in use right now def __lineCancel(self): pass def __getSettingsFromGui(self): self.settings.linesExplode = (self.ui.IDC_chkLinesExplode.checkState() == Qt.Checked) self.settings.linesMerge = (self.ui.IDC_chkLinesMerge.checkState() == Qt.Checked) self.settings.onlySelectedFeatures = (self.ui.IDC_chkOnlySelectedFeatures.checkState() == Qt.Checked) self.settings.equiDistance = self.ui.IDC_dblspinDistance.value() self.settings.vertexCnt = self.ui.IDC_dblspinVertexCnt.value() #self.settings.createHekto = (self.ui.IDC_chkCreateHekto.checkState() == Qt.Checked) self.settings.nodesAndVertices = (self.ui.IDC_chkNodesAndVertices.checkState() == Qt.Checked) if QGis.QGIS_VERSION_INT < 10900: self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex() ).toPyObject() ) else: self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) if self.settings.onlySelectedFeatures is True and self.settings.mapData.selectedLineLyr.line.selectedFeatureCount() < 1: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', u'Der gewählte Layer hat keine selektierten Elemente.', None, QApplication.UnicodeUTF8)) return False if self.ui.IDC_rbDigi.isChecked(): self.settings.modeLine = enumModeLine.customLine elif self.ui.IDC_rbShapeLine.isChecked(): self.settings.modeLine = enumModeLine.line else: #self.ui.IDC_rbStraigthLine self.settings.modeLine = enumModeLine.straightLine if self.ui.IDC_rbEquiDistance.isChecked(): self.settings.modeVertices = enumModeVertices.equiDistant else: self.settings.modeVertices = enumModeVertices.vertexCnt if self.ui.IDC_rbStraigthLine.isChecked(): ut = Util(self.iface) if ut.isFloat(self.ui.IDC_tbFromX.text(), QApplication.translate('code', 'Rechtswert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromX = float(self.ui.IDC_tbFromX.text()) if ut.isFloat(self.ui.IDC_tbFromY.text(), QApplication.translate('code', 'Hochwert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromY = float(self.ui.IDC_tbFromY.text()) if ut.isFloat(self.ui.IDC_tbToX.text(), QApplication.translate('code', 'Rechtswert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toX = float(self.ui.IDC_tbToX.text()) if ut.isFloat(self.ui.IDC_tbToY.text(), QApplication.translate('code', 'Hochwert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toY = float(self.ui.IDC_tbToY.text()) fromPnt = QgsPoint(fromX, fromY) toPnt = QgsPoint(toX, toY) self.settings.mapData.customLine = ut.createQgLineFeature([fromPnt, toPnt]) return True
class MapTool(QgsMapTool): geometry_changed = pyqtSignal(QgsGeometry, bool) tool_deactivated = pyqtSignal() def __init__(self, canvas, cursorstyle=Qt.CrossCursor): self.canvas = canvas QgsMapTool.__init__(self, canvas) self.caller = self.sender() self.cursorStyle = cursorstyle self.active = False # get selection color selcolor = self.canvas.selectionColor() mycolor = QColor(selcolor.red(), selcolor.green(), selcolor.blue(), 40) self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setStrokeColor(QColor(255, 0, 0, 40)) self.rb.setFillColor(mycolor) self.rb.setLineStyle(Qt.PenStyle(Qt.SolidLine)) self.rb.setWidth(2) def setCursorStyle(self): cursor = QCursor() cursor.setShape(self.cursorStyle) self.setCursor(cursor) def activate(self): self.caller.setChecked(True) self.setCursorStyle() def deactivate(self): self.canvas.scene().removeItem(self.rb) self.tool_deactivated.emit() self.caller.setChecked(False) QgsMapTool.deactivate(self) def setGeometry(self, geo): self.rb.setToGeometry(geo) def canvasReleaseEvent(self, mouseEvent): if mouseEvent.button() == Qt.LeftButton: if not self.active: self.active = True self.geometry_changed.emit(QgsGeometry(), False) self.rb.reset(QgsWkbTypes.PolygonGeometry) self.rb.addPoint(mouseEvent.mapPoint()) if self.rb.numberOfVertices() > 2: self.geometry_changed.emit(self.rb.asGeometry(), False) elif mouseEvent.button() == Qt.RightButton: if self.rb.numberOfVertices() > 3: self.active = False self.rb.removeLastPoint() self.rb.closePoints( ) #Ensures that a polygon geometry is closed and that the last vertex equals the first vertex geo = self.rb.asGeometry() self.geometry_changed.emit(geo, True) else: self.rb.reset(QgsWkbTypes.PolygonGeometry) def canvasMoveEvent(self, mouseEvent): if self.rb.numberOfVertices() > 1 and self.active: self.rb.removeLastPoint() self.rb.addPoint(mouseEvent.mapPoint()) pass
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 DistanceMapTool(QgsMapTool): def __init__(self, iface): self.iface = iface self.line_layer = None self.settings = MySettings() QgsMapTool.__init__(self, iface.mapCanvas()) def activate(self): QgsMapTool.activate(self) self.line_layer = MemoryLayers(self.iface).line_layer self.rubber = QgsRubberBand(self.canvas(), QGis.Point) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIcon(self.settings.value("rubberIcon")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.messageWidget = self.iface.messageBar().createMessage("Intersect It", "Not snapped.") self.messageWidgetExist = True self.messageWidget.destroyed.connect(self.messageWidgetRemoved) def deactivate(self): self.iface.messageBar().popWidget(self.messageWidget) self.rubber.reset() QgsMapTool.deactivate(self) def messageWidgetRemoved(self): self.messageWidgetExist = False def displaySnapInfo(self, match=None): if not self.messageWidgetExist: return if match is None: message = "No snap" else: message = 'Snapped to: <b>{}</b>'.format(match.layer()) self.messageWidget.setText(message) def canvasMoveEvent(self, mouseEvent): match = self.snap_to_vertex(mouseEvent.pos()) self.rubber.reset(QGis.Point) if match.type() == QgsPointLocator.Vertex and match.layer() != self.line_layer: self.rubber.addPoint(match.point()) self.displaySnapInfo(match) def canvasPressEvent(self, mouseEvent): if mouseEvent.button() != Qt.LeftButton: return match = self.snap_to_vertex(mouseEvent.pos()) if match.type() != QgsPointLocator.Vertex and match.layer() != self.line_layer: point = self.toMapCoordinates(mouseEvent.pos()) else: point = match.point() self.rubber.addPoint(point) distance = Distance(self.iface, point, 1) dlg = DistanceDialog(distance, self.canvas()) if dlg.exec_(): distance.save() self.rubber.reset() def snap_to_vertex(self, pos): """ Temporarily override snapping config and snap to vertices and edges of any editable vector layer, to allow selection of node for editing (if snapped to edge, it would offer creation of a new vertex there). """ map_point = self.toMapCoordinates(pos) tol = QgsTolerance.vertexSearchRadius(self.canvas().mapSettings()) snap_type = QgsPointLocator.Type(QgsPointLocator.Vertex) snap_layers = [] for layer in self.canvas().layers(): if not isinstance(layer, QgsVectorLayer): continue snap_layers.append(QgsSnappingUtils.LayerConfig( layer, snap_type, tol, QgsTolerance.ProjectUnits)) snap_util = self.canvas().snappingUtils() old_layers = snap_util.layers() old_mode = snap_util.snapToMapMode() old_inter = snap_util.snapOnIntersections() snap_util.setLayers(snap_layers) snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) snap_util.setSnapOnIntersections(True) m = snap_util.snapToMap(map_point) snap_util.setLayers(old_layers) snap_util.setSnapToMapMode(old_mode) snap_util.setSnapOnIntersections(old_inter) return m
class PolygonMapTool(QgsMapToolEmitPoint): """This class holds a map tool to create a polygon from points got by clicking on the map window. Points are stored in a list of point geometries, which is returned to the main plugin for use.""" def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) # rubberband class gives the user visual feedback of the drawing self.rubberBand = QgsRubberBand(self.canvas, True) # setting up outline and fill color: both red self.rubberBand.setColor(QColor(235, 36, 21)) # RGB color values, last value indicates transparency (0-255) self.rubberBand.setFillColor(QColor(255, 79, 66, 140)) self.rubberBand.setWidth(3) self.points = [] # a flag indicating when a single polygon is finished self.finished = False self.poly_bbox = False self.double_click_flag = False self.reset() def reset(self): """Empties the canvas and the points gathered thus far""" self.rubberBand.reset(True) self.poly_bbox = False self.points.clear() def keyPressEvent(self, e): """Pressing ESC resets the canvas. Pressing enter connects the polygon""" if (e.key() == 16777216): self.reset() if (e.key() == 16777220): self.finishPolygon() def canvasDoubleClickEvent(self, e): self.double_click_flag = True self.finishPolygon() def canvasReleaseEvent(self, e): """Activated when user clicks on the canvas. Gets coordinates, draws them on the map and adds to the list of points.""" if self.double_click_flag: self.double_click_flag = False return # if the finished flag is activated, the canvas will be must be reset # for a new polygon if self.finished: self.reset() self.finished = False self.click_point = self.toMapCoordinates(e.pos()) self.rubberBand.addPoint(self.click_point, True) self.points.append(self.click_point) self.rubberBand.show() def finishPolygon(self): """Activated when by user or when the map window is closed without connecting the polygon. Makes the polygon valid by making first and last point the same. This is reflected visually. Up until now the user has been drawing a line: a polygon is created and shown on the map.""" # nothing will happen if the code below has already been ran if self.finished: return # connecting the polygon is valid if there's already at least 3 points elif len(self.points) > 2: first_point = self.points[0] self.points.append(first_point) self.rubberBand.closePoints() self.rubberBand.addPoint(first_point, True) self.finished = True # a polygon is created and added to the map for visual purposes map_polygon = QgsGeometry.fromPolygonXY([self.points]) self.rubberBand.setToGeometry(map_polygon) # get the bounding box of this new polygon self.poly_bbox = self.rubberBand.asGeometry().boundingBox() else: self.finished = True def getPoints(self): """Returns list of PointXY geometries, i.e. the polygon in list form""" self.rubberBand.reset(True) return self.points def getPolyBbox(self): return self.poly_bbox
class SelectMapTool(QgsMapToolEmitPoint): features_selected_signal = pyqtSignal() selected_feature_ids = list() def __init__(self, canvas, layer, multi=True): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self._multi = multi self._layer = layer # Cursor cursor = QCursor() cursor.setShape(Qt.CrossCursor) self.setCursor(cursor) # RubberBand Style self.rubberBand = QgsRubberBand(self.canvas, True) self.rubberBand.setBrushStyle(Qt.Dense4Pattern) self.rubberBand.setColor(QColor(255, 181, 92)) self.rubberBand.setStrokeColor(QColor(232, 137, 72)) self.rubberBand.setWidth(0.2) 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 def canvasReleaseEvent(self, e): if e.button() & Qt.LeftButton: self.isEmittingPoint = False self.show_rubber_band() if self._multi and (e.modifiers() & Qt.ControlModifier or e.modifiers() & Qt.ShiftModifier): if self.rubberBand.asGeometry().type( ) == QgsWkbTypes.PolygonGeometry: self.reset() return else: id_features_intersect = self.ids_features_intersect() # if Control or Shift are selected, keeps the previous selection for id_feature_intersect in id_features_intersect: # toggle selected feature if id_feature_intersect not in self.selected_feature_ids: self.selected_feature_ids.append(id_feature_intersect) else: self.selected_feature_ids.remove(id_feature_intersect) else: id_features_intersect = self.ids_features_intersect() # Clear selected features if Control or Shift are not selected self.selected_feature_ids = id_features_intersect self.select_features() elif e.button() & Qt.RightButton: # emit the signal when at least one element has been selected if len(self._layer.selectedFeatures()): self.features_selected_signal.emit() self.reset() def canvasDoubleClickEvent(self, e): self.canvasPressEvent(e) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.show_rubber_band() def show_rubber_band(self): self.create_rubber_band() if self.rubberBand: self.rubberBand.show() def create_rubber_band(self): self.rubberBand.reset(True) if self.startPoint is None and self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y( ) == self.endPoint.y(): self.rubberBand.reset(QgsWkbTypes.PointGeometry) self.rubberBand.addPoint(self.startPoint, True) # true to update canvas else: self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) point1 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point2 = QgsPointXY(self.startPoint.x(), self.endPoint.y()) point3 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) point4 = QgsPointXY(self.endPoint.x(), self.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 # Approximate polygon to point when it's too small related to the scale if (self.rubberBand.asGeometry().area() / self.canvas.extent().area()) * 1000000 < 50: self.rubberBand.reset(QgsWkbTypes.PointGeometry) self.rubberBand.addPoint(self.startPoint, True) # true to update canvas def ids_features_intersect(self): index = QgsSpatialIndex(self._layer) bbox = self.rubberBand.asGeometry().boundingBox() candidate_features = self._layer.getFeatures(index.intersects(bbox)) geom = self.rubberBand.asGeometry() centroid = geom if not self._multi and geom.type() == QgsWkbTypes.PolygonGeometry: centroid = geom.centroid() features_selected = [] distances_features_selected = dict() for candidate_feature in candidate_features: if candidate_feature.geometry().intersects(geom): features_selected.append(candidate_feature.id()) # Calculate the distance to the centroid distances_features_selected[candidate_feature.id( )] = candidate_feature.geometry().distance(centroid) if not self._multi and geom.type() == QgsWkbTypes.PolygonGeometry: if features_selected: # Get the key corresponding to the minimum value within a dictionary features_selected = [ min(distances_features_selected, key=distances_features_selected.get) ] return features_selected def select_features(self): # show features selected self._layer.selectByIds(self.selected_feature_ids) self.reset() def deactivate(self): QgsMapTool.deactivate(self) self.deactivated.emit()
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 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)