def zoomAndShowWKT(self, wtk): geom = QgsGeometry.fromWkt(wtk) canvas = self.iface.mapCanvas() self.clear() r = QgsRubberBand(canvas, geom.type() == 3 ) r.setToGeometry(geom, None) r.setColor(QColor(0, 0, 255)) r.setFillColor(QColor(0, 0, 255, 50)) r.setWidth(3) pt = geom.pointOnSurface().asPoint() m = QgsVertexMarker(canvas) m.setCenter( pt ) m.setColor(QColor(0, 0, 255)) m.setIconSize(5) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(3) if geom.type() == 3 or geom.type() == 2: rec = geom.boundingBox() canvas.setExtent(rec) else: self.moveMapTo( pt[0], pt[1], 0) self.graphics.append(r) self.graphics.append(m) self._refresh_layers()
def loadLines(self, lines, points, markers, suffix): no = self.project.readEntry("TUVIEW", "lines{0}no".format(suffix))[0] if no: no = int(no) for i in range(no): a = self.project.readEntry("TUVIEW", 'lines{0}x{1}'.format(suffix, i))[0] a = a.split('~~') b = self.project.readEntry("TUVIEW", 'lines{0}y{1}'.format(suffix, i))[0] b = b.split('~~') points.clear() for j in range(len(a)): x = float(a[j]) y = float(b[j]) point = QgsPoint(x, y) points.append(point) if i + 1 == no: marker = QgsVertexMarker(self.tuView.canvas) if suffix == 'cs': marker.setColor(Qt.red) marker.setIconSize(10) marker.setIconType(QgsVertexMarker.ICON_BOX) else: # 'q' marker.setColor(Qt.blue) marker.setIconSize(12) marker.setIconType(QgsVertexMarker.ICON_DOUBLE_TRIANGLE) marker.setCenter(QgsPointXY(point)) markers.append(marker) line = QgsRubberBand(self.tuView.canvas, False) line.setWidth(2) if suffix == 'cs': line.setColor(QColor(Qt.red)) else: # 'q' line.setColor(QColor(Qt.blue)) line.setToGeometry(QgsGeometry.fromPolyline(points), None) lines.append(line)
class MarkerErrorGeometry(): def __init__(self, mapCanvas): self.__canvas = mapCanvas self.__marker = None def __del__(self): self.reset() def __createMarker(self, point): self.__marker = QgsVertexMarker(self.__canvas) self.__marker.setCenter(point) self.__marker.setIconType(QgsVertexMarker.ICON_X) self.__marker.setPenWidth(3) def setGeom(self, p): if self.__marker is not None: self.reset() if self.__marker is None: self.__createMarker(p) else: self.__marker.setCenter(p) def reset(self): if self.__marker is not None: self.__canvas.scene().removeItem(self.__marker) del self.__marker self.__marker = None
def wtkClicked(self): wktStr = self.ui.wktTxt.toPlainText() geom = QgsGeometry.fromWkt(wktStr) if geom is None: return self.manualLoc_lam72 = geom self.manualLocationName = wktStr self.ui.manualLocationTxt.setText(wktStr) if geom.type() == QGis.Point: marker = QgsVertexMarker(self.iface.mapCanvas()) marker.setCenter( geom.asPoint() ) marker.setColor(QtGui.QColor(0, 255, 255)) marker.setIconSize(5) else: marker = QgsRubberBand(self.iface.mapCanvas(), geom.type() == QGis.Polygon ) marker.setToGeometry(geom , None) marker.setColor(QtGui.QColor(0, 255, 255)) marker.setFillColor(QtGui.QColor(0, 255, 255, 100)) marker.setWidth(3) self._clearGraphicLayer() self.graphicsLayer.append(marker) self.ui.inputGeomTabs.setCurrentWidget(self.ui.drawInputTab)
def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): ''' draw the result ''' resultNodesVertexMarkers = canvasItemList['markers'] schema = """%(edge_schema)s""" % args table = """%(edge_table)s_vertices_pgr""" % args srid, geomType = Utils.getSridAndGeomType(con, schema, table, 'the_geom') Utils.setTransformQuotes(args, srid, args['canvas_srid']) for row in rows: cur2 = con.cursor() if args['version'] < 2.1: args['result_node_id'] = row[1] args['result_edge_id'] = row[2] args['result_cost'] = row[3] else: args['result_node_id'] = row[3] args['result_edge_id'] = row[4] args['result_cost'] = row[5] query2 = sql.SQL(""" SELECT ST_AsText(%(transform_s)s the_geom %(transform_e)s) FROM %(edge_table)s_vertices_pgr WHERE id = %(result_node_id)d """).format(**args) cur2.execute(query2) row2 = cur2.fetchone() if (row2): geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() vertexMarker = QgsVertexMarker(mapCanvas) vertexMarker.setColor(Qt.red) vertexMarker.setPenWidth(2) vertexMarker.setIconSize(5) vertexMarker.setCenter(QgsPointXY(pt)) resultNodesVertexMarkers.append(vertexMarker)
def _zoomLoc(self, txt): self._clearGraphicsLayer() locations = self.gp.fetchLocation(txt) if type( locations ) is list and len(locations): loc = locations[0] LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72'] LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72'] UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72'] UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72'] self.gh.zoomtoRec(QgsPoint(LowerLeftX,LowerLeftY),QgsPoint(UpperRightX, UpperRightY), 31370) xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"]["Y_Lambert72"] x, y = self.gh.prjPtToMapCrs(QgsPoint( xlb , ylb), 31370) m = QgsVertexMarker(self.iface.mapCanvas()) self.graphicsLayer.append(m) m.setCenter(QgsPoint(x,y)) m.setColor(QtGui.QColor(255,255,0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(9) elif type( locations ) is str: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"), locations, level=QgsMessageBar.WARNING, duration=3) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","onbekende fout"), level=QgsMessageBar.CRITICAL, duration=3)
class MapToolEmitPoint(MapToolInteractive): """Tool to emit mouse clicks as map points.""" canvasClicked = pyqtSignal(QgsPointV2, Qt.MouseButton) def __init__(self, canvas): super(MapToolEmitPoint, self).__init__(canvas) self._vertexMarker = QgsVertexMarker(canvas) self._vertexMarker.setIconType(QgsVertexMarker.ICON_NONE) def deactivate(self): self._vertexMarker.setCenter(QgsPoint()) super(MapToolEmitPoint, self).deactivate() def canvasReleaseEvent(self, e): super(MapToolEmitPoint, self).canvasReleaseEvent(e) if e.isAccepted(): return # Emit mode mapPoint, mapPointV2, snapped = self._snapCursorPoint(e.pos()) self._vertexMarker.setCenter(mapPoint) self.canvasClicked.emit(mapPointV2, e.button()) e.accept() def setVertexIcon(self, iconType, iconSize=None, penWidth=None, color=None): self._vertexMarker.setIconType(iconType) if iconSize is not None: self._vertexMarker.setIconSize(iconSize) if iconSize is not None: self._vertexMarker.setPenWidth(penWidth) if iconSize is not None: self._vertexMarker.setColor(color)
def showFlandersPointOnCanvas(self): try: self.__setMapSrs() inputtext = self.dlg.lineEditFlanders.text().encode("utf8").strip() params = urllib.urlencode({"q": inputtext}) result2 = urllib2.urlopen(self.location + params).read() doc = json.loads(result2) if len(doc) > 0 and len(doc["LocationResult"]) > 0: x = doc["LocationResult"][0]["Location"]["X_Lambert72"] y = doc["LocationResult"][0]["Location"]["Y_Lambert72"] canv = self.iface.mapCanvas() canv.clear() marker = QgsVertexMarker(canv) marker.setPenWidth(3) marker.setIconType(QgsVertexMarker.ICON_CROSS) # or, ICON_X ICON_BOX point = QgsPoint(x, y) marker.setCenter(point) self.markers.append(marker) scale = 100 # Create a rectangle to cover the new extent rect = QgsRectangle( doc["LocationResult"][0]["BoundingBox"]["LowerLeft"]["X_Lambert72"], doc["LocationResult"][0]["BoundingBox"]["LowerLeft"]["Y_Lambert72"], doc["LocationResult"][0]["BoundingBox"]["UpperRight"]["X_Lambert72"], doc["LocationResult"][0]["BoundingBox"]["UpperRight"]["Y_Lambert72"], ) # Set the extent to our new rectangle canv.setExtent(rect) canv.refresh() # QMessageBox.information( self.iface.mainWindow(),"Info", "x, y = %s" %str(x) + ", " + str(y) ) # keeps the window alive self.run() else: QMessageBox.information(self.iface.mainWindow(), "Info", "Geen locatie gevonden voor: %s" % inputtext) except: QMessageBox.warning(self.iface.mainWindow(), "Warning", "error: %s" % sys.exc_info()[0])
def _setPointMarker(self, pointgeom): m = QgsVertexMarker(self.qgisIface.mapCanvas()) m.setColor(QColor(self.config['marker_color'])) m.setIconType(self.config['marker_icon']) m.setPenWidth(self.config['marker_width']) m.setIconSize(self.config['marker_size']) m.setCenter(pointgeom.asPoint()) return m
def addMarker(iface, pnt, clr=QColor(255, 255, 0), ico=QgsVertexMarker.ICON_BOX ): m = QgsVertexMarker(iface.mapCanvas()) m.setCenter(pnt) m.setColor(clr) m.setIconSize(1) m.setIconType(ico) m.setPenWidth(9) return m
def _addMarker(self, pnt, clr=QColor(255,255,0)): m = QgsVertexMarker(self.iface.mapCanvas()) m.setCenter( pnt ) m.setColor(clr) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(9) self.graphicsLayer.append(m) return m
def addVertex(self, pt): self.rubberBand.addPoint(pt) m = QgsVertexMarker(self.canvas) m.setCenter(pt) self.vertexMarkers.append(m) #if self.yx: pt = QgsPoint(pt.y(),pt.x()) self.captureList.append (pt) return len(self.captureList)
def addPointGraphic(self, xy, color="#FFFF00", size=1, pen=10, markerType=QgsVertexMarker.ICON_BOX ): "create a point Graphic at location xy and return it" pt = QgsPointXY(xy[0], xy[1]) if isinstance( xy, Iterable) else QgsPointXY(xy) m = QgsVertexMarker(self.canvas) m.setCenter(pt) m.setColor(QColor(color)) m.setIconSize(size) m.setIconType(markerType) m.setPenWidth(pen) return m
def addPointGraphic(self, xy, color="#FFFF00", size=1, pen=10, markerType=QgsVertexMarker.ICON_BOX ): "create a point Graphic at location xy and return it" x, y = list( xy )[:2] m = QgsVertexMarker(self.canvas) m.setCenter(QgsPoint(x,y)) m.setColor(QColor(color)) m.setIconSize(size) m.setIconType(markerType) m.setPenWidth(pen) return m
def addTempGeometry(self, chunkId, geometryType, geometry): """ Add geometries as rubber bands or vertex objects """ if geometryType == "Polygon" or geometryType == "LineString": self.__tmpGeometry[chunkId].addGeometry(geometry, None) elif geometryType == "Point": vertex = QgsVertexMarker(self.iface.mapCanvas()) vertex.setCenter(geometry.asPoint()) vertex.setColor(QColor(0,255,0)) vertex.setIconSize(6) vertex.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X vertex.setPenWidth(3) self.__tmpGeometry[chunkId].append(vertex)
def _createSnappableMarkers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return extent = self.canvas().extent() for vertex in self._snappableVertices.asMultiPoint(): if (extent.contains(vertex)): marker = QgsVertexMarker(self.canvas()) marker.setIconType(QgsVertexMarker.ICON_X) marker.setColor(Qt.gray) marker.setPenWidth(1) marker.setCenter(vertex) self._snappableMarkers.append(marker)
def updateTrack(self): if self.pos and self.trackLen: if len(self.track) >= self.trackLen: tpr = self.track.popleft() self.canvas.scene().removeItem(tpr) del(tpr) tp = QgsVertexMarker(self.canvas) tp.setCenter(self.pos) tp.setIconType(QgsVertexMarker.ICON_CROSS) tp.setColor(self.trackColor) tp.setZValue(self.zValue() - 0.1) tp.setIconSize(3) tp.setPenWidth(3) self.track.append(tp)
def onSelectionChanged(self): selPois = self._getSelectedPois() canvas = self.iface.mapCanvas() self.clearGraphicsLayer() pts = [ self.gh.prjPtToMapCrs( n['location']['points'][0]['Point']['coordinates'], 4326) for n in selPois ] for pt in pts: x,y = pt m = QgsVertexMarker(canvas) self.graphicsLayer.append(m) m.setCenter(QgsPointXY(x,y)) m.setColor(QColor(255,255,0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(10)
class MarkerCursor(QObject): def __init__(self, mapCanvas, srsOL): QObject.__init__(self) self.__srsOL = srsOL self.__canvas = mapCanvas self.__marker = None self.__showMarker = True def __del__(self): self.reset() def __refresh(self, pointCenter): if self.__marker is not None: self.reset() self.__marker = QgsVertexMarker(self.__canvas) self.__marker.setCenter(pointCenter) self.__marker.setIconType(QgsVertexMarker.ICON_X) self.__marker.setPenWidth(4) def setVisible(self, visible): self.__showMarker = visible def reset(self): self.__canvas.scene().removeItem(self.__marker) del self.__marker self.__marker = None @pyqtSlot(str) def changeMarker(self, strListExtent): if not self.__showMarker: return # left, bottom, right, top left, bottom, right, top = [float(item) for item in strListExtent.split(',')] pointCenter = QgsRectangle(QgsPointXY(left, top), QgsPointXY(right, bottom)).center() crsCanvas = self.__canvas.mapSettings().destinationCrs() if self.__srsOL != crsCanvas: coodTrans = QgsCoordinateTransform(self.__srsOL, crsCanvas, QgsProject.instance()) pointCenter = coodTrans.transform( pointCenter, QgsCoordinateTransform.ForwardTransform) self.__refresh(pointCenter)
def mouseMoved(self, event): pClicked = QPoint( event.pos().x(), event.pos().y() ) ( _, snappedPoints ) = self.networkAnalyzer.getSnapper().snapPoint( pClicked, [] ) for marker in self.highLightedPoints: self.canvas.scene().removeItem( marker ) self.highLightedPoints = [] if len( snappedPoints ) > 0: for point in snappedPoints: marker = QgsVertexMarker( self.canvas ) marker.setCenter( point.snappedVertex ) marker.setColor( QColor( "#FFFF33" ) ) marker.setIconSize( 10 ) marker.setIconType( QgsVertexMarker.ICON_X ) marker.setPenWidth( 2 ) self.highLightedPoints.append(marker)
def loadPoints(self, points, markers): a = self.project.readEntry("TUVIEW", "pointsx")[0] if a: a = a.split('~~') b = self.project.readEntry("TUVIEW", "pointsy")[0] b = b.split('~~') for i in range(len(a)): x = float(a[i]) y = float(b[i]) point = QgsPoint(x, y) points.append(point) marker = QgsVertexMarker(self.tuView.canvas) marker.setColor(Qt.red) marker.setFillColor(Qt.red) marker.setIconSize(10) marker.setIconType(QgsVertexMarker.ICON_CIRCLE) marker.setCenter(QgsPointXY(point)) markers.append(marker)
def _addGeometryError(self, error): self._geometryErrors.append(error) if (self._tip != ''): self._tip += '\n' self._tip += error.what() if (error.hasWhere()): marker = QgsVertexMarker(self.canvas()) marker.setCenter(error.where()) marker.setIconType(QgsVertexMarker.ICON_X) marker.setPenWidth(2) marker.setToolTip(error.what()) marker.setColor(Qt.green) marker.setZValue(marker.zValue() + 1) self._geometryErrorMarkers.append(marker) self._iface.mainWindow().statusBar().showMessage(error.what()) if (self._tip != ''): self._iface.mainWindow().statusBar().setToolTip(self._tip)
def canvasMoveEvent(self, event): """ Whenever the mouse is moved update the rubberband and the snapping. :param event: QMouseEvent with coordinates """ point_clicked = QPoint(event.pos().x(), event.pos().y()) (_, snapped_points) = self.networkAnalyzer.getSnapper().snapPoint(point_clicked, []) for marker in self.highLightedPoints: self.canvas.scene().removeItem(marker) self.highLightedPoints = [] if len(snapped_points) > 0: for point in snapped_points: marker = QgsVertexMarker(self.canvas) marker.setCenter(point.snappedVertex) marker.setColor(QColor("#FFFF33")) marker.setIconSize(10) marker.setIconType(QgsVertexMarker.ICON_X) marker.setPenWidth(2) self.highLightedPoints.append(marker)
class ParentManage(ParentAction, object): def __init__(self, iface, settings, controller, plugin_dir): """ Class to keep common functions of classes 'ManageDocument', 'ManageElement' and 'ManageVisit' of toolbar 'edit'.""" super(ParentManage, self).__init__(iface, settings, controller, plugin_dir) self.x = "" self.y = "" self.canvas = self.iface.mapCanvas() self.plan_om = None self.previous_map_tool = None self.autocommit = True self.lazy_widget = None self.workcat_id_end = None self.xyCoordinates_conected = False self.remove_ids = True def reset_lists(self): """ Reset list of selected records """ self.ids = [] self.list_ids = {} self.list_ids['arc'] = [] self.list_ids['node'] = [] self.list_ids['connec'] = [] self.list_ids['gully'] = [] self.list_ids['element'] = [] def reset_layers(self): """ Reset list of layers """ self.layers = {} self.layers['arc'] = [] self.layers['node'] = [] self.layers['connec'] = [] self.layers['gully'] = [] self.layers['element'] = [] def reset_model(self, dialog, table_object, geom_type): """ Reset model of the widget """ table_relation = table_object + "_x_" + geom_type widget_name = "tbl_" + table_relation widget = utils_giswater.getWidget(dialog, widget_name) if widget: widget.setModel(None) def remove_selection(self, remove_groups=True): """ Remove all previous selections """ layer = self.controller.get_layer_by_tablename("v_edit_arc") if layer: layer.removeSelection() layer = self.controller.get_layer_by_tablename("v_edit_node") if layer: layer.removeSelection() layer = self.controller.get_layer_by_tablename("v_edit_connec") if layer: layer.removeSelection() layer = self.controller.get_layer_by_tablename("v_edit_element") if layer: layer.removeSelection() if self.project_type == 'ud': layer = self.controller.get_layer_by_tablename("v_edit_gully") if layer: layer.removeSelection() try: if remove_groups: for layer in self.layers['arc']: layer.removeSelection() for layer in self.layers['node']: layer.removeSelection() for layer in self.layers['connec']: layer.removeSelection() for layer in self.layers['gully']: layer.removeSelection() for layer in self.layers['element']: layer.removeSelection() except: pass self.canvas.refresh() def reset_widgets(self, dialog, table_object): """ Clear contents of input widgets """ if table_object == "doc": utils_giswater.setWidgetText(dialog, "doc_type", "") utils_giswater.setWidgetText(dialog, "observ", "") utils_giswater.setWidgetText(dialog, "path", "") elif table_object == "element": utils_giswater.setWidgetText(dialog, "elementcat_id", "") utils_giswater.setWidgetText(dialog, "state", "") utils_giswater.setWidgetText(dialog, "expl_id","") utils_giswater.setWidgetText(dialog, "ownercat_id", "") utils_giswater.setWidgetText(dialog, "location_type", "") utils_giswater.setWidgetText(dialog, "buildercat_id", "") utils_giswater.setWidgetText(dialog, "workcat_id", "") utils_giswater.setWidgetText(dialog, "workcat_id_end", "") utils_giswater.setWidgetText(dialog, "comment", "") utils_giswater.setWidgetText(dialog, "observ", "") utils_giswater.setWidgetText(dialog, "path", "") utils_giswater.setWidgetText(dialog, "rotation", "") utils_giswater.setWidgetText(dialog, "verified", "") utils_giswater.setWidgetText(dialog, dialog.num_elements, "") def fill_widgets(self, dialog, table_object, row): """ Fill input widgets with data int he @row """ if table_object == "doc": utils_giswater.setWidgetText(dialog, "doc_type", row["doc_type"]) utils_giswater.setWidgetText(dialog, "observ", row["observ"]) utils_giswater.setWidgetText(dialog, "path", row["path"]) elif table_object == "element": state = "" if row['state']: sql = ("SELECT name FROM " + self.schema_name + ".value_state" " WHERE id = '" + str(row['state']) + "'") row_aux = self.controller.get_row(sql, commit=self.autocommit) if row_aux: state = row_aux[0] expl_id = "" if row['expl_id']: sql = ("SELECT name FROM " + self.schema_name + ".exploitation" " WHERE expl_id = '" + str(row['expl_id']) + "'") row_aux = self.controller.get_row(sql, commit=self.autocommit) if row_aux: expl_id = row_aux[0] utils_giswater.setWidgetText(dialog, "code", row['code']) sql = ("SELECT elementtype_id FROM " + self.schema_name + ".cat_element" " WHERE id = '" + str(row['elementcat_id']) + "'") row_type = self.controller.get_row(sql) if row_type: utils_giswater.setWidgetText(dialog, "element_type", row_type[0]) utils_giswater.setWidgetText(dialog, "elementcat_id", row['elementcat_id']) utils_giswater.setWidgetText(dialog, "num_elements", row['num_elements']) utils_giswater.setWidgetText(dialog, "state", state) utils_giswater.setWidgetText(dialog, "expl_id", expl_id) utils_giswater.setWidgetText(dialog, "ownercat_id", row['ownercat_id']) utils_giswater.setWidgetText(dialog, "location_type", row['location_type']) utils_giswater.setWidgetText(dialog, "buildercat_id", row['buildercat_id']) builtdate = QDate.fromString(str(row['builtdate']), 'yyyy-MM-dd') dialog.builtdate.setDate(builtdate) utils_giswater.setWidgetText(dialog, "workcat_id", row['workcat_id']) utils_giswater.setWidgetText(dialog, "workcat_id_end", row['workcat_id_end']) utils_giswater.setWidgetText(dialog, "comment", row['comment']) utils_giswater.setWidgetText(dialog, "observ", row['observ']) utils_giswater.setWidgetText(dialog, "link", row['link']) utils_giswater.setWidgetText(dialog, "verified", row['verified']) utils_giswater.setWidgetText(dialog, "rotation", row['rotation']) if str(row['undelete']) == 'True': dialog.undelete.setChecked(True) def get_records_geom_type(self, dialog, table_object, geom_type): """ Get records of @geom_type associated to selected @table_object """ object_id = utils_giswater.getWidgetText(dialog, table_object + "_id") table_relation = table_object + "_x_" + geom_type widget_name = "tbl_" + table_relation exists = self.controller.check_table(table_relation) if not exists: self.controller.log_info("Not found: " + str(table_relation)) return sql = ("SELECT " + geom_type + "_id" " FROM " + self.schema_name + "." + table_relation + "" " WHERE " + table_object + "_id = '" + str(object_id) + "'") rows = self.controller.get_rows(sql, log_info=False) if rows: for row in rows: self.list_ids[geom_type].append(str(row[0])) self.ids.append(str(row[0])) expr_filter = self.get_expr_filter(geom_type) self.set_table_model(dialog, widget_name, geom_type, expr_filter) def exist_object(self, dialog, table_object): """ Check if selected object (document or element) already exists """ # Reset list of selected records self.reset_lists() field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" object_id = utils_giswater.getWidgetText(dialog, table_object + "_id") # Check if we already have data with selected object_id sql = ("SELECT * " " FROM " + self.schema_name + "." + str(table_object) + "" " WHERE " + str(field_object_id) + " = '" + str(object_id) + "'") row = self.controller.get_row(sql, log_info=False) # If object_id not found: Clear data if not row: self.reset_widgets(dialog, table_object) if table_object == 'element': self.set_combo(dialog, 'state', 'value_state', 'state_vdefault', field_name='name') self.set_combo(dialog, 'expl_id', 'exploitation', 'exploitation_vdefault', field_id='expl_id',field_name='name') self.set_calendars(dialog, 'builtdate', 'config_param_user', 'value', 'builtdate_vdefault') self.set_combo(dialog, 'workcat_id', 'cat_work', 'workcat_vdefault', field_id='id', field_name='id') if hasattr(self, 'single_tool_mode'): # some tools can work differently if standalone or integrated in # another tool if self.single_tool_mode: self.remove_selection(True) else: self.remove_selection(True) self.reset_model(dialog, table_object, "arc") self.reset_model(dialog, table_object, "node") self.reset_model(dialog, table_object, "connec") self.reset_model(dialog, table_object, "element") if self.project_type == 'ud': self.reset_model(dialog, table_object, "gully") return # Fill input widgets with data of the @row self.fill_widgets(dialog, table_object, row) # Check related 'arcs' self.get_records_geom_type(dialog, table_object, "arc") # Check related 'nodes' self.get_records_geom_type(dialog, table_object, "node") # Check related 'connecs' self.get_records_geom_type(dialog, table_object, "connec") # Check related 'elements' self.get_records_geom_type(dialog, table_object, "element") # Check related 'gullys' if self.project_type == 'ud': self.get_records_geom_type(dialog, table_object, "gully") def populate_combo(self, dialog, widget, table_name, field_name="id"): """ Executes query and fill combo box """ sql = ("SELECT " + field_name + "" " FROM " + self.schema_name + "." + table_name + "" " ORDER BY " + field_name) rows = self.controller.get_rows(sql, commit=self.autocommit) utils_giswater.fillComboBox(dialog, widget, rows) if rows: utils_giswater.setCurrentIndex(dialog, widget, 0) def set_combo(self, dialog, widget, table_name, parameter, field_id='id', field_name='id'): """ Executes query and set combo box """ sql = ("SELECT t1." + field_name + " FROM " + self.schema_name + "." + table_name + " as t1" " INNER JOIN " + self.schema_name + ".config_param_user as t2 ON t1." + field_id + "::text = t2.value::text" " WHERE parameter = '" + parameter + "' AND cur_user = current_user") row = self.controller.get_row(sql) if row: utils_giswater.setWidgetText(dialog, widget, row[0]) def set_calendars(self, dialog, widget, table_name, value, parameter): """ Executes query and set QDateEdit """ sql = ("SELECT " + value + " FROM " + self.schema_name + "." + table_name + "" " WHERE parameter = '" + parameter + "' AND cur_user = current_user") row = self.controller.get_row(sql) if row: date = QDate.fromString(row[0], 'yyyy-MM-dd') else: date = QDate.currentDate() utils_giswater.setCalendarDate(dialog, widget, date) def add_point(self): """ Create the appropriate map tool and connect to the corresponding signal """ active_layer = self.iface.activeLayer() if active_layer is None: active_layer = self.controller.get_layer_by_tablename('version') self.iface.setActiveLayer(active_layer) # Vertex marker self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setColor(QColor(255, 100, 255)) self.vertex_marker.setIconSize(15) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CROSS) self.vertex_marker.setPenWidth(3) # Snapper self.snapper = QgsMapCanvasSnapper(self.canvas) self.emit_point = QgsMapToolEmitPoint(self.canvas) self.previous_map_tool = self.canvas.mapTool() self.canvas.setMapTool(self.emit_point) #self.canvas.connect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint&)"), self.mouse_move) self.canvas.xyCoordinates.connect(self.mouse_move) self.xyCoordinates_conected = True self.emit_point.canvasClicked.connect(partial(self.get_xy)) def mouse_move(self, p): self.snapped_point = None self.vertex_marker.hide() map_point = self.canvas.getCoordinateTransform().transform(p) x = map_point.x() y = map_point.y() eventPoint = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result: # Check feature for snapped_point in result: self.snapped_point = QgsPoint(snapped_point.snappedVertex) self.vertex_marker.setCenter(self.snapped_point) self.vertex_marker.show() else: self.vertex_marker.hide() def get_xy(self, point): """ Get coordinates of selected point """ if self.snapped_point: self.x = self.snapped_point.x() self.y = self.snapped_point.y() else: self.x = point.x() self.y = point.y() message = "Geometry has been added!" self.controller.show_info(message) self.emit_point.canvasClicked.disconnect() self.canvas.xyCoordinates.disconnect() self.xyCoordinates_conected = False self.iface.mapCanvas().refreshAllLayers() self.vertex_marker.hide() def get_values_from_form(self, dialog): self.enddate = utils_giswater.getCalendarDate(dialog, "enddate") self.workcat_id_end = utils_giswater.getWidgetText(dialog, "workcat_id_end") self.description = utils_giswater.getWidgetText(dialog, "descript") def tab_feature_changed(self, dialog, table_object, feature_id=None): """ Set geom_type and layer depending selected tab @table_object = ['doc' | 'element' | 'cat_work'] """ self.get_values_from_form(dialog) if dialog.tab_feature.currentIndex() == 3: dialog.btn_snapping.setEnabled(False) else: dialog.btn_snapping.setEnabled(True) tab_position = dialog.tab_feature.currentIndex() if tab_position == 0: self.geom_type = "arc" elif tab_position == 1: self.geom_type = "node" elif tab_position == 2: self.geom_type = "connec" elif tab_position == 3: self.geom_type = "element" elif tab_position == 4: self.geom_type = "gully" self.hide_generic_layers() widget_name = "tbl_" + table_object + "_x_" + str(self.geom_type) viewname = "v_edit_" + str(self.geom_type) self.widget = utils_giswater.getWidget(dialog, widget_name) # Adding auto-completion to a QLineEdit self.set_completer_feature_id(dialog.feature_id, self.geom_type, viewname) self.iface.actionPan().trigger() def set_completer_object(self, dialog, table_object): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ widget = utils_giswater.getWidget(dialog, table_object + "_id") if not widget: return # Set SQL field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" sql = ("SELECT DISTINCT(" + field_object_id + ")" " FROM " + self.schema_name + "." + table_object) row = self.controller.get_rows(sql, commit=self.autocommit) for i in range(0, len(row)): aux = row[i] row[i] = str(aux[0]) # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(row) self.completer.setModel(model) def set_completer_widget(self, tablename, widget, field_id): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ if not widget: return # Set SQL sql = ("SELECT DISTINCT(" + field_id + ")" " FROM " + self.schema_name + "." + tablename +"" " ORDER BY "+ field_id + "") row = self.controller.get_rows(sql) for i in range(0, len(row)): aux = row[i] row[i] = str(aux[0]) # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(row) self.completer.setModel(model) def set_completer_feature_id(self, widget, geom_type, viewname): """ Set autocomplete of widget 'feature_id' getting id's from selected @viewname """ # Adding auto-completion to a QLineEdit self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) widget.setCompleter(self.completer) model = QStringListModel() sql = ("SELECT " + geom_type + "_id" " FROM " + self.schema_name + "." + viewname) row = self.controller.get_rows(sql, commit=self.autocommit) if row: for i in range(0, len(row)): aux = row[i] row[i] = str(aux[0]) model.setStringList(row) self.completer.setModel(model) def get_expr_filter(self, geom_type): """ Set an expression filter with the contents of the list. Set a model with selected filter. Attach that model to selected table """ list_ids = self.list_ids[geom_type] field_id = geom_type + "_id" if len(list_ids) == 0: return None # Set expression filter with features in the list expr_filter = field_id + " IN (" for i in range(len(list_ids)): expr_filter += "'" + str(list_ids[i]) + "', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) if not is_valid: return None # Select features of layers applying @expr self.select_features_by_ids(geom_type, expr) return expr_filter def reload_table(self, dialog, table_object, geom_type, expr_filter): """ Reload @widget with contents of @tablename applying selected @expr_filter """ if type(table_object) is str: widget_name = "tbl_" + table_object + "_x_" + geom_type widget = utils_giswater.getWidget(dialog, widget_name) if not widget: message = "Widget not found" self.controller.log_info(message, parameter=widget_name) return None elif type(table_object) is QTableView: widget = table_object else: message = "Table_object is not a table name or QTableView" self.controller.log_info(message) return None expr = self.set_table_model(dialog, widget, geom_type, expr_filter) return expr def set_table_model(self, dialog, table_object, geom_type, expr_filter): """ Sets a TableModel to @widget_name attached to @table_name and filter @expr_filter """ expr = None if expr_filter: # Check expression (is_valid, expr) = self.check_expression(expr_filter) #@UnusedVariable if not is_valid: return expr # Set a model with selected filter expression table_name = "v_edit_" + geom_type if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set the model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.select() if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) return expr # Attach model to selected widget if type(table_object) is str: widget = utils_giswater.getWidget(dialog, table_object) if not widget: message = "Widget not found" self.controller.log_info(message, parameter=table_object) return expr elif type(table_object) is QTableView: widget = table_object else: message = "Table_object is not a table name or QTableView" self.controller.log_info(message) return expr if expr_filter: widget.setModel(model) widget.model().setFilter(expr_filter) widget.model().select() else: widget.setModel(None) return expr def apply_lazy_init(self, widget): """Apply the init function related to the model. It's necessary a lazy init because model is changed everytime is loaded.""" if self.lazy_widget is None: return if widget != self.lazy_widget: return self.lazy_init_function(self.lazy_widget) def lazy_configuration(self, widget, init_function): """set the init_function where all necessary events are set. This is necessary to allow a lazy setup of the events because set_table_events can create a table with a None model loosing any event connection.""" # TODO: create a dictionary with key:widged.objectName value:initFuction # to allow multiple lazy initialization self.lazy_widget = widget self.lazy_init_function = init_function def select_features_by_ids(self, geom_type, expr): """ Select features of layers of group @geom_type applying @expr """ # Build a list of feature id's and select them for layer in self.layers[geom_type]: if expr is None: layer.removeSelection() else: it = layer.getFeatures(QgsFeatureRequest(expr)) id_list = [i.id() for i in it] if len(id_list) > 0: layer.selectByIds(id_list) else: layer.removeSelection() def delete_records(self, dialog, table_object, query=False): """ Delete selected elements of the table """ self.disconnect_signal_selection_changed() if type(table_object) is str: widget_name = "tbl_" + table_object + "_x_" + self.geom_type widget = utils_giswater.getWidget(dialog, widget_name) if not widget: message = "Widget not found" self.controller.show_warning(message, parameter=widget_name) return elif type(table_object) is QTableView: widget = table_object else: message = "Table_object is not a table name or QTableView" self.controller.log_info(message) return # Get selected rows selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_info_box(message) return if query: full_list = widget.model() for x in range(0, full_list.rowCount()): self.ids.append(widget.model().record(x).value(str(self.geom_type)+"_id")) else: self.ids = self.list_ids[self.geom_type] field_id = self.geom_type + "_id" del_id = [] inf_text = "" list_id = "" for i in range(0, len(selected_list)): row = selected_list[i].row() id_feature = widget.model().record(row).value(field_id) inf_text += str(id_feature) + ", " list_id = list_id + "'" + str(id_feature) + "', " del_id.append(id_feature) inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: for el in del_id: self.ids.remove(el) else: return expr_filter = None expr = None if len(self.ids) > 0: # Set expression filter with features in the list expr_filter = "\"" + field_id + "\" IN (" for i in range(len(self.ids)): expr_filter += "'" + str(self.ids[i]) + "', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) #@UnusedVariable if not is_valid: return # Update model of the widget with selected expr_filter if query: self.delete_feature_at_plan(dialog, self.geom_type, list_id) self.reload_qtable(dialog, self.geom_type, self.plan_om) else: self.reload_table(dialog, table_object, self.geom_type, expr_filter) self.apply_lazy_init(table_object) # Select features with previous filter # Build a list of feature id's and select them self.select_features_by_ids(self.geom_type, expr) if query: self.remove_selection() # Update list self.list_ids[self.geom_type] = self.ids self.enable_feature_type(dialog) self.connect_signal_selection_changed(dialog, table_object) def manage_close(self, dialog, table_object, cur_active_layer=None): """ Close dialog and disconnect snapping """ if cur_active_layer: self.iface.setActiveLayer(cur_active_layer) if hasattr(self, 'single_tool_mode'): # some tools can work differently if standalone or integrated in # another tool if self.single_tool_mode: self.remove_selection(True) else: self.remove_selection(True) self.reset_model(dialog, table_object, "arc") self.reset_model(dialog, table_object, "node") self.reset_model(dialog, table_object, "connec") self.reset_model(dialog, table_object, "element") if self.project_type == 'ud': self.reset_model(dialog, table_object, "gully") self.close_dialog(dialog) self.hide_generic_layers() self.disconnect_snapping() self.disconnect_signal_selection_changed() # reset previous dialog in not in single_tool_mode # if hasattr(self, 'single_tool_mode') and not self.single_tool_mode: # if hasattr(self, 'previous_dialog'): def selection_init(self, dialog, table_object, query=False): """ Set canvas map tool to an instance of class 'MultipleSelection' """ multiple_selection = MultipleSelection(self.iface, self.controller, self.layers[self.geom_type], parent_manage=self, table_object=table_object, dialog=dialog) self.previous_map_tool = self.canvas.mapTool() self.canvas.setMapTool(multiple_selection) self.disconnect_signal_selection_changed() self.connect_signal_selection_changed(dialog, table_object, query) cursor = self.get_cursor_multiple_selection() self.canvas.setCursor(cursor) def selection_changed(self, dialog, table_object, geom_type, query=False): """ Slot function for signal 'canvas.selectionChanged' """ self.disconnect_signal_selection_changed() field_id = geom_type + "_id" if self.remove_ids: self.ids = [] # Iterate over all layers of the group for layer in self.layers[self.geom_type]: if layer.selectedFeatureCount() > 0: # Get selected features of the layer features = layer.selectedFeatures() for feature in features: # Append 'feature_id' into the list selected_id = feature.attribute(field_id) if selected_id not in self.ids: self.ids.append(selected_id) if geom_type == 'arc': self.list_ids['arc'] = self.ids elif geom_type == 'node': self.list_ids['node'] = self.ids elif geom_type == 'connec': self.list_ids['connec'] = self.ids elif geom_type == 'gully': self.list_ids['gully'] = self.ids elif geom_type == 'element': self.list_ids['element'] = self.ids expr_filter = None if len(self.ids) > 0: # Set 'expr_filter' with features that are in the list expr_filter = "\"" + field_id + "\" IN (" for i in range(len(self.ids)): expr_filter += "'" + str(self.ids[i]) + "', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) #@UnusedVariable if not is_valid: return self.select_features_by_ids(geom_type, expr) # Reload contents of table 'tbl_@table_object_x_@geom_type' if query: self.insert_feature_to_plan(dialog, self.geom_type) if self.plan_om == 'plan': self.remove_selection() self.reload_qtable(dialog, geom_type, self.plan_om) else: self.reload_table(dialog, table_object, self.geom_type, expr_filter) self.apply_lazy_init(table_object) # Remove selection in generic 'v_edit' layers if self.plan_om == 'plan': self.remove_selection(False) self.enable_feature_type(dialog) self.connect_signal_selection_changed(dialog, table_object) def delete_feature_at_plan(self, dialog, geom_type, list_id): """ Delete features_id to table plan_@geom_type_x_psector""" value = utils_giswater.getWidgetText(dialog, dialog.psector_id) sql = ("DELETE FROM " + self.schema_name + "." + self.plan_om + "_psector_x_" + geom_type + "" " WHERE " + geom_type + "_id IN (" + list_id + ") AND psector_id = '" + str(value) + "'") self.controller.execute_sql(sql) def enable_feature_type(self, dialog): feature_type = dialog.findChild(QComboBox, 'feature_type') assigned_to = dialog.findChild(QComboBox, 'cmb_assigned_to') visit_class = dialog.findChild(QComboBox, 'cmb_visit_class') table = dialog.findChild(QTableView, 'tbl_relation') if feature_type is not None and table is not None: if len(self.ids) > 0: feature_type.setEnabled(False) if assigned_to is not None: assigned_to.setEnabled(False) if assigned_to is not None: visit_class.setEnabled(False) else: feature_type.setEnabled(True) if assigned_to is not None: assigned_to.setEnabled(True) if assigned_to is not None: visit_class.setEnabled(True) def insert_feature(self, dialog, table_object, query=False, remove_ids=True): """ Select feature with entered id. Set a model with selected filter. Attach that model to selected table """ self.disconnect_signal_selection_changed() # Clear list of ids if remove_ids: self.ids = [] field_id = self.geom_type + "_id" feature_id = utils_giswater.getWidgetText(dialog, "feature_id") if feature_id == 'null': message = "You need to enter a feature id" self.controller.show_info_box(message) return # Iterate over all layers of the group for layer in self.layers[self.geom_type]: if layer.selectedFeatureCount() > 0: # Get selected features of the layer features = layer.selectedFeatures() for feature in features: # Append 'feature_id' into the list selected_id = feature.attribute(field_id) if selected_id not in self.ids: self.ids.append(selected_id) if feature_id not in self.ids: # If feature id doesn't exist in list -> add self.ids.append(str(feature_id)) # Set expression filter with features in the list expr_filter = "\"" + field_id + "\" IN (" for i in range(len(self.ids)): expr_filter += "'" + str(self.ids[i]) + "', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) if not is_valid: return # Select features with previous filter # Build a list of feature id's and select them for layer in self.layers[self.geom_type]: it = layer.getFeatures(QgsFeatureRequest(expr)) id_list = [i.id() for i in it] if len(id_list) > 0: layer.selectByIds(id_list) # Reload contents of table 'tbl_???_x_@geom_type' if query: self.insert_feature_to_plan(dialog, self.geom_type) self.remove_selection() else: self.reload_table(dialog, table_object, self.geom_type, expr_filter) self.apply_lazy_init(table_object) # Update list self.list_ids[self.geom_type] = self.ids self.enable_feature_type(dialog) self.connect_signal_selection_changed(dialog, table_object) def insert_feature_to_plan(self, dialog, geom_type): """ Insert features_id to table plan_@geom_type_x_psector""" value = utils_giswater.getWidgetText(dialog, dialog.psector_id) for i in range(len(self.ids)): sql = ("SELECT " + geom_type + "_id" " FROM " + self.schema_name + "." + self.plan_om + "_psector_x_" + geom_type + "" " WHERE " + geom_type + "_id = '" + str(self.ids[i]) + "' AND psector_id = '" + str(value) + "'") row = self.controller.get_row(sql) if not row: sql = ("INSERT INTO " + self.schema_name + "." + self.plan_om + "_psector_x_" + geom_type + "" "(" + geom_type + "_id, psector_id) VALUES('" + str(self.ids[i]) + "', '" + str(value) + "')") self.controller.execute_sql(sql) self.reload_qtable(dialog, geom_type, self.plan_om) def reload_qtable(self, dialog, geom_type, plan_om): """ Reload QtableView """ value = utils_giswater.getWidgetText(dialog, dialog.psector_id) sql = ("SELECT * FROM " + self.schema_name + "." + plan_om + "_psector_x_" + geom_type + "" " WHERE psector_id = '" + str(value) + "'") qtable = utils_giswater.getWidget(dialog, 'tbl_psector_x_' + geom_type) self.fill_table_by_query(qtable, sql) self.set_table_columns(dialog, qtable, plan_om + "_psector_x_"+geom_type) self.refresh_map_canvas() def disconnect_snapping(self): """ Select 'Pan' as current map tool and disconnect snapping """ try: self.iface.actionPan().trigger() self.canvas.xyCoordinates.disconnect() if self.emit_point: self.emit_point.canvasClicked.disconnect() except: pass def fill_table_object(self, widget, table_name, expr_filter=None): """ Set a model with selected filter. Attach that model to selected table """ if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.sort(0, 1) if expr_filter: model.setFilter(expr_filter) model.select() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view widget.setModel(model) def filter_by_id(self, dialog, widget_table, widget_txt, table_object, field_object_id='id'): field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" object_id = utils_giswater.getWidgetText(dialog, widget_txt) if object_id != 'null': expr = field_object_id + "::text ILIKE '%" + str(object_id) + "%'" # Refresh model with selected filter widget_table.model().setFilter(expr) widget_table.model().select() else: self.fill_table_object(widget_table, self.schema_name + "." + table_object) def delete_selected_object(self, widget, table_object): """ Delete selected objects of the table (by object_id) """ # Get selected rows selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return inf_text = "" list_id = "" field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" elif "v_ui_om_visitman_x_" in table_object: field_object_id = "visit_id" for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = widget.model().record(row).value(str(field_object_id)) inf_text += str(id_) + ", " list_id = list_id + "'" + str(id_) + "', " inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: sql = ("DELETE FROM " + self.schema_name + "." + table_object + "" " WHERE " + field_object_id + " IN (" + list_id + ")") self.controller.execute_sql(sql, commit=self.autocommit) widget.model().select() def open_selected_object(self, dialog, widget, table_object): """ Open object form with selected record of the table """ selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return row = selected_list[0].row() # Get object_id from selected row field_object_id = "id" widget_id = table_object + "_id" if table_object == "element": field_object_id = table_object + "_id" if table_object == "v_ui_om_visit": widget_id = "visit_id" elif "v_ui_om_visitman_x_" in table_object: field_object_id = "visit_id" selected_object_id = widget.model().record(row).value(field_object_id) # Close this dialog and open selected object dialog.close() # set previous dialog # if hasattr(self, 'previous_dialog'): if table_object == "doc": self.manage_document() utils_giswater.setWidgetText(self.dlg_add_doc, widget_id, selected_object_id) elif table_object == "element": self.manage_element(new_element_id=False) utils_giswater.setWidgetText(self.dlg_add_element, widget_id, selected_object_id) elif table_object == "v_ui_om_visit": self.manage_visit(visit_id=selected_object_id) elif "v_ui_om_visitman_x_" in table_object: self.manage_visit(visit_id=selected_object_id) def set_selectionbehavior(self, dialog): # Get objects of type: QTableView widget_list = dialog.findChildren(QTableView) for widget in widget_list: widget.setSelectionBehavior(QAbstractItemView.SelectRows) def hide_generic_layers(self, visible=False): """ Hide generic layers """ layer = self.controller.get_layer_by_tablename("v_edit_arc") if layer: self.iface.legendInterface().setLayerVisible(layer, visible) layer = self.controller.get_layer_by_tablename("v_edit_node") if layer: self.iface.legendInterface().setLayerVisible(layer, visible) layer = self.controller.get_layer_by_tablename("v_edit_connec") if layer: self.iface.legendInterface().setLayerVisible(layer, visible) layer = self.controller.get_layer_by_tablename("v_edit_element") if layer: self.iface.legendInterface().setLayerVisible(layer, visible) if self.project_type == 'ud': layer = self.controller.get_layer_by_tablename("v_edit_gully") if layer: self.iface.legendInterface().setLayerVisible(layer, visible) def connect_signal_selection_changed(self, dialog, table_object, query=False): """ Connect signal selectionChanged """ try: self.canvas.selectionChanged.connect(partial(self.selection_changed, dialog, table_object, self.geom_type, query)) except Exception: pass def disconnect_signal_selection_changed(self): """ Disconnect signal selectionChanged """ try: self.canvas.selectionChanged.disconnect() except Exception: pass def fill_widget_with_fields(self, dialog, data_object, field_names): """Fill the Widget with value get from data_object limited to the list of field_names.""" for field_name in field_names: value = getattr(data_object, field_name) if not hasattr(dialog, field_name): continue widget = getattr(dialog, field_name) if type(widget) in [QDateEdit, QDateTimeEdit]: widget.setDateTime(value if value else QDate.currentDate() ) if type(widget) in [QLineEdit, QTextEdit]: if value: widget.setText(value) else: widget.clear() if type(widget) in [QComboBox]: if not value: widget.setCurrentIndex(0) continue # look the value in item text index = widget.findText(str(value)) if index >= 0: widget.setCurrentIndex(index) continue # look the value in itemData index = widget.findData(value) if index >= 0: widget.setCurrentIndex(index) continue def set_model_to_table(self, widget, table_name, expr_filter): """ Set a model with selected filter. Attach that model to selected table """ # Set model model = QSqlTableModel(); model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.setFilter(expr_filter) model.select() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view if widget: widget.setModel(model) else: self.controller.log_info("set_model_to_table: widget not found")
class FlyingTrackerDialog(QtGui.QDockWidget, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" super(FlyingTrackerDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.course_comboBox.clear() self.lcdNumberWpt.display(1) self.GpsFixlcdNumber.display(0) self.SatelliteslcdNumber.display(0) self.pushButtonDisconnect.hide() self.pushButtonConnect.clicked.connect(self.Connect) self.pushButtonDisconnect.clicked.connect(self.Disconnect) self.pushCloseButton.clicked.connect(self.Close) self.LoadtoolButton.clicked.connect(self.FillComboBox) self.wptplus_toolButton.clicked.connect(self.NextWpt) self.wptmin_toolButton.clicked.connect(self.BackWpt) QtCore.QObject.connect(self.wptplus_toolButton, QtCore.SIGNAL("valueChanged(int)"), self.AdjNxtWpt) QtCore.QObject.connect(self.wptmin_toolButton, QtCore.SIGNAL("valueChanged(int)"), self.AdjNxtWpt) self.ZoomIntoolButton.clicked.connect(self.ZoomIn) self.ZoomOuttoolButton.clicked.connect(self.ZoomOut) shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_S), self.iface.mainWindow()) shortcut.setContext(QtCore.Qt.ApplicationShortcut) shortcut.activated.connect(self.ZoomIn) shortcut2 = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_A), self.iface.mainWindow()) shortcut2.setContext(QtCore.Qt.ApplicationShortcut) shortcut2.activated.connect(self.ZoomOut) shortcut3 = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_X), self.iface.mainWindow()) shortcut3.setContext(QtCore.Qt.ApplicationShortcut) shortcut3.activated.connect(self.NextWpt) shortcut4 = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Z), self.iface.mainWindow()) shortcut4.setContext(QtCore.Qt.ApplicationShortcut) shortcut4.activated.connect(self.BackWpt) self.timer = QtCore.QTimer() QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.SendSerial) self.NxtWptRubber = False self.PositionMarker = False self.RubberBand = False self.WptVertexSignal = False self.trackCounter = 4 def AdjNxtWpt(self): if self.WptVertexSignal == True: self.iface.mapCanvas().scene().removeItem(self.WptVertex) del self.WptVertex self.WptVertexSignal = False if self.NxtWptRubber == True: self.iface.mapCanvas().scene().removeItem(self.rdue) del self.rdue self.iface.mapCanvas().scene().removeItem(self.rtre) del self.rtre self.NxtWptRubber = False canvas = self.iface.mapCanvas() mapRenderer = canvas.mapRenderer() crsSrc = QgsCoordinateReferenceSystem(4326) # NMEA is in WGS 84 crsDest = mapRenderer.destinationCrs() xform = QgsCoordinateTransform(crsSrc, crsDest) WptValue = int(self.lcdNumberWpt.value()) try: Wpt = self.pts[WptValue - 1] except IndexError: return self.WptVertex = QgsVertexMarker(self.iface.mapCanvas()) self.WptVertex.setIconSize(20) self.WptVertex.setIconType(QgsVertexMarker.ICON_X) self.WptVertex.setPenWidth(20) self.WptVertex.setCenter(xform.transform(Wpt)) self.WptVertexSignal = True if WptValue != 1: Wpt2 = self.pts[WptValue - 2] self.rdue = QgsRubberBand(self.iface.mapCanvas(), False) # False = not a polygon pointsdue = [xform.transform(Wpt), xform.transform(Wpt2)] self.rdue.setColor(QtGui.QColor(255, 0, 0)) self.rdue.setWidth(8) self.rdue.setLineStyle(QtCore.Qt.PenStyle(QtCore.Qt.DotLine)) self.rdue.setToGeometry(QgsGeometry.fromPolyline(pointsdue), None) #creation of NextWayPoint rubberband self.rtre = QgsRubberBand(self.iface.mapCanvas(), False) pointstre = self.pts[0:WptValue - 1] for i in xrange(len(pointstre)): pointstre[i] = xform.transform(pointstre[i]) self.rtre.setColor(QtGui.QColor(127, 0, 255)) self.rtre.setWidth(8) self.rtre.setLineStyle(QtCore.Qt.PenStyle(QtCore.Qt.DotLine)) self.rtre.setToGeometry(QgsGeometry.fromPolyline(pointstre), None) self.NxtWptRubber = True def Connect(self): try: self.positionMarker = PositionMarker(self.iface.mapCanvas()) self.PositionMarker = True portName = self.comboBox.currentText() self.ser = serial.Serial(portName, 38400) layername = self.course_comboBox.itemData( self.course_comboBox.currentIndex()) RouteLayer = QgsMapLayerRegistry.instance().mapLayer(layername) RouteLayer.selectAll() feats = RouteLayer.selectedFeatures() RouteLayer.removeSelection() feat = feats[0] geom = feat.geometry() self.pts = geom.asPolyline() SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1]) if SourceIntCRS != 4326: SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1]) SourceCRS = QgsCoordinateReferenceSystem(SourceIntCRS) DestCRS = QgsCoordinateReferenceSystem(4326) xformRouteLayer = QgsCoordinateTransform(SourceCRS, DestCRS) for i in xrange(len(self.pts)): x = self.pts[i][0] y = self.pts[i][ 1] #if track layer is not in WGS84 Geographic every coordinate is transformed TmpPoint = QgsPoint(x, y) Tmp2Point = xformRouteLayer.transform(TmpPoint) self.pts[i] = Tmp2Point self.TrackLayer = QgsVectorLayer("Point?crs=epsg:4326&index=yes", "Flight_track", "memory") self.TrackLayerProvider = self.TrackLayer.dataProvider() self.TrackLayerProvider.addAttributes([ QgsField("id", QtCore.QVariant.Int), QgsField('Time', QtCore.QVariant.String), QgsField('Ele', QtCore.QVariant.String), ]) QgsMapLayerRegistry.instance().addMapLayer(self.TrackLayer) symbols = self.TrackLayer.rendererV2().symbols() symbol = symbols[0] symbol.setColor(QtGui.QColor(0, 255, 0)) self.iface.legendInterface().refreshLayerSymbology(self.TrackLayer) if self.lcdNumberWpt.value() == 0: self.lcdNumberWpt.display(1) elif self.lcdNumberWpt.value() > len(self.pts): self.lcdNumberWpt.display(1) self.InRouteTolerance = float( self.DTrack_spinBox.value() ) #if we are distant from route less than this value path become green, otherwise red self.CompassTolerance = self.DCompass_spinBox.value( ) #if compass to next wpt confront to actual compass diverge less than this value projection of direction become green, otherwise red self.WptArrivedTolerance = float( self.DWpt_spinBox.value() ) #if we pass near a wpt less than this value (in meters) the program will set the next wpt self.EleTolerance = float( self.DHeightspinBox.value() ) #if our elevation diverge from planned elevation more than this value our cursor will be red, otherwise green canvas = self.iface.mapCanvas() mapRenderer = canvas.mapRenderer() crsSrc = QgsCoordinateReferenceSystem(4326) # NMEA is in WGS 84 crsDest = mapRenderer.destinationCrs() self.backxform = QgsCoordinateTransform(crsDest, crsSrc) self.xform = QgsCoordinateTransform( crsSrc, crsDest) #usage: xform.transform(QgsPoint) self.AdjNxtWpt() self.timer.start(1000) except: pass def SendSerial(self): thread = Thread(target=self.ReadSerial) thread.start() thread.join() def Disconnect(self, serialPort): self.timer.stop() self.lcdNumberSpeed.display(0) self.lcdNumberCompass.display(0) try: self.iface.mapCanvas().scene().removeItem(self.WptVertex) self.ser.close() self.iface.mapCanvas().scene().removeItem(self.r) self.iface.mapCanvas().scene().removeItem(self.runo) self.iface.mapCanvas().scene().removeItem(self.positionMarker) del self.WptVertex del self.r del self.runo except: pass self.iface.mapCanvas().setRotation(0) self.RubberBand = False if self.NxtWptRubber == True: self.iface.mapCanvas().scene().removeItem(self.rdue) self.iface.mapCanvas().scene().removeItem(self.rtre) del self.rdue del self.rtre self.NxtWptRubber = False else: pass self.WptVertexSignal = False self.lcdNumberHeights.display(0) self.lcdNumberSpeed.display(0) self.lcdNumberCompass.display(0) self.lcdCompassWpt.display(0) self.GpsFixlcdNumber.display(0) self.SatelliteslcdNumber.display(0) def FillComboBox(self): try: self.comboBox.clear() portlist = [] system_name = platform.system() if system_name == "Windows": # Scan for available ports. available = [] for i in range(256): try: s = serial.Serial(i) available.append(i) s.close() except serial.SerialException: #Search for active serial port pass #print available list1 = available elif system_name == "Darwin": # Mac #print glob.glob('/dev/tty*') + glob.glob('/dev/cu*') list1 = glob.glob('/dev/tty*') + glob.glob('/dev/cu*') else: # Assume Linux or something else #print glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*') list1 = glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*') for i in list1: try: serial.Serial(i).close() portlist.append(i) except IOError: pass for x in portlist: self.comboBox.addItem(x) self.course_comboBox.clear() LayerRegistryItem = QgsMapLayerRegistry.instance().mapLayers() for id, layer in LayerRegistryItem.iteritems(): if layer.type() == QgsMapLayer.VectorLayer: self.course_comboBox.addItem(layer.name(), id) layername = self.course_comboBox.itemData( self.course_comboBox.currentIndex()) RouteLayer = QgsMapLayerRegistry.instance().mapLayer(layername) RouteLayer.selectAll() RouteLayer.featureCount() feats = RouteLayer.selectedFeatures() RouteLayer.removeSelection() feat = feats[0] geom = feat.geometry() self.pts = geom.asPolyline() SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1]) if SourceIntCRS != 4326: SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1]) SourceCRS = QgsCoordinateReferenceSystem(SourceIntCRS) DestCRS = QgsCoordinateReferenceSystem(4326) xformRouteLayer = QgsCoordinateTransform(SourceCRS, DestCRS) for i in xrange(len(self.pts)): x = self.pts[i][0] y = self.pts[i][ 1] #if track layer is not in WGS84 Geographic every coordinate is transformed TmpPoint = QgsPoint(x, y) Tmp2Point = xformRouteLayer.transform(TmpPoint) self.pts[i] = Tmp2Point self.AdjNxtWpt() except: pass def Close(self): self.timer.stop() self.lcdNumberHeights.display(0) self.lcdNumberSpeed.display(0) self.lcdNumberCompass.display(0) self.lcdCompassWpt.display(0) self.course_comboBox.clear() if self.PositionMarker == True: self.iface.mapCanvas().scene().removeItem(self.positionMarker) if self.NxtWptRubber == True: self.iface.mapCanvas().scene().removeItem(self.rdue) self.iface.mapCanvas().scene().removeItem(self.rtre) del self.rdue del self.rtre self.NxtWptRubber = False else: pass if self.WptVertexSignal == True: self.iface.mapCanvas().scene().removeItem(self.WptVertex) del self.WptVertex self.WptVertexSignal = False self.close() def ZoomIn(self): self.iface.mapCanvas().zoomIn() def ZoomOut(self): self.iface.mapCanvas().zoomOut() def NextWpt(self): try: currentValue = self.lcdNumberWpt.value() if currentValue == len(self.pts): pass else: self.lcdNumberWpt.display(currentValue + 1) self.AdjNxtWpt() except: pass def BackWpt(self): try: currentValue = self.lcdNumberWpt.value() if currentValue >= 2: self.lcdNumberWpt.display(currentValue - 1) self.AdjNxtWpt() except: pass def ReadSerial(self): if self.trackCounter > 5: self.trackCounter = 4 self.trackCounter = self.trackCounter + 1 # when it arrive to 5 a gps point is painted (default is 4) GPGGA = 0 GPVTG = 0 data = self.ser.read(1) n = self.ser.inWaiting() if n: data = data + self.ser.read(n) if re.search("\r\n", data): # Since we found a CRLF, split it out data2 = data.split("\r\n") for i in range(len(data2)): if data2[i][0:6] == '$GPGGA': GPGGA = data2[i].split(',') #print GPGGA elif data2[i][0:6] == '$GPVTG': GPVTG = data2[i].split(',') #print GPRMC if GPGGA == 0: #print 'mancato' return elif GPVTG == 0: #print 'mancato' return else: decimalsLat = (float(GPGGA[2][2:])) / 60 degreeLat = float(GPGGA[2][0:2]) decimalsLon = (float(GPGGA[4][3:])) / 60 degreeLon = float(GPGGA[4][0:3]) Lat = degreeLat + decimalsLat Lon = degreeLon + decimalsLon if GPGGA[5] == 'W': Lon = -Lon if GPGGA[3] == 'S': Lat = -Lat Ele = float(GPGGA[9]) Compass = float(GPVTG[1]) Speed = (float(GPVTG[7])) # in Km/h GpsFix = int(GPGGA[6]) GpsSatellites = int(GPGGA[7]) self.ser.flushInput() self.ser.flushOutput() self.GpsFixlcdNumber.display(GpsFix) self.SatelliteslcdNumber.display(GpsSatellites) if self.RubberBand == True: self.iface.mapCanvas().scene().removeItem(self.r) del self.r self.RubberBand = False Point = QgsPoint() Point.set(Lon, Lat) TransfPoint = self.xform.transform(Point) canvas = self.iface.mapCanvas() if Compass <= 180: #canvas.setRotation(-(Compass-self.rotation)) canvas.setRotation( -Compass ) # set canvas rotation according to: UP of the map = Compass Direction else: Compass = 360 - Compass canvas.setRotation(Compass) canvas.setCenter(TransfPoint) self.positionMarker.newCoords( TransfPoint) # Put the arrow on screen #self.positionMarker.angle = 0.0 WptValue = int(self.lcdNumberWpt.value()) WptE = self.pts[WptValue - 1][0] WptN = self.pts[WptValue - 1][1] GeodesicAircraftToWpt = Geodesic.WGS84.Inverse( Lat, Lon, WptN, WptE) distance = GeodesicAircraftToWpt['s12'] azim = GeodesicAircraftToWpt[ 'azi1'] #determine azimuth from next wpt if azim < 0: azim += 360 if distance <= self.WptArrivedTolerance: # tolerance in meter for next wpt self.NextWpt() #feetEle = Ele * 3.2808399 #meters to feet if self.comboBox_2.currentText() == 'ft.': feetEle = Ele * 3.2808399 #Convert if needed self.lcdNumberHeights.display(feetEle) else: self.lcdNumberHeights.display(Ele) if self.comboBox_3.currentText() != 'km/h': Speed = Speed * 0.53995694 #Convert if needed self.lcdNumberSpeed.display(float(Speed)) else: self.lcdNumberSpeed.display(float(Speed)) self.lcdNumberCompass.display(float(Compass)) self.lcdCompassWpt.display(azim) canvasInPixel = canvas.getCoordinateTransform() ExtentHeightInPixel = canvasInPixel.mapHeight() ExtentWidthInPixel = canvasInPixel.mapWidth() LocateCompassProjectionEndInMapUnit = canvasInPixel.toMapPoint( ExtentWidthInPixel / 2.0, ExtentHeightInPixel - (ExtentHeightInPixel * 0.95)) self.r = QgsRubberBand(self.iface.mapCanvas(), False) # False = not a polygon #points = [TransfPoint, QgsPoint(x,y)] #creazione della proiezione della prua su mappa points = [TransfPoint, LocateCompassProjectionEndInMapUnit] self.r.setWidth(8) self.r.setToGeometry(QgsGeometry.fromPolyline(points), None) if abs(Compass - azim) <= self.CompassTolerance: #Compass tolerance self.r.setColor(QtGui.QColor(0, 255, 0)) else: self.r.setColor(QtGui.QColor(255, 0, 0)) self.RubberBand = True try: self.iface.mapCanvas().scene().removeItem( self.runo) # remove track for first waypoint except: pass if WptValue != 1: #DistanceFromLineTolerance = 100 #meter set distance from route BackwardLat = self.rdue.asGeometry().asPolyline()[1][ 1] #start to design a QgsRectangle buffer around current route to confront to Point BackwardLon = self.rdue.asGeometry().asPolyline()[1][0] BackwardPoint = QgsPoint(BackwardLon, BackwardLat) BackwardPointTransformed = self.backxform.transform( BackwardPoint) GeodesicWptWpt = Geodesic.WGS84.Inverse( BackwardPointTransformed.y(), BackwardPointTransformed.x(), WptN, WptE) #GeodesicWptWpt = Geodesic.WGS84.Inverse(BackwardLat, BackwardLon, WptN, WptE) WptWptCompass = GeodesicWptWpt['azi1'] if WptWptCompass < 0: WptWptCompass += 360 #print WptWptCompass WptWptCompassRight = WptWptCompass + 90 if WptWptCompassRight > 360: WptWptCompassRight = WptWptCompassRight - 360 #print WptWptCompassRight WptWptCompassLeft = WptWptCompass - 90 if WptWptCompassLeft < 0: WptWptCompassLeft += 360 #print WptWptCompassLeft origin = geopy.Point(WptN, WptE) URBufferVertex = vincenty( meters=self.InRouteTolerance).destination( origin, WptWptCompassRight) URBufferVertexPoint = QgsPoint(URBufferVertex.longitude, URBufferVertex.latitude) ULBufferVertex = vincenty( meters=self.InRouteTolerance).destination( origin, WptWptCompassLeft) ULBufferVertexPoint = QgsPoint(ULBufferVertex.longitude, ULBufferVertex.latitude) del origin origin = geopy.Point(BackwardPointTransformed.y(), BackwardPointTransformed.x()) DRBufferVertex = vincenty( meters=self.InRouteTolerance).destination( origin, WptWptCompassRight) DRBufferVertexPoint = QgsPoint(DRBufferVertex.longitude, DRBufferVertex.latitude) DLBufferVertex = vincenty( meters=self.InRouteTolerance).destination( origin, WptWptCompassLeft) DLBufferVertexPoint = QgsPoint(DLBufferVertex.longitude, DLBufferVertex.latitude) del origin gPolygon = QgsGeometry.fromPolygon([[ URBufferVertexPoint, ULBufferVertexPoint, DLBufferVertexPoint, DRBufferVertexPoint ]]) if not gPolygon.contains(Point): self.rdue.setColor(QtGui.QColor(255, 0, 0)) #print 'noncontiene' else: self.rdue.setColor(QtGui.QColor(0, 255, 0)) #print 'contiene' else: self.runo = QgsRubberBand(self.iface.mapCanvas(), False) points = [TransfPoint, self.xform.transform(self.pts[0]) ] # draw track for first waypoint self.runo.setColor(QtGui.QColor(255, 0, 0)) self.runo.setWidth(6) self.runo.setLineStyle( QtCore.Qt.PenStyle(QtCore.Qt.DotLine)) self.runo.setToGeometry(QgsGeometry.fromPolyline(points), None) #if abs(float((self.fixedHeightspinBox.value()/ 3.2808399)) - (feetEle/ 3.2808399)) <= self.EleTolerance: #ele tolerance expressed in meters if abs(self.fixedHeightspinBox.value() - Ele) <= self.EleTolerance: self.positionMarker.setHasPosition( True) #True or False to change color else: self.positionMarker.setHasPosition(False) if self.trackCounter == 5: #pass fc = int(self.TrackLayerProvider.featureCount()) time = str(GPGGA[1])[0:2] + ':' + str( GPGGA[1])[2:4] + ':' + str( GPGGA[1])[4:6] # timestamp for GPX layer feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPoint(Point)) feature.setAttributes([fc, time, Ele]) self.TrackLayer.startEditing() self.TrackLayer.addFeature(feature, True) self.TrackLayer.commitChanges() self.TrackLayer.setCacheImage(None) self.TrackLayer.triggerRepaint() self.trackCounter = 0 return
class DrawMonoLineMapTool(QgsMapToolEmitPoint): azimuth_calcul = pyqtSignal(QgsPoint, QgsPoint) def __init__(self, canvas): self.canvas = canvas s = QSettings() s.beginGroup('qgis') color = QColor(int(s.value('default_measure_color_red', 222)), int(s.value('default_measure_color_green', 17)), int(s.value('default_measure_color_blue', 28))) s.endGroup() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDraw = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDraw.setColor(color) self.rubberBandDraw.setWidth(1) self.rubberBand.setColor(color) self.rubberBand.setWidth(1) # self.rubberBand.setLineStyle(Qt.DashLine) self.points = [] self.vertex = None self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QgsWkbTypes.LineGeometry) self.rubberBandDraw.reset(QgsWkbTypes.LineGeometry) def canvasPressEvent(self, e): self.isEmittingPoint = False def canvasReleaseEvent(self, e): self.isEmittingPoint = True pt = self.snappoint(e.originalPixelPoint()) self.startPoint = pt if len(self.points) < 2: self.rubberBandDraw.reset(QgsWkbTypes.LineGeometry) self.rubberBand.reset(QgsWkbTypes.LineGeometry) self.points.append(self.startPoint) if len(self.points) == 2: self.rubberBandDraw.setToGeometry( QgsGeometry.fromPolyline([ QgsPoint(self.points[0].x(), self.points[0].y()), QgsPoint(self.points[1].x(), self.points[1].y()) ]), None) self.points = [] self.isEmittingPoint = False def canvasMoveEvent(self, e): self.snappoint(e.originalPixelPoint()) # input is QPoint if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) if len(self.points) > 0: start = QgsPoint(self.startPoint.x(), self.startPoint.y()) end = QgsPoint(self.endPoint.x(), self.endPoint.y()) geom = QgsGeometry.fromPolyline([start, end]) self.rubberBand.setToGeometry(geom, None) if ((self.startPoint is not None and self.endPoint is not None and self.startPoint != self.endPoint)): self.azimuth_calcul.emit(start, end) def activate(self): self.reset() super(DrawMonoLineMapTool, self).activate() self.snapcolor = QgsSettings().value("/qgis/digitizing/snap_color", QColor(Qt.magenta)) self.activated.emit() def deactivate(self): self.reset() super(DrawMonoLineMapTool, self).deactivate() self.removeVertexMarker() self.deactivated.emit() def removeVertexMarker(self): if self.vertex is not None: self.canvas.scene().removeItem(self.vertex) self.vertex = None def snappoint(self, qpoint): match = self.canvas.snappingUtils().snapToMap(qpoint) if match.isValid(): if self.vertex is None: self.vertex = QgsVertexMarker(self.canvas) self.vertex.setIconSize(12) self.vertex.setPenWidth(2) self.vertex.setColor(self.snapcolor) self.vertex.setIconType(QgsVertexMarker.ICON_BOX) self.vertex.setCenter(match.point()) return (match.point()) # Returns QgsPointXY else: self.removeVertexMarker() return self.toMapCoordinates( qpoint) # QPoint input, returns QgsPointXY
class StartDrawing(QgsMapToolEmitPoint): def __init__(self, canvas, iface): QgsMapToolEmitPoint.__init__(self, canvas) # qgis interface self.canvas = canvas self.iface = iface # snap marker self.snap_mark = QgsVertexMarker(self.canvas) self.snap_mark.setColor(QColor(0, 0, 255)) self.snap_mark.setPenWidth(2) self.snap_mark.setIconType(QgsVertexMarker.ICON_BOX) self.snap_mark.setIconSize(10) # rectangle self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.GeometryType(3)) self.rubberBand.setWidth(3) self.rubberBand.setStrokeColor(QColor(254, 0, 0)) # initialize variable self.rubberBand_width = 0 self.rubberBand_height = 0 self.rubberBand_angle = 0 self.reset() def setConfiguration(self, width, height, angle): self.rubberBand_width = width self.rubberBand_height = height self.rubberBand_angle = angle def reset(self): self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) self.isEmittingPoint = False def canvasMoveEvent(self, e): self.snap_mark.hide() self.snapPoint = False self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) self.snapPoint = self.checkSnapToPoint(e.pos()) if self.snapPoint[0]: self.snap_mark.setCenter(self.snapPoint[1]) self.snap_mark.show() self.rectangle = self.getRectangle(self.snapPoint[1]) self.rubberBand.setToGeometry(self.rectangle, None) self.rubberBand.show() else: self.rectangle = self.getRectangle(self.toMapCoordinates(e.pos())) self.rubberBand.setToGeometry(self.rectangle, None) self.rubberBand.show() def checkSnapToPoint(self, point): snapped = False snap_point = self.toMapCoordinates(point) snapper = self.canvas.snappingUtils() snapMatch = snapper.snapToMap(point) if snapMatch.hasVertex(): snap_point = snapMatch.point() snapped = True return snapped, snap_point def canvasPressEvent(self, e): if self.snapPoint == False: point = self.toMapCoordinates(self.canvas.mouseLastXY()) else: point = self.snapPoint[1] layer = self.iface.activeLayer() layer_crs = layer.crs().authid() layer_crs = QgsCoordinateReferenceSystem(layer_crs) canvas_crs = self.canvas.mapSettings().destinationCrs().authid() canvas_crs = QgsCoordinateReferenceSystem(canvas_crs) crs2crs = QgsCoordinateTransform(canvas_crs, layer_crs, QgsProject.instance()) feature = QgsFeature() fields = layer.fields() feature.setFields(fields) if layer.wkbType() == QgsWkbTypes.Polygon or layer.wkbType( ) == QgsWkbTypes.MultiPolygon: if layer_crs == canvas_crs: feature.setGeometry(self.rectangle) else: geom = self.rectangle geom.transform(crs2crs) feature.setGeometry(geom) layer.startEditing() layer.addFeature(feature) layer.commitChanges() layer.reload() else: self.iface.messageBar().pushCritical( 'QRectangle Creator: ', 'The current layer is not of Polygon or MultiPolygon type. The object has not been added' ) def getRectangle(self, point): polygon = QgsWkbTypes.GeometryType(3) x = point.x() y = point.y() points = [[ QgsPointXY( # Left Top corner x - (self.rubberBand_width / 2), y + (self.rubberBand_height / 2)), QgsPointXY( # Right Top corner x + (self.rubberBand_width / 2), y + (self.rubberBand_height / 2)), QgsPointXY( # Right Down corner x + (self.rubberBand_width / 2), y - (self.rubberBand_height / 2)), QgsPointXY( # Left Down corner x - (self.rubberBand_width / 2), y - (self.rubberBand_height / 2)) ]] polygon = QgsGeometry.fromPolygonXY(points) polygon.rotate(self.rubberBand_angle, point) return polygon
class DeleteTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.elev = -1 self.vertex_marker = QgsVertexMarker(self.canvas()) # Stroke self.rubber_band = QgsRubberBand(self.data_dock.iface.mapCanvas()) self.rubber_band.setColor(QColor(255, 0, 0, 255)) self.rubber_band.setWidth(2) self.mouse_clicked = False self.snapper = None self.clicked_pt = None self.snap_results = None self.adj_links_fts = None self.selected_node_ft = None self.selected_node_ft_lay = None self.mouse_pt = None self.pump_valve_selected = False self.pump_or_valve = None self.pump_valve_ft = None self.adj_junctions = None self.delta_vec = None self.adj_pipes_fts_d = {} def canvasPressEvent(self, event): # if self.snap_results is None: # return if event.button() == Qt.RightButton: self.mouse_clicked = False self.clicked_pt = None if event.button() == Qt.LeftButton: self.mouse_clicked = True self.clicked_pt = self.toMapCoordinates(event.pos()) def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) if elev is not None: self.elev = elev self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) # Mouse not clicked if not self.mouse_clicked: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): self.snap_results = match # snapped_pt = self.snap_results[0].snappedVertex self.snapped_pipe_id = match.featureId() snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter( QgsPointXY(snapped_vertex.x(), snapped_vertex.y())) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType( QgsVertexMarker.ICON_CIRCLE) # or ICON_CROSS, ICON_X self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: self.snap_results = None self.selected_node_ft = None self.vertex_marker.hide() # Mouse clicked: draw rectangle else: if self.snap_results is None: end_point = self.toMapCoordinates(event.pos()) self.show_rect(self.clicked_pt, end_point) def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # Snapped: one element selected if self.snap_results is not None: selected_node_ft_lay, selected_node_ft = vector_utils.findSnappedNode( self.snapper, self.snap_results, self.params) # A node ha been snapped if selected_node_ft_lay is not None: self.delete_element(selected_node_ft_lay, selected_node_ft) # A link has been snapped else: snapped_ft = vector_utils.get_feats_by_id( self.snap_results.layer(), self.snap_results.featureId()) snapped_layer = self.snap_results.layer() self.delete_element(snapped_layer, snapped_ft[0]) # Not snapped: rectangle else: rubber_band_rect = self.rubber_band.asGeometry().boundingBox() self.rubber_band.reset() self.delete_elements(self.params.valves_vlay, rubber_band_rect) self.delete_elements(self.params.pumps_vlay, rubber_band_rect) self.delete_elements(self.params.pipes_vlay, rubber_band_rect) self.delete_elements(self.params.tanks_vlay, rubber_band_rect) self.delete_elements(self.params.reservoirs_vlay, rubber_band_rect) self.delete_elements(self.params.junctions_vlay, rubber_band_rect) # Refresh symbology.refresh_layer(self.iface.mapCanvas(), self.params.junctions_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.reservoirs_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.tanks_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pipes_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pumps_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.valves_vlay) def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) layers = { self.params.junctions_vlay: QgsSnappingConfig.Vertex, self.params.reservoirs_vlay: QgsSnappingConfig.Vertex, self.params.tanks_vlay: QgsSnappingConfig.Vertex, self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment } self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) self.snapper.toggleEnabled() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.reservoirs_vlay.isEditable(): self.params.reservoirs_vlay.startEditing() if not self.params.tanks_vlay.isEditable(): self.params.tanks_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.pumps_vlay.isEditable(): self.params.pumps_vlay.startEditing() if not self.params.valves_vlay.isEditable(): self.params.valves_vlay.startEditing() def deactivate(self): self.data_dock.btn_delete_element.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def show_rect(self, start_point, end_point): self.rubber_band.reset() if start_point.x() == end_point.x() or start_point.y() == end_point.y( ): return point1 = QgsPointXY(start_point.x(), start_point.y()) point2 = QgsPointXY(start_point.x(), end_point.y()) point3 = QgsPointXY(end_point.x(), end_point.y()) point4 = QgsPointXY(end_point.x(), start_point.y()) point5 = QgsPointXY(start_point.x(), start_point.y()) self.rubber_band.addPoint(point1, False) self.rubber_band.addPoint(point2, False) self.rubber_band.addPoint(point3, False) self.rubber_band.addPoint(point4, False) self.rubber_band.closePoints(True) # true to update canvas self.rubber_band.show() def delete_elements(self, layer, rectangle): try: QApplication.setOverrideCursor(Qt.WaitCursor) feats = layer.getFeatures() for feat in feats: if rectangle.contains(feat.geometry().boundingBox()): self.delete_element(layer, feat) finally: QApplication.restoreOverrideCursor() def delete_element(self, layer, feature): # If node if layer == self.params.junctions_vlay or \ layer == self.params.reservoirs_vlay or \ layer == self.params.tanks_vlay: # The node is a junction if layer == self.params.junctions_vlay: adj_links_fts = NetworkUtils.find_adjacent_links( self.params, feature.geometry()) # Only pipes adjacent to node: it's a simple junction if not adj_links_fts['pumps'] and not adj_links_fts['valves']: # Delete node NodeHandler.delete_node(self.params, layer, feature) # Delete adjacent pipes adj_pipes = NetworkUtils.find_adjacent_links( self.params, feature.geometry()) for adj_pipe in adj_pipes['pipes']: LinkHandler.delete_link(self.params.pipes_vlay, adj_pipe) # The node is part of a pump or valve else: if adj_links_fts['pumps']: LinkHandler.delete_link(self.params, self.params.pumps_vlay, feature) elif adj_links_fts['valves']: LinkHandler.delete_link(self.params, self.params.valves_vlay, feature) # The node is a reservoir or a tank elif layer == self.params.reservoirs_vlay or \ layer == self.params.tanks_vlay: adj_pipes = NetworkUtils.find_adjacent_links( self.params, feature.geometry())['pipes'] NodeHandler._delete_feature(self.params, layer, feature) for adj_pipe in adj_pipes: LinkHandler.delete_link(self.params, self.params.pipes_vlay, adj_pipe) # If pipe elif layer == self.params.pipes_vlay: if self.snap_results is not None: vertex = feature.geometry().closestVertexWithContext( self.snap_results.point()) vertex_dist = vertex[0] if vertex_dist < self.params.min_dist: # Delete vertex LinkHandler.delete_vertex(self.params, self.params.pipes_vlay, feature, vertex[1]) else: # Delete whole feature LinkHandler.delete_link(self.params, layer, feature) else: LinkHandler.delete_link(self.params, layer, feature)
class ArkMapToolInteractive(QgsMapTool): _active = False _dragging = False _panningEnabled = False _zoomingEnabled = False _zoomRubberBand = None #QgsRubberBand() _zoomRect = None # QRect() _snappingEnabled = False _snapper = None #QgsMapCanvasSnapper() _snappingMarker = None # QgsVertexMarker() _showSnappableVertices = False _snappableVertices = [] # [QgsPoint()] _snappableMarkers = [] # [QgsVertexMarker()] def __init__(self, canvas, snappingEnabled=False, showSnappableVertices=False): super(ArkMapToolInteractive, self).__init__(canvas) self._snappingEnabled = snappingEnabled self._showSnappableVertices = showSnappableVertices def __del__(self): if self._active: self.deactivate() def isActive(self): return self._active def activate(self): super(ArkMapToolInteractive, self).activate() self._active = True self._startSnapping() def deactivate(self): self._active = False if self._snappingEnabled: self._stopSnapping() if (self._zoomRubberBand is not None): self.canvas().scene().removeItem(self._zoomRubberBand) self._zoomRubberBand = None super(ArkMapToolInteractive, self).deactivate() def setAction(self, action): super(ArkMapToolInteractive, self).setAction(action) self.action().triggered.connect(self._activate) def _activate(self): self.canvas().setMapTool(self) def panningEnabled(self): return self._panningEnabled def setPanningEnabled(self, enabled): self._panningEnabled = enabled def zoomingEnabled(self): return self._zoomingEnabled def setZoomingEnabled(self, enabled): self._zoomingEnabled = enabled def snappingEnabled(self): return self._snappingEnabled def setSnappingEnabled(self, enabled): if (self._snappingEnabled == enabled): return self._snappingEnabled = enabled if not self._active: return if enabled: self._startSnapping() else: self._stopSnapping() def _startSnapping(self): self._snapper = QgsMapCanvasSnapper() self._snapper.setMapCanvas(self.canvas()) if self._showSnappableVertices: self._startSnappableVertices() def _stopSnapping(self): self._deleteSnappingMarker() self._snapper = None if self._showSnappableVertices: self._stopSnappableVertices() def showSnappableVertices(self): return self._showSnappableVertices def setShowSnappableVertices(self, show): if (self._showSnappableVertices == show): return self._showSnappableVertices = show if not self._active: return if show: self._startSnappableVertices() else: self._stopSnappableVertices() def _startSnappableVertices(self): self.canvas().layersChanged.connect(self._layersChanged) self.canvas().extentsChanged.connect(self._redrawSnappableMarkers) QgsProject.instance().snapSettingsChanged.connect(self._layersChanged) self._layersChanged() def _stopSnappableVertices(self): self._deleteSnappableMarkers() self._snappableLayers = [] self.canvas().layersChanged.disconnect(self._layersChanged) self.canvas().extentsChanged.disconnect(self._redrawSnappableMarkers) QgsProject.instance().snapSettingsChanged.disconnect(self._layersChanged) def canvasMoveEvent(self, e): super(ArkMapToolInteractive, self).canvasMoveEvent(e) if not self._active: return e.ignore() if (self._panningEnabled and e.buttons() & Qt.LeftButton): # Pan map mode if not self._dragging: self._dragging = True self.setCursor(QCursor(Qt.ClosedHandCursor)) self.canvas().panAction(e) e.accept() elif (self._zoomingEnabled and e.buttons() & Qt.RightButton): # Zoom map mode if not self._dragging: self._dragging = True self.setCursor(QCursor(Qt.ClosedHandCursor)) self._zoomRubberBand = QgsRubberBand(self.canvas(), QGis.Polygon) color = QColor(Qt.blue) color.setAlpha(63) self._zoomRubberBand.setColor(color) self._zoomRect = QRect(0, 0, 0, 0) self._zoomRect.setTopLeft(e.pos()) self._zoomRect.setBottomRight(e.pos()) if self._zoomRubberBand is not None: self._zoomRubberBand.setToCanvasRectangle(self._zoomRect) self._zoomRubberBand.show() e.accept() elif self._snappingEnabled: mapPoint, snapped = self._snapCursorPoint(e.pos()) if (snapped): self._createSnappingMarker(mapPoint) else: self._deleteSnappingMarker() def canvasReleaseEvent(self, e): super(ArkMapToolInteractive, self).canvasReleaseEvent(e) e.ignore() if (e.button() == Qt.LeftButton): if self._dragging: # Pan map mode self.canvas().panActionEnd(e.pos()) self.setCursor(capture_point_cursor) self._dragging = False e.accept() elif (e.button() == Qt.RightButton): if self._dragging: # Zoom mode self._zoomRect.setBottomRight(e.pos()) if (self._zoomRect.topLeft() != self._zoomRect.bottomRight()): coordinateTransform = self.canvas().getCoordinateTransform() ll = coordinateTransform.toMapCoordinates(self._zoomRect.left(), self._zoomRect.bottom()) ur = coordinateTransform.toMapCoordinates(self._zoomRect.right(), self._zoomRect.top()) r = QgsRectangle() r.setXMinimum(ll.x()) r.setYMinimum(ll.y()) r.setXMaximum(ur.x()) r.setYMaximum(ur.y()) r.normalize() if (r.width() != 0 and r.height() != 0): self.canvas().setExtent(r) self.canvas().refresh() self._dragging = False if (self._zoomRubberBand is not None): self.canvas().scene().removeItem(self._zoomRubberBand) self._zoomRubberBand = None e.accept() def keyPressEvent(self, e): super(ArkMapToolInteractive, self).keyPressEvent(e) if (e.key() == Qt.Key_Escape): self.canvas().unsetMapTool(self) e.accept() def _snapCursorPoint(self, cursorPoint): res, snapResults = self._snapper.snapToBackgroundLayers(cursorPoint) if (res != 0 or len(snapResults) < 1): return self.toMapCoordinates(cursorPoint), False else: # Take a copy as QGIS will delete the result! snappedVertex = QgsPoint(snapResults[0].snappedVertex) return snappedVertex, True def _createSnappingMarker(self, snapPoint): if (self._snappingMarker is None): self._snappingMarker = QgsVertexMarker(self.canvas()) self._snappingMarker.setIconType(QgsVertexMarker.ICON_CROSS) self._snappingMarker.setColor(Qt.magenta) self._snappingMarker.setPenWidth(3) self._snappingMarker.setCenter(snapPoint) def _deleteSnappingMarker(self): if (self._snappingMarker is not None): self.canvas().scene().removeItem(self._snappingMarker) self._snappingMarker = None def _createSnappableMarkers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return extent = self.canvas().extent() for vertex in self._snappableVertices.asMultiPoint(): if (extent.contains(vertex)): marker = QgsVertexMarker(self.canvas()) marker.setIconType(QgsVertexMarker.ICON_X) marker.setColor(Qt.gray) marker.setPenWidth(1) marker.setCenter(vertex) self._snappableMarkers.append(marker) def _deleteSnappableMarkers(self): for marker in self._snappableMarkers: self.canvas().scene().removeItem(marker) del self._snappableMarkers[:] def _layersChanged(self): if (not self._showSnappableVertices or not self._snappingEnabled): return self._buildSnappableLayers() self._deleteSnappableMarkers() self._createSnappableMarkers() def _redrawSnappableMarkers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return self._deleteSnappableMarkers() self._createSnappableMarkers() def _buildSnappableLayers(self): if (not self._showSnappableVertices or not self._snappingEnabled): return vertices = [] for layer in self.canvas().layers(): ok, enabled, type, units, tolerance, avoid = QgsProject.instance().snapSettingsForLayer(layer.id()) if (ok and enabled and not layer.isEditable()): for feature in layer.getFeatures(): geometry = feature.geometry() if geometry is None: pass elif geometry.type() == QGis.Point: vertices.extend([geometry.asPoint()]) elif geometry.type() == QGis.Line: vertices.extend(geometry.asPolyline()) elif geometry.type() == QGis.Polygon: lines = geometry.asPolygon() for line in lines: vertices.extend(line) self._snappableVertices = QgsGeometry.fromMultiPoint(vertices) self._snappableVertices.simplify(0)
class LineMapTool(MapTool, QgsMapToolEmitPoint): ''' draw a line on the map (connected, multiple sections) Attributes ---------- drawn : pyqtSignal emitted after double click or right click on the map canvas, emits the drawn line ''' drawn = pyqtSignal(QgsGeometry) wkbtype = QgsWkbTypes.LineGeometry def __init__(self, ui_element: QWidget, canvas: QgsMapCanvas = None, color: Union[str, int] = None, draw_markers=False, line_width: int = 2, line_style: int = Qt.SolidLine, snap_geometry: QgsGeometry = None, target_epsg: int = 25832): ''' Parameters ---------- ui_element : QWidget clickable UI element, clicking it will activate/deactivate this tool canvas : QgsMapCanvas, optional the map canvas the tool will work on, defaults to the map canvas of the QGIS UI color : int or str, optional color description, sets color of line and markers while drawing, defaults to blue draw_markers : bool, optional draw markers between segments of the line, defaults to no markers line_width : int, optional width of drawn lines in pixels, defaults to 2 pixels line_style : int, optional style of drawn lines (e.g. Qt.DashDotLine), defaults to solid line snap_geometry : QgsGeometry, optional snap drawn lines to outline of given geometry, defaults to no snapping target_epsg : int, optional projection of emitted geometry after drawing as epsg code, defaults to 25832 ''' self.canvas = canvas MapTool.__init__(self, ui_element, self.canvas) QgsMapToolEmitPoint.__init__(self, canvas=self.canvas) self.rubberband = QgsRubberBand(self.canvas, self.wkbtype) self.color = QColor(color) if color else QColor(0, 0, 255) self.rubberband.setColor(self.color) self.color.setAlpha(100) self.rubberband.setFillColor(self.color) self.rubberband.setLineStyle(line_style) self.rubberband.setWidth(line_width) self.snap_geometry = self.set_snap_geometry(snap_geometry) self.draw_markers = draw_markers if self.draw_markers: # auto points on outline should but doesn't work: #self.drawing_lines.setIcon(QgsRubberBand.ICON_CIRCLE) #self.drawing_lines.setIconSize(8) # drawing markers manually instead self.markers = [] self._drawing = False self._moving = False # marker for showing snapped point on move self._move_marker = QgsVertexMarker(self.canvas) self._move_marker.setColor(self.color) self._move_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self._move_marker.setIconSize(10) self._move_marker.setPenWidth(3) self.reset() def set_snap_geometry(self, geom: QgsGeometry): ''' snap lines to outline of given geometry Parameters ---------- snap_geometry : QgsGeometry geometry to snap lines to ''' if not geom: return if SHAPELY_LOADED: self.snap_geometry = wkt.loads(geom.asWkt()).boundary # alternative for MacOS else: self.snap_geometry = QgsCurvePolygon() self.snap_geometry.fromWkt(geom.asWkt()) def reset(self): ''' reset drawing ''' scene = self.canvas.scene() if self.draw_markers: for m in self.markers: scene.removeItem(m) self.markers = [] self._moving = False self._drawing = False self.rubberband.reset(self.wkbtype) def canvasDoubleClickEvent(self, e): ''' override, emit line on double click ''' if self._moving: self.rubberband.removeLastPoint() geom = self.rubberband.asGeometry() self.drawn.emit(self.transform_from_map(geom)) self.reset() def _snap(self, point: QgsPoint) -> QgsPointXY: ''' snap point to snap-geometry ''' point = self.transform_from_map(point) if SHAPELY_LOADED: p = geometry.Point(point.x(), point.y()) np = nearest_points(self.snap_geometry, p)[0] p = QgsPointXY(np.x, np.y) # alternative for MacOS else: closest = QgsGeometryUtils.closestPoint( self.snap_geometry, QgsPoint(point.x(), point.y())) p = QgsPointXY(closest.x(), closest.y()) p = self.transform_to_map(p) return p def canvasPressEvent(self, e): ''' override, finish line segment when map is clicked ''' if (e.button() == Qt.RightButton): if self._moving: self.rubberband.removeLastPoint() geom = self.rubberband.asGeometry() self.drawn.emit(self.transform_from_map(geom)) self.reset() return self._moving = False self._drawing = True point = self.toMapCoordinates(e.pos()) if self.snap_geometry: point = self._snap(point) self.rubberband.addPoint(point, True) if self.draw_markers: marker = QgsVertexMarker(self.canvas) marker.setCenter(point) marker.setColor(self.color) marker.setIconSize(8) marker.setIconType(QgsVertexMarker.ICON_CIRCLE) marker.setPenWidth(4) self.markers.append(marker) def canvasMoveEvent(self, e): ''' override, draw connecting line to last segment when moving mouse ''' if not self.snap_geometry and not self._drawing: return point = self.toMapCoordinates(e.pos()) if self.snap_geometry: point = self._snap(point) if self.snap_geometry: self._move_marker.setCenter(point) if self._drawing: #self.rubberBand.removeLastPoint() if self._moving: self.rubberband.removeLastPoint() self.rubberband.addPoint(point, True) self._moving = True def disconnect(self, **kwargs): ''' override, 'remove' marker ''' if self._move_marker: #scene = self.canvas.scene() #scene.removeItem(self._move_marker) # workaround: if removed from scene marker won't appear any more # set it somewhere it can't be seen self._move_marker.setCenter(QgsPointXY(0, 0)) super().disconnect(**kwargs)
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath()).path() + "/python/plugins/pdokservicesplugin" # initialize locale localePath = "" if isinstance(QSettings().value("locale/userLocale"), QVariant): locale = QSettings().value("locale/userLocale").value()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/pdokservicesplugin_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration self.run_action = QAction(QIcon(":/plugins/pdokservicesplugin/icon.png"), \ u"Pdok Services Plugin", self.iface.mainWindow()) self.servicesLoaded = False # connect the action to the run method # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if "True" == self.docked or "true" == self.docked or True == self.docked: # self.run_action.triggered.connect(self.showAndRaise) # self.dlg.radioDocked.setChecked(True) # # docked the dialog is immidiately visible, so should run NOW # else: # self.run_action.triggered.connect(self.run) # self.dlg.radioDocked.setChecked(False) # self.setupfq() self.run_action.triggered.connect(self.run) #self.dlg.radioDocked.setChecked(False) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") self.toolbar.addAction(self.run_action) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup self.clean_action = QAction(QIcon(":/plugins/pdokservicesplugin/eraser.png"), \ u"Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText("PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect(self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText("een of meer zoekwoorden uit resultaat") #self.dlg.radioDocked.toggled.connect(self.set_docked) self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson) #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen('http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen('http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(self.plugin_dir +'/pdok.json', 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str(pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) # def set_docked(self, foo): # self.setSettingsValue('docked', self.dlg.radioDocked.isChecked()) # #if Qgis.QGIS_VERSION_INT < 10900: # # # qgis <= 1.8 # # QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked())) # #else: # # QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked()) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Pdok Services Plugin",self.run_action) del self.toolbarSearch def showService(self, selectedIndexes): if len(selectedIndexes)==0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo(self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data(Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)]=str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale ='' if 'minscale' in self.currentLayer and self.currentLayer['minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:"+self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer['maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:"+self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml('<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype=="WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype=="WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location,query = urllib.parse.splitquery(url) url = location if query != None and query != '': url +=('?'+urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) [""], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs="+crs+"&layers="+layers+"&styles="+style+"&format="+imgformat+"&url="+url; self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs=tilematrixset i = crs.find(':', 5) if i > -1: crs=crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs='EPSG:3857' uri = "tileMatrixSet="+tilematrixset+"&crs="+crs+"&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='"+layers+"' url='"+url+"' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers=='ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper()) ) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData( serviceLayer, Qt.UserRole ) itemType.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem("%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) ) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["servicetitle"] )) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem("%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"]) ) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) self.sourceModel.appendRow( [ itemLayername, itemType, itemServicetitle, itemFilter ] ) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount()>0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data(Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data= data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml( '<h4>{}</h4><lu>{}</lu>'.format(adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) def removePointer(self): if self.pointer is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
class ParentMapTool(QgsMapTool): def __init__(self, iface, settings, action, index_action): """ Class constructor """ self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.show_help = bool(int(self.settings.value('status/show_help', 1))) self.index_action = index_action self.layer_arc = None self.layer_connec = None self.layer_node = None self.layer_arc_man = None self.layer_connec_man = None self.layer_node_man = None self.layer_gully_man = None self.schema_name = None self.controller = None self.dao = None # Call superclass constructor and set current action QgsMapTool.__init__(self, self.canvas) self.setAction(action) # Snapper self.snapper_manager = SnappingConfigManager(self.iface) self.snapper = QgsMapCanvasSnapper(self.canvas) # Change map tool cursor self.cursor = QCursor() self.cursor.setShape(Qt.CrossCursor) # Get default cursor self.std_cursor = self.parent().cursor() # Set default vertex marker color = QColor(255, 100, 255) self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setColor(color) self.vertex_marker.setIconSize(15) self.vertex_marker.setPenWidth(3) # Set default rubber band color_selection = QColor(254, 178, 76, 63) self.rubber_band = QgsRubberBand(self.canvas, QGis.Polygon) self.rubber_band.setColor(color) self.rubber_band.setFillColor(color_selection) self.rubber_band.setWidth(1) self.reset() self.force_active_layer = True # Set default encoding reload(sys) sys.setdefaultencoding('utf-8') #@UndefinedVariable def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ path_folder = os.path.join(os.path.dirname(__file__), os.pardir) path_cursor = os.path.join(path_folder, 'icons', '201.png') if os.path.exists(path_cursor): cursor = QCursor(QPixmap(path_cursor)) else: cursor = QCursor(Qt.ArrowCursor) return cursor def set_layers(self, layer_arc_man, layer_connec_man, layer_node_man, layer_gully_man=None): """ Sets layers involved in Map Tools functions Sets Snapper Manager """ self.layer_arc_man = layer_arc_man self.layer_connec_man = layer_connec_man self.layer_node_man = layer_node_man self.layer_gully_man = layer_gully_man self.snapper_manager.set_layers(layer_arc_man, layer_connec_man, layer_node_man, layer_gully_man) def set_controller(self, controller): self.controller = controller self.schema_name = controller.schema_name self.plugin_dir = self.controller.plugin_dir self.snapper_manager.controller = controller def deactivate(self): # Uncheck button self.action().setChecked(False) # Restore previous snapping self.snapper_manager.recover_snapping_options() # Recover cursor self.canvas.setCursor(self.std_cursor) # Remove highlight self.vertex_marker.hide() def set_icon(self, widget, icon): """ Set @icon to selected @widget """ # Get icons folder icons_folder = os.path.join(self.plugin_dir, 'icons') icon_path = os.path.join(icons_folder, str(icon) + ".png") if os.path.exists(icon_path): widget.setIcon(QIcon(icon_path)) else: self.controller.log_info("File not found", parameter=icon_path) def set_action_pan(self): """ Set action 'Pan' """ try: self.iface.actionPan().trigger() except Exception: pass def reset(self): # Graphic elements self.rubber_band.reset(QGis.Polygon) # Selection self.snapped_feat = None def cancel_map_tool(self): """ Executed if user press right button or escape key """ # Reset rubber band self.reset() # Deactivate map tool self.deactivate() self.set_action_pan() def remove_markers(self): """ Remove previous markers """ vertex_items = [i for i in self.canvas.scene().items() if issubclass(type(i), QgsVertexMarker)] for ver in vertex_items: if ver in self.canvas.scene().items(): self.canvas.scene().removeItem(ver) def refresh_map_canvas(self): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() def open_dialog(self, dlg=None, dlg_name=None, maximize_button=True, stay_on_top=True): """ Open dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg # Manage i18n of the dialog if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top and maximize button if maximize_button and stay_on_top: dlg.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint) elif not maximize_button and stay_on_top: dlg.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowStaysOnTopHint) elif maximize_button and not stay_on_top: dlg.setWindowFlags(Qt.WindowMaximizeButtonHint) # Open dialog dlg.open() def close_dialog(self, dlg=None, set_action_pan=True): """ Close dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg try: self.save_settings(dlg) dlg.close() if set_action_pan: map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.set_action_pan() except AttributeError: pass def load_settings(self, dialog=None): """ Load QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: x = self.controller.plugin_settings_value(dialog.objectName() + "_x") y = self.controller.plugin_settings_value(dialog.objectName() + "_y") width = self.controller.plugin_settings_value(dialog.objectName() + "_width", dialog.property('width')) height = self.controller.plugin_settings_value(dialog.objectName() + "_height", dialog.property('height')) if int(x) < 0 or int(y) < 0: dialog.resize(int(width), int(height)) else: screens = ctypes.windll.user32 screen_x = screens.GetSystemMetrics(78) screen_y = screens.GetSystemMetrics(79) if int(x) > screen_x: x = int(screen_x) - int(width) if int(y) > screen_y: y = int(screen_y) dialog.setGeometry(int(x), int(y), int(width), int(height)) except: pass def save_settings(self, dialog=None): """ Save QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: self.controller.plugin_settings_set_value(dialog.objectName() + "_width", dialog.property('width')) self.controller.plugin_settings_set_value(dialog.objectName() + "_height", dialog.property('height')) self.controller.plugin_settings_set_value(dialog.objectName() + "_x", dialog.pos().x()) self.controller.plugin_settings_set_value(dialog.objectName() + "_y", dialog.pos().y()) except: pass def check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr is valid """ if log_info: self.controller.log_info(expr_filter) expr = QgsExpression(expr_filter) if expr.hasParserError(): message = "Expression Error" self.controller.log_warning(message, parameter=expr_filter) return (False, expr) return (True, expr) def canvasMoveEvent(self, event): # Make sure active layer is always 'v_edit_node' cur_layer = self.iface.activeLayer() if cur_layer != self.layer_node and self.force_active_layer: self.iface.setActiveLayer(self.layer_node) # Hide highlight self.vertex_marker.hide() try: # Get current mouse coordinates x = event.pos().x() y = event.pos().y() event_point = QPoint(x, y) except(TypeError, KeyError): self.iface.actionPan().trigger() return # Snapping (retval, result) = self.snapper.snapToCurrentLayer(event_point, 2) # @UnusedVariable # That's the snapped features if result: # Get the point and add marker on it point = QgsPoint(result[0].snappedVertex) self.vertex_marker.setCenter(point) self.vertex_marker.show()
class AddPipeTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.first_click = False self.rubber_band = QgsRubberBand(self.canvas(), False) self.rubber_band.setColor(QColor(255, 128, 128)) self.rubber_band.setWidth(1) self.rubber_band.setBrushStyle(Qt.Dense4Pattern) self.rubber_band.reset() self.snapper = None self.snapped_feat_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = None self.diameter_dialog = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = False if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) last_ix = self.rubber_band.numberOfVertices() self.rubber_band.movePoint(last_ix - 1, (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt)) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') # Mouse not clicked: snapping to closest vertex match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # Pipe starts from an existing vertex self.snapped_feat_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() # else: # It's a new, isolated pipe self.snapped_vertex = None self.snapped_feat_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): # if not self.mouse_clicked: # return if event.button() == Qt.LeftButton: # Update rubber bands self.rubber_band.addPoint( (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt), True) if self.first_click: self.rubber_band.addPoint( (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt), True) self.first_click = not self.first_click elif event.button() == Qt.RightButton: # try: pipe_band_geom = self.rubber_band.asGeometry() # No rubber band geometry and feature snapped: pop the context menu if pipe_band_geom is None and self.snapped_feat_id is not None: menu = QMenu() section_action = menu.addAction('Section...') # TODO: softcode diameter_action = menu.addAction( 'Change diameter...') # TODO: softcode action = menu.exec_(self.iface.mapCanvas().mapToGlobal( QPoint(event.pos().x(), event.pos().y()))) pipe_ft = vector_utils.get_feats_by_id(self.params.pipes_vlay, self.snapped_feat_id)[0] if action == section_action: if self.params.dem_rlay is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'No DEM selected. Cannot edit section.', QgsMessageBar.WARNING, 5) # TODO: softcode else: # Check whether the pipe is all inside the DEM pipe_pts = pipe_ft.geometry().asPolyline() for pt in pipe_pts: if not self.params.dem_rlay.extent().contains(pt): self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Some pipe vertices fall outside of the DEM. Cannot edit section.', QgsMessageBar.WARNING, 5) # TODO: softcode return # Check whether the start/end nodes have an elevation value (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes( self.params, pipe_ft.geometry()) start_node_elev = start_node_ft.attribute( Junction.field_name_elev) end_node_elev = end_node_ft.attribute( Junction.field_name_elev) if start_node_elev == NULL or end_node_elev == NULL: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Missing elevation value in start or end node attributes. Cannot edit section.', QgsMessageBar.WARNING, 5) # TODO: softcode return pipe_dialog = PipeSectionDialog( self.iface.mainWindow(), self.iface, self.params, pipe_ft) pipe_dialog.exec_() elif action == diameter_action: old_diam = pipe_ft.attribute(Pipe.field_name_diameter) self.diameter_dialog = DiameterDialog( self.iface.mainWindow(), self.params, old_diam) self.diameter_dialog.exec_() # Exec creates modal dialog new_diameter = self.diameter_dialog.get_diameter() if new_diameter is None: return # Update pipe diameter vector_utils.update_attribute(self.params.pipes_vlay, pipe_ft, Pipe.field_name_diameter, new_diameter) # Check if a valve is present adj_valves = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, pipe_ft, True, True, False) if adj_valves['valves']: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Valves detected on the pipe: need to update their diameters too.', QgsMessageBar.WARNING, 5) # TODO: softcode return # There's a rubber band: create new pipe if pipe_band_geom is not None: # Finalize line rubberband_pts = pipe_band_geom.asPolyline()[:-1] rubberband_pts = self.remove_duplicated_point(rubberband_pts) if len(rubberband_pts) < 2: return # Check whether the pipe points are located on existing nodes junct_nrs = [0] for p in range(1, len(rubberband_pts) - 1): overlapping_nodes = NetworkUtils.find_overlapping_nodes( self.params, rubberband_pts[p]) if overlapping_nodes['junctions'] or overlapping_nodes[ 'reservoirs'] or overlapping_nodes['tanks']: junct_nrs.append(p) junct_nrs.append(len(rubberband_pts) - 1) new_pipes_nr = len(junct_nrs) - 1 new_pipes_fts = [] new_pipes_eids = [] for np in range(new_pipes_nr): pipe_eid = NetworkUtils.find_next_id( self.params.pipes_vlay, Pipe.prefix) # TODO: softcode demand = float(self.data_dock.txt_pipe_demand.text()) diameter = float(self.data_dock.txt_pipe_diameter.text()) loss = float(self.data_dock.txt_pipe_loss.text()) roughness = float( self.data_dock.lbl_pipe_roughness_val_val.text()) status = self.data_dock.cbo_pipe_status.currentText() material = self.data_dock.cbo_pipe_roughness.currentText() pipe_desc = self.data_dock.txt_pipe_desc.text() pipe_tag = self.data_dock.cbo_pipe_tag.currentText() pipe_ft = LinkHandler.create_new_pipe( self.params, pipe_eid, diameter, loss, roughness, status, material, rubberband_pts[junct_nrs[np]:junct_nrs[np + 1] + 1], True, pipe_desc, pipe_tag) self.rubber_band.reset() new_pipes_fts.append(pipe_ft) new_pipes_eids.append(pipe_eid) emitter_coeff_s = self.data_dock.txt_junction_emit_coeff.text() if emitter_coeff_s is None or emitter_coeff_s == '': emitter_coeff = float(0) else: emitter_coeff = float( self.data_dock.txt_junction_emit_coeff.text()) # Description junction_desc = self.data_dock.txt_junction_desc.text() # Tag junction_tag = self.data_dock.cbo_junction_tag.currentText() # Create start and end node, if they don't exist (start_junction, end_junction) = NetworkUtils.find_start_end_nodes( self.params, new_pipes_fts[0].geometry()) new_start_junction = None if not start_junction: new_start_junction = rubberband_pts[0] junction_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) elev = raster_utils.read_layer_val_from_coord( self.params.dem_rlay, new_start_junction, 1) if elev is None: elev = 0 # If elev is none, and the DEM is selected, it's better to inform the user if self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', QgsMessageBar.WARNING, 5) # TODO: softcode deltaz = float(self.data_dock.txt_junction_deltaz.text()) j_demand = float(self.data_dock.txt_junction_demand.text()) pattern = self.data_dock.cbo_junction_pattern.itemData( self.data_dock.cbo_junction_pattern.currentIndex()) if pattern is not None: pattern_id = pattern.id else: pattern_id = None NodeHandler.create_new_junction( self.params, new_start_junction, junction_eid, elev, j_demand, deltaz, pattern_id, emitter_coeff, junction_desc, junction_tag) (start_junction, end_junction) = NetworkUtils.find_start_end_nodes( self.params, new_pipes_fts[len(new_pipes_fts) - 1].geometry()) new_end_junction = None if not end_junction: new_end_junction = rubberband_pts[len(rubberband_pts) - 1] junction_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) elev = raster_utils.read_layer_val_from_coord( self.params.dem_rlay, new_end_junction, 1) if elev is None: elev = 0 # If elev is none, and the DEM is selected, it's better to inform the user if self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', QgsMessageBar.WARNING, 5) # TODO: softcode deltaz = float(self.data_dock.txt_junction_deltaz.text()) pattern = self.data_dock.cbo_junction_pattern.itemData( self.data_dock.cbo_junction_pattern.currentIndex()) if pattern is not None: pattern_id = pattern.id else: pattern_id = None NodeHandler.create_new_junction( self.params, new_end_junction, junction_eid, elev, demand, deltaz, pattern_id, emitter_coeff, junction_desc, junction_tag) # If end or start node intersects a pipe, split it if new_start_junction: for pipe_ft in self.params.pipes_vlay.getFeatures(): if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[0] and\ pipe_ft.geometry().distance(QgsGeometry.fromPoint(new_start_junction)) < self.params.tolerance: LinkHandler.split_pipe(self.params, pipe_ft, new_start_junction) if new_end_junction: for pipe_ft in self.params.pipes_vlay.getFeatures(): if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[-1] and\ pipe_ft.geometry().distance(QgsGeometry.fromPoint(new_end_junction)) < self.params.tolerance: LinkHandler.split_pipe(self.params, pipe_ft, new_end_junction) # except Exception as e: # self.rubber_band.reset() # self.iface.messageBar().pushWarning('Cannot add new pipe to ' + self.params.pipes_vlay.name() + ' layer', repr(e)) # traceback.print_exc(file=sys.stdout) def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.rubber_band.reset() def activate(self): self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() def deactivate(self): QgsProject.instance().setSnapSettingsForLayer( self.params.junctions_vlay.id(), True, QgsSnapper.SnapToVertex, QgsTolerance.MapUnits, 0, True) QgsProject.instance().setSnapSettingsForLayer( self.params.reservoirs_vlay.id(), True, QgsSnapper.SnapToVertex, QgsTolerance.MapUnits, 0, True) QgsProject.instance().setSnapSettingsForLayer( self.params.tanks_vlay.id(), True, QgsSnapper.SnapToVertex, QgsTolerance.MapUnits, 0, True) QgsProject.instance().setSnapSettingsForLayer( self.params.pipes_vlay.id(), True, QgsSnapper.SnapToSegment, QgsTolerance.MapUnits, 0, True) # self.rubber_band.reset() self.data_dock.btn_add_pipe.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def remove_duplicated_point(self, pts): # This is needed because the rubber band sometimes returns duplicated points purged_pts = [pts[0]] for p in enumerate(range(len(pts) - 1), 1): if pts[p[0]] == pts[p[0] - 1]: continue else: purged_pts.append(pts[p[0]]) return purged_pts def update_snapper(self): layers = { self.params.junctions_vlay: QgsPointLocator.Vertex, self.params.reservoirs_vlay: QgsPointLocator.Vertex, self.params.tanks_vlay: QgsPointLocator.Vertex, self.params.pipes_vlay: QgsPointLocator.All } self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) # Needed by Observable def update(self, observable): self.update_snapper()
class CopyLatLonTool(QgsMapToolEmitPoint): '''Class to interact with the map canvas to capture the coordinate when the mouse button is pressed and to display the coordinate in in the status bar.''' captureStopped = pyqtSignal() def __init__(self, settings, iface): QgsMapToolEmitPoint.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.settings = settings self.marker = None self.vertex = None def activate(self): '''When activated set the cursor to a crosshair.''' self.canvas.setCursor(Qt.CrossCursor) self.snapcolor = QgsSettings().value("/qgis/digitizing/snap_color", QColor(Qt.magenta)) def deactivate(self): self.removeMarker() self.removeVertexMarker() self.captureStopped.emit() def formatCoord(self, pt, delimiter): '''Format the coordinate string according to the settings from the settings dialog.''' if self.settings.captureProjIsWgs84(): # ProjectionTypeWgs84 # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) lat = pt4326.y() lon = pt4326.x() if self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDMS: # DMS msg = formatDmsString(lat, lon, 0, self.settings.dmsPrecision, self.settings.coordOrder, delimiter, settings.captureAddDmsSpace, settings.capturePadZeroes) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDDMMSS: # DDMMSS msg = formatDmsString(lat, lon, 1, self.settings.dmsPrecision, self.settings.coordOrder, delimiter, settings.capturePadZeroes) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDMM: # DM.MM msg = formatDmsString(lat, lon, 2, settings.captureDmmPrecision, self.settings.coordOrder, delimiter, settings.captureAddDmsSpace, settings.capturePadZeroes) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeWKT: # WKT msg = 'POINT({:.{prec}f} {:.{prec}f})'.format( pt4326.x(), pt4326.y(), prec=self.settings.decimalDigits) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeGeoJSON: # GeoJSON msg = '{{"type": "Point","coordinates": [{:.{prec}f},{:.{prec}f}]}}'.format( pt4326.x(), pt4326.y(), prec=self.settings.decimalDigits) else: # decimal degrees if self.settings.coordOrder == self.settings.OrderYX: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt4326.y(), delimiter, pt4326.x(), prec=self.settings.decimalDigits) else: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt4326.x(), delimiter, pt4326.y(), prec=self.settings.decimalDigits) elif self.settings.captureProjIsProjectCRS(): # Projection in the project CRS if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.y(), delimiter, pt.x(), prec=self.settings.decimalDigits) else: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.x(), delimiter, pt.y(), prec=self.settings.decimalDigits) else: msg = 'POINT({:.{prec}f} {:.{prec}f})'.format( pt.x(), pt.y(), prec=self.settings.decimalDigits) elif self.settings.captureProjIsCustomCRS(): # Projection is a custom CRS canvasCRS = self.canvas.mapSettings().destinationCrs() customCRS = self.settings.captureCustomCRS() transform = QgsCoordinateTransform(canvasCRS, customCRS, QgsProject.instance()) pt = transform.transform(pt.x(), pt.y()) if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.y(), delimiter, pt.x(), prec=self.settings.decimalDigits) else: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.x(), delimiter, pt.y(), prec=self.settings.decimalDigits) else: msg = 'POINT({:.{prec}f} {:.{prec}f})'.format( pt.x(), pt.y(), prec=self.settings.decimalDigits) elif self.settings.captureProjIsMGRS(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) try: msg = mgrs.toMgrs(pt4326.y(), pt4326.x()) except Exception: # traceback.print_exc() msg = None elif self.settings.captureProjIsPlusCodes(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) try: msg = olc.encode(pt4326.y(), pt4326.x(), self.settings.plusCodesLength) except Exception: msg = None elif self.settings.captureProjIsUTM(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) msg = latLon2UtmString(pt4326.y(), pt4326.x(), settings.captureUtmPrecision) if msg == '': msg = None elif self.settings.captureProjIsGeohash(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) msg = geohash.encode(pt4326.y(), pt4326.x(), settings.captureGeohashPrecision) if msg == '': msg = None elif self.settings.captureProjIsMaidenhead(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) try: msg = maidenhead.toMaiden( pt4326.y(), pt4326.x(), precision=settings.captureMaidenheadPrecision) except Exception: msg = None msg = '{}{}{}'.format(self.settings.capturePrefix, msg, self.settings.captureSuffix) return msg def canvasMoveEvent(self, event): '''Capture the coordinate as the user moves the mouse over the canvas. Show it in the status bar.''' pt = self.snappoint(event.originalPixelPoint()) # input is QPoint try: msg = self.formatCoord(pt, ', ') formatString = self.coordFormatString() if msg is None: self.iface.statusBarIface().showMessage("") else: self.iface.statusBarIface().showMessage( "{} - {}".format(msg, formatString), 4000) except Exception: self.iface.statusBarIface().showMessage("") def snappoint(self, qpoint): match = self.canvas.snappingUtils().snapToMap(qpoint) if match.isValid(): if self.vertex is None: self.vertex = QgsVertexMarker(self.canvas) self.vertex.setIconSize(12) self.vertex.setPenWidth(2) self.vertex.setColor(self.snapcolor) self.vertex.setIconType(QgsVertexMarker.ICON_BOX) self.vertex.setCenter(match.point()) return (match.point()) # Returns QgsPointXY else: self.removeVertexMarker() return self.toMapCoordinates( qpoint) # QPoint input, returns QgsPointXY def coordFormatString(self): if self.settings.captureProjIsWgs84(): if self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDecimal: if self.settings.coordOrder == self.settings.OrderYX: s = 'Lat Lon' else: s = 'Lon Lat' elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeWKT: s = 'WKT' elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeGeoJSON: s = 'GeoJSON' elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDMM: s = 'DM.MM' else: s = 'DMS' elif self.settings.captureProjIsProjectCRS(): crsID = self.canvas.mapSettings().destinationCrs().authid() if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: s = '{} - Y,X'.format(crsID) else: s = '{} - X,Y'.format(crsID) else: # WKT s = 'WKT' elif self.settings.captureProjIsMGRS(): s = 'MGRS' elif self.settings.captureProjIsUTM(): s = 'Standard UTM' elif self.settings.captureProjIsPlusCodes(): s = 'Plus Codes' elif self.settings.captureProjIsGeohash(): s = 'Geohash' elif self.settings.captureProjIsMaidenhead(): s = 'Maidenhead Grid Locator' elif self.settings.captureProjIsCustomCRS(): if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: s = '{} - Y,X'.format(self.settings.captureCustomCRSID()) else: s = '{} - X,Y'.format(self.settings.captureCustomCRSID()) else: # WKT s = 'WKT' else: # Should never happen s = '' return s def canvasReleaseEvent(self, event): '''Capture the coordinate when the mouse button has been released, format it, and copy it to the clipboard. pt is QgsPointXY''' pt = self.snappoint(event.originalPixelPoint()) self.removeVertexMarker() if settings.captureShowLocation: if self.marker is None: self.marker = QgsVertexMarker(self.canvas) self.marker.setIconSize(18) self.marker.setPenWidth(2) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.setCenter(pt) else: self.removeMarker() try: msg = self.formatCoord(pt, self.settings.delimiter) formatString = self.coordFormatString() if msg is not None: clipboard = QApplication.clipboard() clipboard.setText(msg) self.iface.messageBar().pushMessage( "", "{} coordinate {} copied to the clipboard".format( formatString, msg), level=Qgis.Info, duration=4) except Exception as e: self.iface.messageBar().pushMessage( "", "Invalid coordinate: {}".format(e), level=Qgis.Warning, duration=4) def removeMarker(self): if self.marker is not None: self.canvas.scene().removeItem(self.marker) self.marker = None def removeVertexMarker(self): if self.vertex is not None: self.canvas.scene().removeItem(self.vertex) self.vertex = None
class FeatureDragger(FeaturePicker): ''' tool for moving features on the map canvas with drag & drop, does not change the geometry of the dragged feature but draws a marker at the new position and emits the geometry Attributes ---------- feature_dragged : pyqtSignal emitted when a feature is dragged or clicked on the map canvas, (feature id, release position) drag_cursor : QCursor the appearance of the cursor while dragging a feature ''' feature_dragged = pyqtSignal(int, QgsPointXY) drag_cursor = QCursor(Qt.DragMoveCursor) def __init__(self, ui_element: QWidget, layers: List[QgsVectorLayer] = [], canvas: QgsMapCanvas = None, target_epsg: int = 25832): ''' Parameters ---------- ui_element : QWidget clickable UI element, clicking on it will adctivate/deactivate this tool layers : list, optional the layers containing the features that can be picked, defaults to not setting any layers canvas : QgsMapCanvas, optional the map canvas the tool will work on, defaults to the map canvas of the QGIS UI target_epsg : int, optional projection of emitted geometry after drawing as epsg code, defaults to 25832 ''' super().__init__(ui_element, layers=layers, canvas=canvas) self._marker = None self._picked_feature = None self._dragging = False self.target_crs = QgsCoordinateReferenceSystem(target_epsg) def reset(self): ''' reset the feature picker to it's initial state ''' self.remove_marker() self._picked_feature = None def remove_marker(self): ''' remove the marker from the map ''' if not self._marker: return self.canvas.scene().removeItem(self._marker) self._marker = None def canvasPressEvent(self, e): ''' override, remember feature on click and move marker (create one if not drawn yet) ''' if self._picked_feature is None: features = QgsMapToolIdentify(self.canvas).identify( e.pos().x(), e.pos().y(), self._layers, QgsMapToolIdentify.TopDownStopAtFirst) if len(features) == 0: return feature = features[0].mFeature self._picked_feature = feature.id() # there is a feature -> drag it self._dragging = True self.canvas.setCursor(self.drag_cursor) # not marked yet -> mark position if not self._marker: color = QColor(0, 0, 255) color.setAlpha(100) self._marker = QgsVertexMarker(self.canvas) self._marker.setColor(color) self._marker.setIconSize(10) self._marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self._marker.setPenWidth(10) point = self.toMapCoordinates(e.pos()) self._marker.setCenter(point) def canvasMoveEvent(self, e): ''' override, move marker if mouse dragging is active and mouse is moved ''' if not self._marker or not self._dragging: return # update position of marker while dragging point = self.toMapCoordinates(e.pos()) self._marker.setCenter(point) def canvasReleaseEvent(self, mouseEvent): ''' override, emit geometry of position of marker on mouse release ''' self._dragging = False if self._picked_feature is None: return self.canvas.setCursor(self.cursor) point = self.toMapCoordinates(self._marker.pos().toPoint()) self.feature_dragged.emit(self._picked_feature, self.transform_from_map(point))
class MincutMapTool(ParentMapTool): ''' Button 26. User select one node or arc. Execute SQL function: 'gw_fct_mincut' This function fills 3 temporary tables with id's: node_id, arc_id and valve_id Returns and integer: error code Get these id's and select them in its corresponding layers ''' def __init__(self, iface, settings, action, index_action): ''' Class constructor ''' # Call ParentMapTool constructor super(MincutMapTool, self).__init__(iface, settings, action, index_action) # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(255, 25, 25)) self.vertexMarker.setIconSize(11) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) ''' QgsMapTools inherited event functions ''' def canvasMoveEvent(self, event): # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Snapping (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable self.current_layer = None # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer.name() == self.layer_node.name() or snapPoint.layer.name() == self.layer_arc.name(): # Get the point point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() # Data for function self.current_layer = result[0].layer self.snappFeat = next(result[0].layer.getFeatures(QgsFeatureRequest().setFilterFid(result[0].snappedAtGeometry))) # Change symbol if snapPoint.layer.name() == self.layer_node.name(): self.vertexMarker.setIconType(QgsVertexMarker.ICON_CIRCLE) else: self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) break def canvasReleaseEvent(self, event): ''' With left click the digitizing is finished ''' if event.button() == Qt.LeftButton and self.current_layer is not None: # Get selected layer type: 'arc' or 'node' if self.current_layer.name() == self.layer_arc.name(): elem_type = 'arc' elif self.current_layer.name() == self.layer_node.name(): elem_type = 'node' else: message = "Current layer not valid" self.controller.show_warning(message, context_name='ui_message') return feature = self.snappFeat elem_id = feature.attribute(elem_type+'_id') # Execute SQL function function_name = "gw_fct_mincut" sql = "SELECT "+self.schema_name+"."+function_name+"('"+str(elem_id)+"', '"+elem_type+"');" result = self.controller.execute_sql(sql) print sql if result: # Get 'arc' and 'node' list and select them self.mg_flow_trace_select_features(self.layer_arc, 'arc') self.mg_flow_trace_select_features(self.layer_node, 'node') # Refresh map canvas self.iface.mapCanvas().refresh() def mg_flow_trace_select_features(self, layer, elem_type): sql = "SELECT * FROM "+self.schema_name+".anl_mincut_"+elem_type+" ORDER BY "+elem_type+"_id" rows = self.controller.get_rows(sql) if rows: # Build an expression to select them aux = "\""+elem_type+"_id\" IN (" for elem in rows: aux += elem[0] + ", " aux = aux[:-2] + ")" # Get a featureIterator from this expression: expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error: " + str(expr.parserErrorString()) self.controller.show_warning(message, context_name='ui_message') return it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer.setSelectedFeatures(id_list) def activate(self): # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to arc and node self.snapperManager.snapToArc() self.snapperManager.snapToNode() # Change cursor self.canvas.setCursor(self.cursor) # Show help message when action is activated if self.show_help: message = "Select a node or pipe and click on it, the valves minimum cut polygon is computed" self.controller.show_info(message, context_name='ui_message' ) # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_arc) except: self.canvas.setCurrentLayer(self.layer_arc) def deactivate(self): # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) # Remove highlight self.h = None
class AddValveTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.snapper = None self.snapped_pipe_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = -1 self.diameter_dialog = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = True if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') if not self.mouse_clicked: # Mouse not clicked: snapping to closest vertex # (retval, result) = self.snapper.snapMapPoint(self.toMapCoordinates(event.pos())) # if len(result) > 0: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # It's a vertex on an existing pipe # self.snapped_pipe_id = result[0].snappedAtGeometry # snapped_vertex = result[0].snappedVertex # self.snapped_vertex_nr = result[0].snappedVertexNr # self.snapped_vertex = QgsPoint(snapped_vertex.x(), snapped_vertex.y()) self.snapped_pipe_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: # It's a new, isolated vertex self.snapped_pipe_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # No pipe snapped: notify user if self.snapped_pipe_id is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'You need to snap the cursor to a pipe to add a valve.', QgsMessageBar.INFO, 5) # A pipe has been snapped else: request = QgsFeatureRequest().setFilterFid( self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: # Check whether the pipe has a start and an end node (start_node, end_node) = NetworkUtils.find_start_end_nodes( self.params, features[0].geometry()) if not start_node or not end_node: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The pipe is missing the start or end nodes.', QgsMessageBar.WARNING, 5) # TODO: softcode return # Find endnode closest to valve position dist_1 = start_node.geometry().distance( QgsGeometry.fromPoint(self.snapped_vertex)) dist_2 = end_node.geometry().distance( QgsGeometry.fromPoint(self.snapped_vertex)) # Get the attributes of the closest junction (start_node, end_node) = NetworkUtils.find_start_end_nodes( self.params, features[0].geometry(), False, True, True) if dist_1 < dist_2: closest_junction_ft = start_node else: closest_junction_ft = end_node # Create the valve diameter = features[0].attribute(Pipe.field_name_diameter) # diameter = self.data_dock.txt_valve_diameter.text() minor_loss = self.data_dock.txt_valve_minor_loss.text() selected_type = self.data_dock.cbo_valve_type.itemData( self.data_dock.cbo_valve_type.currentIndex()) if selected_type != Valve.type_gpv: setting = self.data_dock.txt_valve_setting.text() else: valve_curve = self.data_dock.cbo_valve_curve.itemData( self.data_dock.cbo_valve_curve.currentIndex()) if valve_curve is not None: setting = valve_curve.id else: setting = None # Pump status valve_status = self.data_dock.cbo_valve_status.itemData( self.data_dock.cbo_valve_status.currentIndex()) # Valve description valve_desc = self.data_dock.txt_valve_desc.text() # Valve tag valve_tag = self.data_dock.cbo_valve_tag.currentText() try: LinkHandler.create_new_pumpvalve( self.params, self.data_dock, features[0], closest_junction_ft, self.snapped_vertex, self.params.valves_vlay, { Valve.field_name_diameter: diameter, Valve.field_name_minor_loss: minor_loss, Valve.field_name_setting: setting, Valve.field_name_type: selected_type, Valve.field_name_status: valve_status, Valve.field_name_description: valve_desc, Valve.field_name_tag: valve_tag }) except PumpValveCreationException as ex: self.iface.messageBar().pushMessage( Parameters.plug_in_name, ex.message, QgsMessageBar.INFO, 5) elif event.button() == Qt.RightButton: self.mouse_clicked = False # Check whether it clicked on a valve vertex if len( NetworkUtils.find_adjacent_links( self.params, self.snapped_vertex)['valves']) == 0: return menu = QMenu() diameter_action = menu.addAction( 'Change diameter...') # TODO: softcode invert_action = menu.addAction( 'Flip orientation') # TODO: softcode action = menu.exec_(self.iface.mapCanvas().mapToGlobal( QPoint(event.pos().x(), event.pos().y()))) request = QgsFeatureRequest().setFilterFid(self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: adj_links = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, features[0], True, True, False) for valve_ft in adj_links['valves']: if action == diameter_action: old_diam = valve_ft.attribute( Valve.field_name_diameter) self.diameter_dialog = DiameterDialog( self.iface.mainWindow(), self.params, old_diam) self.diameter_dialog.exec_( ) # Exec creates modal dialog new_diameter = self.diameter_dialog.get_diameter() if new_diameter is None: return # Update valve diameter vector_utils.update_attribute( self.params.valves_vlay, valve_ft, Valve.field_name_diameter, new_diameter) # Modify pipes diameters adj_pipes_fts = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.valves_vlay, valve_ft, False, True, True) if adj_pipes_fts: for adj_pipe_ft in adj_pipes_fts['pipes']: vector_utils.update_attribute( self.params.pipes_vlay, adj_pipe_ft, Pipe.field_name_diameter, new_diameter) self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Diameters of pipes adjacent to valve updated.', QgsMessageBar.INFO, 5) # TODO: softcode elif action == invert_action: request = QgsFeatureRequest().setFilterFid( self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: adj_links = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, features[0], True, True, False) for adj_link in adj_links['valves']: adj_link_pts = adj_link.geometry().asPolyline() for adj_link_pt in adj_link_pts: if NetworkUtils.points_overlap( adj_link_pt, self.snapped_vertex, self.params.tolerance): geom = adj_link.geometry() if geom.wkbType( ) == QGis.WKBMultiLineString: nodes = geom.asMultiPolyline() for line in nodes: line.reverse() newgeom = QgsGeometry.fromMultiPolyline( nodes) self.params.valves_vlay.changeGeometry( adj_link.id(), newgeom) if geom.wkbType( ) == QGis.WKBLineString: nodes = geom.asPolyline() nodes.reverse() newgeom = QgsGeometry.fromPolyline( nodes) self.params.valves_vlay.changeGeometry( adj_link.id(), newgeom) self.iface.mapCanvas().refresh() break def activate(self): snap_layer_pipes = NetworkUtils.set_up_snap_layer( self.params.pipes_vlay, None, QgsSnapper.SnapToVertexAndSegment) self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.valves_vlay.isEditable(): self.params.valves_vlay.startEditing() def deactivate(self): QgsProject.instance().setSnapSettingsForLayer( self.params.pipes_vlay.id(), True, QgsSnapper.SnapToSegment, QgsTolerance.MapUnits, 0, True) self.data_dock.btn_add_valve.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def update_snapper(self): layers = {self.params.pipes_vlay: QgsPointLocator.All} self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) # Needed by Observable def update(self, observable): self.update_snapper()
class Interface(QtWidgets.QDockWidget, GUI): def __init__(self, canvas): super(Interface, self).__init__() self.setupUi(self) self.canvas = canvas self.initVariables() self.initSignals() self.dateTimeEdit.setDateTime(QDateTime.currentDateTime()) self.layerCombo.setFilters(QgsMapLayerProxyModel.RasterLayer) self.outputFile.setFilter(".tif") def initVariables(self): self.myTool = QgsMapToolEmitPoint(self.canvas) self.currentTool = self.canvas.mapTool() self.clickedPoint = '' def createVertexMarker(self): self.clickedPoint = QgsVertexMarker(self.canvas) self.clickedPoint.setIconSize(15) self.clickedPoint.setPenWidth(3) def initSignals(self): self.ativarButton.toggled.connect(self.getPoint) self.myTool.canvasClicked.connect(self.doWork) self.canvas.mapToolSet.connect(self.verifyTool) def verifyTool(self, tool): if tool != self.myTool: self.ativarButton.setChecked(False) def getPoint(self, state): if state: self.canvas.setMapTool(self.myTool) self.createVertexMarker() else: self.canvas.unsetMapTool(self.myTool) self.canvas.scene().removeItem(self.clickedPoint) def doWork(self, point, button): if button == QtCore.Qt.LeftButton: wgsPoint = self.getWGSPoint(point) now = self.dateTimeEdit.dateTime().toPyDateTime() az,zen = sunpos(now, wgsPoint.y(), wgsPoint.x(), 0)[:2] if not self.layerCombo.currentLayer(): QMessageBox.critical(self, u"Erro", u"Nenhuma camada raster selecionada. Selecione uma camada.") return workingLayerFile = self.layerCombo.currentLayer().dataProvider().dataSourceUri() outputpath = self.outputFile.filePath() if not outputpath: QMessageBox.critical(self, u"Erro", u"Nenhum arquivo de saída definido. Escolha um arquivo de saída.") return if not outputpath.endswith(".tif"): outputpath = outputpath + '.tif' processing.run("gdal:hillshade", {'INPUT': workingLayerFile, 'BAND': 1, 'AZIMUTH': float(az), 'ALTITUDE': 90 - float(zen), 'OUTPUT': outputpath}) shadedlayer = QgsRasterLayer(outputpath, "Raster sombreado") QgsProject.instance().addMapLayer(shadedlayer) self.clickedPoint.setCenter(point) def getWGSPoint(self, pt): crsSrc = self.canvas.mapSettings().destinationCrs() crsDest = QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId) coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) wgsPt = coordinateTransformer.transform(pt) return wgsPt
class MoveTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.dock_widget = data_dock """:type : DataDock""" self.params = params self.elev = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.mouse_clicked = False self.snapper = None self.clicked_pt = None self.snap_results = None self.selected_node_ft = None self.selected_node_ft_lay = None self.mouse_pt = None self.pump_valve_selected = False self.pump_or_valve = None self.pump_valve_ft = None self.adj_links_fts = None self.adj_junctions = None self.delta_vec = QgsVector(0, 0) self.adj_links_fts_d = {} # self.rubber_bands_d = {} # self.rubber_bands_geoms_d = {} self.rubber_band = None self.logger = logging.getLogger('Logger1') def canvasPressEvent(self, event): if self.snap_results is None: self.clicked_pt = None return if event.button() == Qt.RightButton: self.mouse_clicked = False self.clicked_pt = None if event.button() == Qt.LeftButton: self.mouse_clicked = True self.clicked_pt = self.snap_results.point() # Check if a node was snapped: it can be just one self.selected_node_ft = None self.adj_links_fts = None # Check if I snapped on a node snapped_layer_name = self.snap_results.layer().name() self.selected_node_ft = None pt_locator_ju = self.snapper.locatorForLayer(self.params.junctions_vlay) pt_locator_re = self.snapper.locatorForLayer(self.params.reservoirs_vlay) pt_locator_ta = self.snapper.locatorForLayer(self.params.tanks_vlay) match_ju = pt_locator_ju.nearestVertex(self.snap_results.point(), 1) match_re = pt_locator_re.nearestVertex(self.snap_results.point(), 1) match_ta = pt_locator_ta.nearestVertex(self.snap_results.point(), 1) if match_ju.isValid() or match_re.isValid() or match_ta.isValid(): if match_ju.isValid(): node_feat_id = match_ju.featureId() request = QgsFeatureRequest().setFilterFid(node_feat_id) node = list(self.params.junctions_vlay.getFeatures(request)) self.selected_node_ft_lay = self.params.junctions_vlay if match_re.isValid(): node_feat_id = match_re.featureId() request = QgsFeatureRequest().setFilterFid(node_feat_id) node = list(self.params.reservoirs_vlay.getFeatures(request)) self.selected_node_ft_lay = self.params.reservoirs_vlay if match_ta.isValid(): node_feat_id = match_ta.featureId() request = QgsFeatureRequest().setFilterFid(node_feat_id) node = list(self.params.tanks_vlay.getFeatures(request)) self.selected_node_ft_lay = self.params.tanks_vlay self.selected_node_ft = QgsFeature(node[0]) # self.selected_node_ft_lay = self.snap_results.layer() self.adj_links_fts = NetworkUtils.find_adjacent_links(self.params, self.selected_node_ft.geometry()) # No selected nodes: it's just a vertex if self.selected_node_ft is None: snapped_ft = vector_utils.get_feats_by_id(self.snap_results.layer(), self.snap_results.featureId())[0] snapped_ft_geom = snapped_ft.geometry() points = [] if self.snap_results.vertexIndex() - 1 >= 0 and self.snap_results.vertexIndex() - 1 < len(snapped_ft_geom.asPolyline()): vertex_index = 1 vertex_before = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex() - 1) points.append(vertex_before) vertex_at = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex()) points.append(vertex_at) if self.snap_results.vertexIndex() + 1 >= 0 and self.snap_results.vertexIndex() + 1 < len(snapped_ft_geom.asPolyline()): vertex_index = 0 vertex_after = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex() + 1) points.append(vertex_after) if self.snap_results.vertexIndex() > 0 and self.snap_results.vertexIndex() < len(snapped_ft_geom.asPolyline()) - 1: vertex_index = 1 # self.rubber_bands_d[0] = (self.build_rubber_band(points), [vertex_index], [vertex_at]) self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) # It's a node else: # It's an isolated node: no rubber band! if self.adj_links_fts is None or (not self.adj_links_fts['pipes'] and not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']): # self.rubber_bands_d.clear() self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) return # Adjacent links are neither pumps nor valves: find the two pipes adjacent to the node # OR node adjacent to pump or valve and NOT using block logic if (not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']) or not self.params.block_logic: self.pump_valve_selected = False rb_points = [] for adjacent_pipes_ft in self.adj_links_fts['pipes']: closest = adjacent_pipes_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint()) self.adj_links_fts_d[adjacent_pipes_ft] = (closest[1], self.params.pipes_vlay) if closest[1] == 0: next_vertext_id = closest[1] + 1 else: next_vertext_id = closest[1] - 1 rb_points.append(adjacent_pipes_ft.geometry().vertexAt(next_vertext_id)) for adj_pumps_ft in self.adj_links_fts['pumps']: closest = adj_pumps_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint()) self.adj_links_fts_d[adj_pumps_ft] = (closest[1], self.params.pumps_vlay) if closest[1] == 0: next_vertext_id = closest[1] + 1 else: next_vertext_id = closest[1] - 1 rb_points.append(adj_pumps_ft.geometry().vertexAt(next_vertext_id)) for adj_valves_ft in self.adj_links_fts['valves']: closest = adj_valves_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint()) self.adj_links_fts_d[adj_valves_ft] = (closest[1], self.params.valves_vlay) if closest[1] == 0: next_vertext_id = closest[1] + 1 else: next_vertext_id = closest[1] - 1 rb_points.append(adj_valves_ft.geometry().vertexAt(next_vertext_id)) rb_points.insert(1, self.selected_node_ft.geometry().asPoint()) # self.rubber_bands_d[0] = (self.build_rubber_band(rb_points), [1], [self.selected_node_ft.geometry().asPoint()]) self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) # Node adjacent to pump or valve and using block logic else: self.pump_valve_selected = True # Find the pipes adjacent to the pump/valve if self.adj_links_fts['pumps']: self.pump_valve_ft = self.adj_links_fts['pumps'][0] self.pump_or_valve = 'pump' adj_links = NetworkUtils.find_links_adjacent_to_link(self.params, self.params.pumps_vlay, self.pump_valve_ft, False, True, True) elif self.adj_links_fts['valves']: self.pump_valve_ft = self.adj_links_fts['valves'][0] self.pump_or_valve = 'valve' adj_links = NetworkUtils.find_links_adjacent_to_link(self.params, self.params.valves_vlay, self.pump_valve_ft, False, True, True) else: return pump_valve_pts = self.pump_valve_ft.geometry().asPolyline() # self.rubber_bands_d[0] = (self.build_rubber_band(pump_valve_pts), range(len(pump_valve_pts)), pump_valve_pts) self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) rb_index = 1 if 'pipes' in adj_links: for adj_link_ft in adj_links['pipes']: self.process_adj(adj_link_ft, self.params.pipes_vlay, rb_index) rb_index += 1 if 'pumps' in adj_links: for adj_link_ft in adj_links['pumps']: self.process_adj(adj_link_ft, self.params.pumps_vlay, rb_index) rb_index += 1 if 'valves' in adj_links: for adj_link_ft in adj_links['valves']: self.process_adj(adj_link_ft, self.params.valves_vlay, rb_index) rb_index += 1 # Find the nodes adjacent to the pump/valve self.adj_junctions = NetworkUtils.find_start_end_nodes_w_layer(self.params, self.pump_valve_ft.geometry()) def process_adj(self, adj_link_ft, layer, rb_index): adj_link_pts = adj_link_ft.geometry().asPolyline() pump_valve_pts = self.pump_valve_ft.geometry().asPolyline() if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[0]), adj_link_pts[0], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (0, layer) # rb_pts = [pump_valve_pts[0], adj_link_pts[1]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[0]]) if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[-1]), adj_link_pts[0], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (0, layer) # rb_pts = [pump_valve_pts[-1], adj_link_pts[1]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[-1]]) if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[0]), adj_link_pts[-1], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (len(adj_link_pts)-1, layer) # rb_pts = [pump_valve_pts[0], adj_link_pts[-2]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[0]]) if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[-1]), adj_link_pts[-1], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (len(adj_link_pts)-1, layer) # rb_pts = [pump_valve_pts[-1], adj_link_pts[-2]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[-1]]) def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) if elev is not None: self.elev = elev self.dock_widget.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.elev = None self.dock_widget.lbl_elev_val.setText('-') # Mouse not clicked if not self.mouse_clicked: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): self.snap_results = match # snapped_pt = self.snap_results[0].snappedVertex snapped_vertex = match.point() self.vertex_marker.setCenter(QgsPoint(snapped_vertex.x(), snapped_vertex.y())) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) # or ICON_CROSS, ICON_X self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: self.snap_results = None self.selected_node_ft = None self.vertex_marker.hide() # Mouse clicked else: # Update rubber band if self.snap_results is not None and self.rubber_band: snapped_pt = self.snap_results.point() # In 2.16+: self.delta_vec = QgsVector(self.mouse_pt - snapped_pt) self.delta_vec = QgsVector(self.mouse_pt.x() - snapped_pt.x(), self.mouse_pt.y() - snapped_pt.y()) self.move_rubber_band_pt(self.rubber_band) # if self.adj_links_fts is None or (not self.adj_links_fts['pipes'] and not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']): # # There are no adjacent links # self.move_rubber_band_pt(self.rubber_bands_d[0]) # else: # # # It's just a junction # if not self.pump_valve_selected: # for key, value in self.rubber_bands_d.iteritems(): # self.move_rubber_band_pt(self.rubber_bands_d[key]) # # # It's a pump/valve # else: # # # Adjacent links are neither pumps nor valves: find the two pipes adjacent to the node # # Or node adjacent to pump or valve and NOT using block logic # if (not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']) or not self.params.block_logic: # # for key, value in self.rubber_bands_d.iteritems(): # self.move_rubber_band_pt(self.rubber_bands_d[key]) # # in 2.16: self.rubber_bands_d[key].movePoint(1, snapped_pt + self.delta_vec) # # self.rubber_bands_d[key].movePoint(2, QgsPoint(snapped_pt.x() + self.delta_vec.x(), snapped_pt.y() + self.delta_vec.y())) # # # Node adjacent to pump or valve and using block logic # else: # for key, value in self.rubber_bands_d.iteritems(): # self.move_rubber_band_pt(self.rubber_bands_d[key]) def move_rubber_band_pt(self, rubber_band_v): # rubber_band = rubber_band_v[0] # pt_indices = rubber_band_v[1] # start_pts = rubber_band_v[2] # for i in range(len(pt_indices)): # rubber_band.movePoint(pt_indices[i], QgsPoint(start_pts[i].x() + self.delta_vec.x(), start_pts[i].y() + self.delta_vec.y())) rubber_band_v.movePoint(1, QgsPoint(self.clicked_pt.x() + self.delta_vec.x(), self.clicked_pt.y() + self.delta_vec.y())) def canvasReleaseEvent(self, event): mouse_pt = self.toMapCoordinates(event.pos()) if not self.mouse_clicked: return if event.button() == 1: self.mouse_clicked = False if self.snap_results is not None: snap_results = self.snap_results selected_node_ft = self.selected_node_ft selected_node_ft_lay = self.selected_node_ft_lay # Check elev if self.elev is None and self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', QgsMessageBar.WARNING, 5) # TODO: softcode # It's just a pipe vertex if selected_node_ft is None: feat = vector_utils.get_feats_by_id(snap_results.layer(), snap_results.featureId()) vertex_id = QgsVertexId(0, 0, snap_results.vertexIndex(), QgsVertexId.SegmentVertex) vertex_v2 = feat[0].geometry().geometry().vertexAt(vertex_id) new_pos_pt_v2 = QgsPointV2(mouse_pt.x(), mouse_pt.y()) new_pos_pt_v2.addZValue(vertex_v2.z()) LinkHandler.move_link_vertex(self.params, self.params.pipes_vlay, feat[0], new_pos_pt_v2, snap_results.vertexIndex()) # There are adjacent links: it's a node else: # Not pump or valve: plain junction if not self.pump_valve_selected: # Update junction geometry NodeHandler.move_element( selected_node_ft_lay, self.params.dem_rlay, selected_node_ft, mouse_pt) # Update pipes for feat, (vertex_index, layer) in self.adj_links_fts_d.iteritems(): vertex_id = QgsVertexId(0, 0, vertex_index, QgsVertexId.SegmentVertex) vertex_v2 = feat.geometry().geometry().vertexAt(vertex_id) new_pos_pt_v2 = QgsPointV2(mouse_pt.x(), mouse_pt.y()) new_pos_pt_v2.addZValue(vertex_v2.z()) LinkHandler.move_link_vertex(self.params, layer, feat, new_pos_pt_v2, vertex_index) # Pump or valve else: # Update junctions geometry NodeHandler.move_element(self.adj_junctions[0][1], self.params.dem_rlay, self.adj_junctions[0][0], QgsPoint( self.adj_junctions[0][0].geometry().asPoint().x() + self.delta_vec.x(), self.adj_junctions[0][0].geometry().asPoint().y() + self.delta_vec.y())) NodeHandler.move_element(self.adj_junctions[1][1], self.params.dem_rlay, self.adj_junctions[1][0], QgsPoint( self.adj_junctions[1][0].geometry().asPoint().x() + self.delta_vec.x(), self.adj_junctions[1][0].geometry().asPoint().y() + self.delta_vec.y())) # in 2.16: NodeHandler.move_element(Parameters.junctions_vlay, self.adj_links_fts[0], self.adj_links_fts[0].geometry().asPoint() + self.delta_vec) if self.pump_or_valve == 'pump': lay = self.params.pumps_vlay elif self.pump_or_valve == 'valve': lay = self.params.valves_vlay # Move the pump/valve LinkHandler.move_pump_valve(lay, self.pump_valve_ft, self.delta_vec) # Move the adjacent pipes' vertices for feat, (vertex_index, layer) in self.adj_links_fts_d.iteritems(): vertex_id = QgsVertexId(0, 0, vertex_index, QgsVertexId.SegmentVertex) vertex_v2 = feat.geometry().geometry().vertexAt(vertex_id) new_pos_pt_v2 = QgsPointV2( feat.geometry().vertexAt(vertex_index).x() + self.delta_vec.x(), feat.geometry().vertexAt(vertex_index).y() + self.delta_vec.y()) new_pos_pt_v2.addZValue(vertex_v2.z()) LinkHandler.move_link_vertex(self.params, layer, feat, new_pos_pt_v2, vertex_index) # In 2.16: LinkHandler.move_pipe_vertex(feat, feat.geometry().vertexAt(vertex_index) + self.delta_vec, vertex_index) self.adj_links_fts_d.clear() symbology.refresh_layer(self.iface.mapCanvas(), self.params.junctions_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.reservoirs_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.tanks_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pipes_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pumps_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.valves_vlay) # Remove vertex marker and rubber band self.vertex_marker.hide() self.iface.mapCanvas().scene().removeItem(self.rubber_band) def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) # Snapping layers = { self.params.junctions_vlay: QgsPointLocator.Vertex, self.params.reservoirs_vlay: QgsPointLocator.Vertex, self.params.tanks_vlay: QgsPointLocator.Vertex, self.params.pipes_vlay: QgsPointLocator.Vertex} self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.reservoirs_vlay.isEditable(): self.params.reservoirs_vlay.startEditing() if not self.params.tanks_vlay.isEditable(): self.params.tanks_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.pumps_vlay.isEditable(): self.params.pumps_vlay.startEditing() if not self.params.valves_vlay.isEditable(): self.params.valves_vlay.startEditing() def deactivate(self): # self.rubber_bands.clear() self.canvas().scene().removeItem(self.vertex_marker) self.dock_widget.btn_move_element.setChecked(False) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def build_rubber_band(self, points): rubber_band = QgsRubberBand(self.canvas(), False) # False = not a polygon rubber_band.setToGeometry(QgsGeometry.fromPolyline(points), None) # for point in points: # rubber_band.addPoint(point) rubber_band.setColor(QColor(255, 128, 128)) rubber_band.setWidth(1) rubber_band.setBrushStyle(Qt.Dense4Pattern) return rubber_band
class MoveNodeMapTool(ParentMapTool): ''' Button 16. Move node Execute SQL function: 'gw_fct_node2arc' ''' def __init__(self, iface, settings, action, index_action, controller, srid): ''' Class constructor ''' # Call ParentMapTool constructor super(MoveNodeMapTool, self).__init__(iface, settings, action, index_action) self.srid = srid # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setIconSize(9) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Rubber band self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) mFillColor = QColor(255, 0, 0); self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(3) self.reset() def reset(self): # Clear selected features layer = self.canvas.currentLayer() if layer is not None: layer.removeSelection() # Graphic elements self.rubberBand.reset() def move_node(self, node_id, point): ''' Move selected node to the current point ''' if self.srid is None: self.srid = self.settings.value('db/srid') if self.schema_name is None: self.schema_name = self.settings.value('db/schema_name') # Update node geometry the_geom = "ST_GeomFromText('POINT("+str(point.x())+" "+str(point.y())+")', "+str(self.srid)+")"; sql = "UPDATE "+self.schema_name+".node SET the_geom = "+the_geom sql+= " WHERE node_id = '"+node_id+"'" status = self.controller.execute_sql(sql) if status: # Execute SQL function and show result to the user function_name = "gw_fct_node2arc" sql = "SELECT "+self.schema_name+"."+function_name+"('"+str(node_id)+"');" self.controller.execute_sql(sql) else: message = "Move node: Error updating geometry" self.controller.show_warning(message, context_name='ui_message') # Refresh map canvas self.canvas.currentLayer().triggerRepaint() ''' QgsMapTool inherited event functions ''' def activate(self): ''' Called when set as currently active map tool ''' # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to node self.snapperManager.snapToNode() self.snapperManager.snapToArc() # Change pointer cursor = QCursor() cursor.setShape(Qt.CrossCursor) # Get default cursor self.stdCursor = self.parent().cursor() # And finally we set the mapTool's parent cursor self.parent().setCursor(cursor) # Reset self.reset() # Show help message when action is activated if self.show_help: message = "Select the disconnected node by clicking on it, move the pointer to desired location inside a pipe and click again" self.controller.show_info(message, context_name='ui_message' ) # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_node) except: self.canvas.setCurrentLayer(self.layer_node) def deactivate(self): ''' Called when map tool is being deactivated ''' # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) try: self.rubberBand.reset(QGis.Line) except AttributeError: pass def canvasMoveEvent(self, event): ''' Mouse movement event ''' # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Node layer layer = self.canvas.currentLayer() if layer is None: return # Select node or arc if layer.selectedFeatureCount() == 0: # Snap to node (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_node.name()): point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setCenter(point) self.vertexMarker.show() # Set a new point to go on with #self.appendPoint(point) self.rubberBand.movePoint(point) else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) else: # Snap to arc result = [] (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if (result <> []) and (result[0].layer.name() == self.layer_arc.name()) and (result[0].snappedVertexNr == -1): point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setColor(QColor(255, 0, 0)) self.vertexMarker.setCenter(point) self.vertexMarker.show() # Select the arc self.layer_arc.removeSelection() self.layer_arc.select([result[0].snappedAtGeometry]) # Bring the rubberband to the cursor i.e. the clicked point self.rubberBand.movePoint(point) else: # Bring the rubberband to the cursor i.e. the clicked point point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) def canvasReleaseEvent(self, event): ''' Mouse release event ''' if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Node layer layer = self.canvas.currentLayer() # Select node or arc if layer.selectedFeatureCount() == 0: # Snap to node (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_node.name()): point = QgsPoint(result[0].snappedVertex) layer.select([result[0].snappedAtGeometry]) # Hide highlight self.vertexMarker.hide() # Set a new point to go on with self.rubberBand.addPoint(point) else: # Snap to arc (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if (result <> []) and (result[0].layer.name() == self.layer_arc.name()): point = QgsPoint(result[0].snappedVertex) # Get selected feature (at this moment it will have one and only one) feature = layer.selectedFeatures()[0] node_id = feature.attribute('node_id') # Move selected node to the released point self.move_node(node_id, point) # Rubberband reset self.reset() # Refresh map canvas self.iface.mapCanvas().refresh() elif event.button() == Qt.RightButton: self.reset()
class QGISRedMoveVertexsTool(QgsMapTool): ownMainLayers = ["Pipes", "Valves", "Pumps"] def __init__(self, button, iface, projectDirectory, netwName): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.toolbarButton = button self.snapper = None self.vertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.vertexMarker.setColor(QColor(255, 87, 51)) self.vertexMarker.setIconSize(15) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(3) self.vertexMarker.hide() self.mouseClicked = False self.clickedPoint = None self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.newPositionVector = QgsVector(0, 0) self.rubberBand = None self.newVertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.newVertexMarker.setColor(QColor(55, 198, 5)) self.newVertexMarker.setIconSize(15) self.newVertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.newVertexMarker.setPenWidth(3) self.newVertexMarker.hide() def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) myLayers = [] # Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath(self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: myLayers.append(layer) if not layer.isEditable(): layer.startEditing() # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(2) # Vertex config.setMode(2) # All layers config.setTolerance(1) config.setUnits(2) # Pixels config.setEnabled(True) self.snapper.setConfig(config) def deactivate(self): self.toolbarButton.setChecked(False) # End Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath(self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: if layer.isModified(): layer.commitChanges() else: layer.rollBack() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def getUniformedPath(self, path): return QGISRedUtils().getUniformedPath(path) def getLayerPath(self, layer): return QGISRedUtils().getLayerPath(layer) def generatePath(self, folder, fileName): return QGISRedUtils().generatePath(folder, fileName) def getLayers(self): return QGISRedUtils().getLayers() def areOverlapedPoints(self, point1, point2): tolerance = 0.1 if point1.distance(point2) < tolerance: return True else: return False def isInPath(self, point1, point2, myPoint): width = point2.x() - point1.x() height = point2.y() - point1.y() widthM = myPoint.x() - point1.x() heightM = myPoint.y() - point1.y() if abs(width) >= abs(height): yEst = widthM * height / width + point1.y() if abs(yEst - myPoint.y()) < 1E-9: return True else: xEst = heightM * width / height + point1.x() if abs(xEst - myPoint.x()) < 1E-9: return True return False def createRubberBand(self, points): myPoints = points if isinstance(points[0], QgsPointXY): myPoints = [] for p in points: myPoints.append(QgsPoint(p.x(), p.y())) self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand.setToGeometry(QgsGeometry.fromPolyline(myPoints), None) self.rubberBand.setColor(QColor(55, 198, 5)) self.rubberBand.setWidth(1) self.rubberBand.setLineStyle(Qt.DashLine) self.newVertexMarker.setCenter( QgsPointXY(points[0].x(), points[0].y())) self.newVertexMarker.show() def updateRubberBand(self): newX = self.clickedPoint.x() + self.newPositionVector.x() newY = self.clickedPoint.y() + self.newPositionVector.y() self.rubberBand.movePoint(1, QgsPointXY(newX, newY)) self.newVertexMarker.setCenter(QgsPointXY(newX, newY)) def moveVertexLink(self, layer, feature, newPosition, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.moveVertex(newPosition.x(), newPosition.y(), feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def deleteVertexLink(self, layer, feature, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.deleteVertex(feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def insertVertexLink(self, layer, feature, newPoint): if layer.isEditable(): layer.beginEditCommand("Update link geometry") vertex = -1 if layer.geometryType() == 1: # Line featureGeometry = self.selectedFeature.geometry() if featureGeometry.isMultipart(): parts = featureGeometry.get() for part in parts: # only one part for i in range(len(part)-1): if self.isInPath(QgsPointXY(part[i].x(), part[i].y()), QgsPointXY(part[i+1].x(), part[i+1].y()), newPoint): vertex = i+1 try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.insertVertex( newPoint.x(), newPoint.y(), feature.id(), vertex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() """Events""" def canvasPressEvent(self, event): if self.objectSnapped is None: self.clickedPoint = None return if event.button() == Qt.RightButton: self.mouseClicked = False self.clickedPoint = None if event.button() == Qt.LeftButton: self.clickedPoint = self.objectSnapped.point() if self.vertexIndex == -1: return self.mouseClicked = True self.createRubberBand( [self.objectSnapped.point(), self.objectSnapped.point()]) def canvasMoveEvent(self, event): mousePoint = self.toMapCoordinates(event.pos()) # Mouse not clicked if not self.mouseClicked: matchSnapper = self.snapper.snapToMap(mousePoint) if matchSnapper.isValid(): valid = False layer = matchSnapper.layer() snapLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath(self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if snapLayerPath == layerPath: valid = True if valid: self.objectSnapped = matchSnapper self.selectedLayer = layer vertex = matchSnapper.point() featureId = matchSnapper.featureId() request = QgsFeatureRequest().setFilterFid(featureId) nodes = list(layer.getFeatures(request)) self.selectedFeature = QgsFeature(nodes[0]) # #Ver aquí si es el nudo inicial y final middleNode = False self.vertexIndex = -1 if layer.geometryType() == 1: # Line featureGeometry = self.selectedFeature.geometry() if featureGeometry.isMultipart(): parts = featureGeometry.get() for part in parts: # only one part if middleNode: break i = -1 for v in part: i = i+1 if i == 0 or i == len(part)-1: continue matchedPoint = QgsPointXY(vertex.x(), vertex.y()) if self.areOverlapedPoints(QgsGeometry.fromPointXY(matchedPoint), QgsGeometry.fromPointXY(QgsPointXY(v.x(), v.y()))): middleNode = True self.vertexIndex = i break if middleNode: self.vertexMarker.setCenter( QgsPointXY(vertex.x(), vertex.y())) self.vertexMarker.show() else: self.vertexMarker.hide() else: self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexMarker.hide() else: self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexMarker.hide() # Mouse clicked else: # # Update rubber band if self.objectSnapped is not None and self.rubberBand is not None: snappedPoint = self.objectSnapped.point() self.newPositionVector = QgsVector(mousePoint.x() - snappedPoint.x(), mousePoint.y() - snappedPoint.y()) self.updateRubberBand() def canvasReleaseEvent(self, event): mousePoint = self.toMapCoordinates(event.pos()) if self.mouseClicked: if event.button() == 1: self.mouseClicked = False if self.objectSnapped is not None: self.moveVertexLink(self.selectedLayer, self.selectedFeature, mousePoint, self.vertexIndex) elif event.button() == 2: if self.objectSnapped is not None: self.deleteVertexLink(self.selectedLayer, self.selectedFeature, self.vertexIndex) elif event.button() == 1: if self.objectSnapped is not None: self.insertVertexLink(self.selectedLayer, self.selectedFeature, self.objectSnapped.point()) self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexIndex = -1 self.iface.mapCanvas().refresh() # Remove vertex marker and rubber band self.vertexMarker.hide() self.iface.mapCanvas().scene().removeItem(self.rubberBand) self.newVertexMarker.hide()
class KPTool(QgsMapToolEmitPoint): """Class to interact with the map canvas to capture the coordinate when the mouse button is pressed and to display the coordinate in in the status bar. It will take all the things from Class QgsMapToolEmitPoint and overwrite some of the functions, activate, etc.. Other functions here do the measurements """ captureStopped = pyqtSignal() epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326") geod = Geodesic.WGS84 def __init__(self, iface, linelayer=None): QgsMapToolEmitPoint.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.marker = None self.vertex = None self.linelayer = linelayer #import a line layer from the main function self.proj_crs = iface.mapCanvas().mapSettings().destinationCrs() def __str__(self, linelayer): return linelayer.name() def activate(self): '''When activated set the cursor to a crosshair.''' self.canvas.setCursor(Qt.CrossCursor) self.snapcolor = QgsSettings().value("/qgis/digitizing/snap_color", QColor(Qt.magenta)) def deactivate(self): self.removeMarker() self.removeVertexMarker() self.captureStopped.emit() def canvasMoveEvent(self, event): '''Capture the coordinate as the user moves the mouse over the canvas. Show it in the status bar. Currently not used''' pt = self.snappoint( event.originalPixelPoint()) # input is QPoint, cursor snaps def snappoint(self, qpoint): match = self.canvas.snappingUtils().snapToMap(qpoint) if match.isValid(): if self.vertex is None: self.vertex = QgsVertexMarker(self.canvas) self.vertex.setIconSize(12) self.vertex.setPenWidth(2) self.vertex.setColor(self.snapcolor) self.vertex.setIconType(QgsVertexMarker.ICON_BOX) self.vertex.setCenter(match.point()) return (match.point()) # Returns QgsPointXY else: self.removeVertexMarker() return self.toMapCoordinates( qpoint) # QPoint input, returns QgsPointXY def canvasReleaseEvent(self, event): """Capture the coordinate when the mouse button has been released, format it, and copy it to the clipboard. pt is QgsPointXY""" pt = self.snappoint(event.originalPixelPoint()) self.removeVertexMarker() # if we enable this we will get some vertex markers: # if self.marker is None: # self.marker = QgsVertexMarker(self.canvas) # self.marker.setIconSize(18) # self.marker.setPenWidth(2) # self.marker.setIconType(QgsVertexMarker.ICON_CROSS) # self.marker.setCenter(pt) if self.geodetic_use == 0: # use cartesian line_length = self.measureLine(self.linelayer, pt, False) whole_line_length = self.measureWholeLine(self.linelayer, False) dist_off_Line = self.closestPt( self.linelayer, pt, True, False)[1] #second False means we use Cartesian distance elif self.geodetic_use == 2: # use geodetic line_length = self.measureLine(self.linelayer, pt, True) whole_line_length = self.measureWholeLine(self.linelayer, True) dist_off_Line = self.closestPt(self.linelayer, pt, True, True)[1] if self.reversekp_status == 0: LL_message = '{:.{prec}f}'.format( line_length + self.offset, prec=self.kpdec) #round to specified decimal elif self.reversekp_status == 2: LL_message = '{:.{prec}f}'.format( whole_line_length - line_length + self.offset, prec=self.kpdec) #round to specified decimal DT_message = '{:.{prec}f}'.format( dist_off_Line, prec=self.dccdec) #round to specified decimal # check for output format below: if self.out_format == KpFindDialogInteractive.KP_out: # KP msg = 'KP ' + str(LL_message) elif self.out_format == KpFindDialogInteractive.KP_DCC_Out: # KP and DCC msg = 'KP ' + str(LL_message) + ' , DOL : ' + str( DT_message) + ' m' elif self.out_format == KpFindDialogInteractive.DMS_out: # Lat Lon and KP msg = self.formatCoord(pt)[0] + ', ' + self.formatCoord( pt)[1] + ' (KP ' + str(LL_message) + ')' if msg is not None: clipboard = QApplication.clipboard() clipboard.setText(msg) self.iface.messageBar().pushMessage(msg + ' on layer ' + str(self.linelayer.name()) + ' copied to clipboard', level=Qgis.Info, duration=2) else: self.iface.messageBar().pushMessage( 'Something went wrong with the coordinate composition', level=Qgis.Info, duration=2) def removeMarker(self): if self.marker is not None: self.canvas.scene().removeItem(self.marker) self.marker = None def removeVertexMarker(self): if self.vertex is not None: self.canvas.scene().removeItem(self.vertex) self.vertex = None def name_collision_checker(self, new_names, old_layer): #check for name collisions in attribute fields fields_ptLayer = old_layer.dataProvider().fields() old_fields_names = [ ] #we will use this later to compare for collisions for f in fields_ptLayer: #iterate over all field names and add to new provider old_fields_names.append(f.name()) #create the collision test list trynumber = 1 num_suffix = '' max_num = 1 for name in new_names: collision = True while collision: # Iterate until there are no collissions collision = False if name + num_suffix in old_fields_names: collision = True num_suffix = '_' + str(trynumber) if trynumber > max_num + 1: max_num = trynumber num_suffix = '_' + str(max_num) trynumber = trynumber + 1 new_list = [item + num_suffix for item in new_names] #add same suffix to all fields return new_list def closestPt(self, linelayer, clicked_point, extend_line=False, give_geod=True): # get the linelayer and QgsPointXY """This will take a point and a line layer and find the closest point along a perpendicular line from the point to the line layer. It takes the first feature in that line layer. It will also extend the first and last line segments. Cartesian and geodetic distance possible""" if linelayer.getFeature(0).geometry().isMultipart(): line_feat = linelayer.getFeature(0) linear_geom = line_feat.geometry() else: line_feat = linelayer.getFeature( 1) #for some reason linestring feature 0 is NULL linear_geom = line_feat.geometry() linear_geom.convertToMultiType() if extend_line == True: linear_geom = linear_geom.extendLine( 1000000, 10000000) # extend fist and last seg by 1000km, bit of a fudge _, mindistpt, _, leftoff = linear_geom.closestSegmentWithContext( clicked_point) # get min distance point to line projected_point = QgsPointXY( mindistpt[0], mindistpt[1]) # create projected point on line distance_cart = QgsDistanceArea().measureLine( clicked_point, projected_point) #cartesian distance srcCRS = linelayer.sourceCrs() # get CRS from line wgs84 = KPTool.epsg4326 # define EPSG 4326 if srcCRS != wgs84: geomTo4326 = QgsCoordinateTransform( srcCRS, wgs84, QgsProject.instance()) #convert if needed ptCP84 = geomTo4326.transform(clicked_point) ptPP84 = geomTo4326.transform(projected_point) else: ptCP84 = clicked_point ptPP84 = projected_point geod_ds = KPTool.geod.Inverse(ptCP84.y(), ptCP84.x(), ptPP84.y(), ptPP84.x()) # use geographiclib distance_geod = geod_ds['s12'] if leftoff < 0: distance_cart = -distance_cart #planar distance distance_geod = -distance_geod #return the geographiclib distance #if abs(distance_cart)>1000000 or abs(distance_geod)>1000000: #self.iface.messageBar().pushMessage('Line extended more than 1000 km, possible errors',level=Qgis.Warning) #warn user if they click more than 1000 km away from end of line if give_geod is True: return projected_point, distance_geod else: return projected_point, distance_cart def measureLine(self, linetoMeasure, clicked_pt, geodetic_measure=True): """This will take a line segment and the clicked point and return the length up till that point for the segment in km. Inspired by shape tools and closest point plugins""" srcCRS = linetoMeasure.sourceCrs() #get CRS from line feature = linetoMeasure.getFeature(0) #get first feature from line wgs84 = KPTool.epsg4326 #define EPSG 4326 projected_point_raw, dist_ext = self.closestPt( linetoMeasure, clicked_pt, True ) #we'll need that point later to compare with projected points of subsegments projected_point_on_line_raw, dist_nonext = self.closestPt( linetoMeasure, clicked_pt, False) #get proj point on non-extended line if srcCRS != wgs84: geomTo4326 = QgsCoordinateTransform( srcCRS, wgs84, QgsProject.instance()) #convert if needed if feature.geometry().isMultipart(): # get nodes out of data ptdata = [feature.geometry().asMultiPolyline()] else: feature = linetoMeasure.getFeature( 1) #for some reason singleline first feature is NULL ptdata = [[feature.geometry().asPolyline()]] for seg in ptdata: if len(seg) < 1: # should never happen self.iface.messageBar().pushMessage( 'Something is strange with your line file', level=Qgis.Critical, duration=2) continue for pts in seg: # now we get all nodes from start to end of segment line numpoints = len(pts) if numpoints < 2: # should never happen self.iface.messageBar().pushMessage( 'Something is strange with your line file', level=Qgis.Critical, duration=2) continue ptStart_raw = QgsPointXY(pts[0].x(), pts[0].y()) #get initial point coords # Calculate the total distance of this line segment distance = 0.0 for x in range(1, numpoints): # from point one (since we preextend segment) start measuring distance cumulative ptEnd_raw = QgsPointXY( pts[x].x(), pts[x].y()) # coordinates of next point P1 = QgsPoint(ptStart_raw.x(), ptStart_raw.y()) P2 = QgsPoint(ptEnd_raw.x(), ptEnd_raw.y()) seg_geom = QgsGeometry.fromPolyline( (P1, P2)) # generate geometry for current subsegment _, mindistpt, _, _ = seg_geom.closestSegmentWithContext( projected_point_raw) TestPoint = QgsPointXY( mindistpt[0], mindistpt[1]) #create projected point on subsegment ptStart = ptStart_raw ptEnd = ptEnd_raw projected_point = projected_point_raw projected_point_on_line = projected_point_on_line_raw if geodetic_measure == True: if srcCRS != wgs84: # Convert to 4326 - just to be safe when using geodetic measure ptStart = geomTo4326.transform(ptStart_raw) ptEnd = geomTo4326.transform(ptEnd_raw) projected_point = geomTo4326.transform( projected_point_raw) projected_point_on_line = geomTo4326.transform( projected_point_on_line_raw) TestPoint = geomTo4326.transform(TestPoint) d_to_click = KPTool.geod.Inverse( ptStart.y(), ptStart.x(), projected_point_on_line.y(), projected_point_on_line.x())['s12'] len_subsegment = KPTool.geod.Inverse( ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x() )['s12'] # geodetic distance between begin and end of subsegment test_distance = KPTool.geod.Inverse( projected_point_on_line.y(), projected_point_on_line.x(), TestPoint.y(), TestPoint.x() )['s12'] # check distance between two on-subsegment-points else: d_to_click = QgsDistanceArea().measureLine( ptStart, projected_point_on_line) len_subsegment = QgsDistanceArea().measureLine( ptStart, ptEnd ) # cartesian distance between begin and end point test_distance = QgsDistanceArea().measureLine( projected_point_on_line, TestPoint ) # check distance between two on-subsegment-points round_test_distance = round( test_distance, 6 ) # round so we can get zero, otherwise it will be a small fraction if round_test_distance != 0: # not yet the last segment to be measured distance += len_subsegment #add to comulative else: # this is the segment, where we have to stop measuring distance += d_to_click break # break loop and give distance so far ptStart_raw = ptEnd_raw # make ready for next pair of points if QgsPoint(projected_point) != QgsPoint( projected_point_on_line): # if our point on the extended line is different, the projected point is before start or after end of line layer if geodetic_measure == True: extra_dist = KPTool.geod.Inverse( projected_point.y(), projected_point.x(), projected_point_on_line.y(), projected_point_on_line.x())['s12'] else: extra_dist = QgsDistanceArea().measureLine( projected_point, projected_point_on_line) if round(distance, 6) == 0: # we are at the start of the line layer, the for loops hasn't done anything, so negative distance to starting node distance = -extra_dist else: # we are at the end of the line segment, so need to add more distance distance = distance + extra_dist out = distance / 1000 # Distance converted to KM return out def kpIteratePts(self, linetoMeasurekp4p, ptLayer): """will iterate over all points in a layer, finding distance to and along line. Outputs a new layer.""" new_pt_layer = QgsVectorLayer( 'Point', 'KPed_' + str(ptLayer.name()), 'memory') # define new layer we will return new_pt_layer.setCrs(ptLayer.crs()) # get crs from input layer prov_old = ptLayer.dataProvider( ) # provider to get the attribute field names and type provider_ptLayer = new_pt_layer.dataProvider( ) # provider for new layer to add the features to fields_ptLayer = prov_old.fields() for f in fields_ptLayer: # iterate over all field names and add to new provider znameField = f.name() type_field = str(f.typeName()) if type_field == 'Integer': provider_ptLayer.addAttributes( [QgsField(znameField, QVariant.Int)]) if type_field == 'Real': provider_ptLayer.addAttributes( [QgsField(znameField, QVariant.Double)]) if type_field == 'String': provider_ptLayer.addAttributes( [QgsField(znameField, QVariant.String)]) else: provider_ptLayer.addAttributes( [QgsField(znameField, QVariant.String)]) iterate_names_list = ["KP", "DOL", "Latitude", "Longitude"] new_attr_names = self.name_collision_checker( iterate_names_list, ptLayer) # check if the new names we add already exist provider_ptLayer.addAttributes([ QgsField(new_attr_names[0], QVariant.Double), QgsField(new_attr_names[1], QVariant.Double), QgsField(new_attr_names[2], QVariant.String), QgsField(new_attr_names[3], QVariant.String) ]) # four new fields we are calculating new_pt_layer.startEditing() for old_feat in ptLayer.getFeatures(): # iterate over all point features geom_of = old_feat.geometry() point_of = geom_of.asPoint() # get point object new_feat = QgsFeature() new_feat.setGeometry( geom_of) # create and set geometry from old feature attributes_nf = old_feat.attributes( ) # copy of old feat attributes if self.geodetic_usekp4p == 0: # we measure Cartesian coords line_length = self.measureLine(self.linelayer, point_of, False) + self.offsetkp4p whole_line_length = self.measureWholeLine( self.linelayer, False) dcc_dist = self.closestPt( linetoMeasurekp4p, point_of, True, False)[1] # second False means we use Cartesian distance elif self.geodetic_usekp4p == 2: # we measure geodetic coords line_length = self.measureLine(linetoMeasurekp4p, point_of, True) + self.offsetkp4p whole_line_length = self.measureWholeLine(self.linelayer, True) dcc_dist = self.closestPt(linetoMeasurekp4p, point_of, True, True)[1] if self.reversekp_statuskp4p == 0: kp_dist = line_length elif self.reversekp_statuskp4p == 2: kp_dist = whole_line_length - line_length attributes_nf.append(round( kp_dist, self.kpdeckp4p)) # round with precision values attributes_nf.append(round( dcc_dist, self.dccdeckp4p)) # round with precision values attributes_nf.append( self.formatCoord(point_of)[0]) # insert latitude attributes_nf.append( self.formatCoord(point_of)[1]) # insert longitude new_feat.setAttributes( attributes_nf) # set new attributes to new feat provider_ptLayer.addFeatures([new_feat ]) # add new feature to provider new_pt_layer.commitChanges() # finish editing layer return new_pt_layer # return the new layer with new features with new attributes def formatCoord(self, pt): canvasCRS = self.canvas.mapSettings().destinationCrs() epsg4326 = QgsCoordinateReferenceSystem('EPSG:4326') # convert point to wgs84 for conversion if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) lat = self.convertDD2DM(pt4326.y(), True, 4) lon = self.convertDD2DM(pt4326.x(), False, 4) return [lat, lon] def convertDD2DM(self, coord, islat, prec): """Convert decimal degrees to DM - taken from latlontools plugin""" if islat: if coord < 0: unit = 'S' else: unit = 'N' else: if coord > 0: unit = 'E' else: unit = 'W' dmsSpace = ' ' # put some spaces in there zeroes = 1 # this will be used for padding coord = math.fabs(coord) deg = math.floor(coord) dmin = (coord - deg) * 60.0 min = math.floor(dmin) sec = (dmin - min) * 60.0 s = "" # Properly handle rounding based on the digit precision d = "{:.{prec}f}".format(dmin, prec=prec) if float(d) == 60: deg += 1 dmin = 0 if islat: s = '{:0{}.0f}\xB0{}{:0{}.0{prec}f}\'{}{}'.format(deg, zeroes * 2, dmsSpace, dmin, prec + zeroes * 3, dmsSpace, unit, prec=prec) else: s = '{:0{}.0f}\xB0{}{:0{}.0{prec}f}\'{}{}'.format(deg, zeroes * 3, dmsSpace, dmin, prec + zeroes * 3, dmsSpace, unit, prec=prec) return (s) def measureWholeLine(self, linetoMeasure, geodetic_measure=True): """This will take a line segment and return its geodetic length""" srcCRS = linetoMeasure.sourceCrs() #get CRS from line feature = linetoMeasure.getFeature(0) #get first feature from line wgs84 = KPTool.epsg4326 #define EPSG 43226 if feature.geometry().isMultipart(): #get nodes out of data ptdata = [feature.geometry().asMultiPolyline()] else: feature = linetoMeasure.getFeature(1) ptdata = [[feature.geometry().asPolyline()]] for seg in ptdata: if len(seg) < 1: #should never happen self.iface.messageBar().pushMessage( 'Something is strange with your line file', level=Qgis.Critical, duration=2) continue for pts in seg: # now we get all nodes from start to end of line numpoints = len(pts) if numpoints < 2: # should never happen self.iface.messageBar().pushMessage( 'Something is strange with your line file', level=Qgis.Critical, duration=2) continue ptStart_raw = QgsPointXY( pts[0].x(), pts[0].y()) # get initial point coords # Calculate the total distance of this line segment distance = 0.0 for x in range(1, numpoints): # from point one (since we preextend segment), cumulative ptEnd_raw = QgsPointXY( pts[x].x(), pts[x].y()) # coordinates of next point if geodetic_measure == True: if srcCRS != wgs84: # Convert to 4326 - just to be safe when using geodetic measure geomTo4326 = QgsCoordinateTransform( srcCRS, wgs84, QgsProject.instance()) # convert if needed ptStart = geomTo4326.transform(ptStart_raw) ptEnd = geomTo4326.transform(ptEnd_raw) length = KPTool.geod.Inverse( ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x() )['s12'] # geodetic distance between begin and end of subsegment else: length = QgsDistanceArea().measureLine( ptStart_raw, ptEnd_raw) distance += length # add to cumulative ptStart_raw = ptEnd_raw # make ready for next pair of points out = distance / 1000 # Distance converted KM return out def putKPPointsAlongLine(self, source, maxseglen): "We travel along the line and test if each consecutive segment should contain KPs and how many" layercrs = source.sourceCrs() if layercrs != KPTool.epsg4326: transto4326 = QgsCoordinateTransform(layercrs, KPTool.epsg4326, QgsProject.instance()) transfrom4326 = QgsCoordinateTransform(KPTool.epsg4326, layercrs, QgsProject.instance()) new_pt_layer = QgsVectorLayer( 'Point', 'KP_points_' + str(source.name()), 'memory') # define new layer we will return new_pt_layer.setCrs(source.crs()) # get crs from input layer new_pt_layer.startEditing() provider_ptLayer = new_pt_layer.dataProvider( ) # provider for new layer to add the features to provider_ptLayer.addAttributes([QgsField('KP', QVariant.Double)]) iterator = source.getFeatures() KP_label_count = 0 for cnt, feature in enumerate(iterator): if feature.geometry().isMultipart(): seg = feature.geometry().asMultiPolyline() else: seg = [feature.geometry().asPolyline()] numseg = len(seg) if numseg < 1 or len(seg[0]) < 2: self.iface.messageBar().pushMessage( 'Less than one segment in line layer', level=Qgis.Critical, duration=2) continue for line in seg: numpoints = len(line) if self.Reverse_KP_points == 2: #reverse point order for reverse KP _ = line.reverse() ptStart = QgsPointXY(line[0][0], line[0][1]) new_kp_point = self.createFeatureFromPoint( ptStart, KP_label_count) provider_ptLayer.addFeatures([new_kp_point]) remaining_dist = 0.0 #remaining distance to next point if layercrs != KPTool.epsg4326: # Convert to 4326 ptStart = transto4326.transform(ptStart) for x in range(1, numpoints): ptEnd = QgsPointXY(line[x][0], line[x][1]) if layercrs != KPTool.epsg4326: # Convert to 4326 ptEnd = transto4326.transform(ptEnd) gline = KPTool.geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) if remaining_dist + gline.s13 > maxseglen: #we have to place at least one KP s = maxseglen - remaining_dist g = gline.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) ptKP = QgsPointXY(g['lon2'], g['lat2']) if layercrs != KPTool.epsg4326: ptKP = transfrom4326.transform(ptKP) KP_label_count = KP_label_count + maxseglen remaining_dist = remaining_dist + gline.s13 - maxseglen new_kp_point = self.createFeatureFromPoint( ptKP, KP_label_count) provider_ptLayer.addFeatures([new_kp_point]) if remaining_dist > maxseglen: #we need to place more KP pts extra_from_start = s n = int(remaining_dist / maxseglen) for i in range(0, n): s = maxseglen * (i + 1) + extra_from_start g = gline.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) ptKP = QgsPointXY(g['lon2'], g['lat2']) if layercrs != KPTool.epsg4326: # Convert each point back to the output CRS ptKP = transfrom4326.transform(ptKP) KP_label_count = KP_label_count + maxseglen remaining_dist = remaining_dist - maxseglen new_kp_point = self.createFeatureFromPoint( ptKP, KP_label_count) provider_ptLayer.addFeatures([new_kp_point]) else: #no KPs placed in this segment, keep the cumulative distance remaining_dist = remaining_dist + gline.s13 ptStart = ptEnd new_pt_layer.commitChanges() return new_pt_layer def putKPPointsAlongLineCart(self, source, maxseglen): "this uses the .interpolate method from the QgsGeometry class, which works well in Cartesian coordinates" new_pt_layer = QgsVectorLayer( 'Point', 'KP_points_' + str(source.name()), 'memory') # define new layer we will return new_pt_layer.setCrs(source.crs()) # get crs from input layer new_pt_layer.startEditing() provider_ptLayer = new_pt_layer.dataProvider( ) # provider for new layer to add the features to provider_ptLayer.addAttributes([QgsField('KP', QVariant.Double)]) iterator = source.getFeatures() for cnt, feature in enumerate(iterator): if feature.geometry().isMultipart(): seg = feature.geometry() else: seg = feature.geometry() seg.convertToMultiType() if self.Reverse_KP_points == 2: #reverse point order for reverse KP seg = self.reverseLineDirection(seg) n = int(seg.length() / maxseglen) KP_label_count = -maxseglen # so we get zero with first interp point for i in range(0, n + 1): ptKP = seg.interpolate(i * maxseglen) ptKP = ptKP.asPoint() KP_label_count = KP_label_count + maxseglen new_kp_point = self.createFeatureFromPoint( ptKP, KP_label_count) provider_ptLayer.addFeatures([new_kp_point]) new_pt_layer.commitChanges() return new_pt_layer def createFeatureFromPoint(self, point, attributes): "takes an QgsPointXY and makes it a feature, adds attributes" new_feat = QgsFeature() new_feat.setGeometry(QgsGeometry.fromPointXY( point)) # create and set geometry from old feature new_feat.setAttributes([round(attributes, 3)]) return new_feat def reverseLineDirection(self, line_geom): mls1 = line_geom.get() mls2 = QgsMultiLineString() # For each reversed linestring, visited in reverse order for i in [QgsLineString([*i][::-1]) for i in [*mls1][::-1]]: _ = mls2.addGeometry(i) # add it to new geometry new_geometry = QgsGeometry(mls2) return new_geometry
class W3WCoordInputDialog(QDockWidget): def __init__(self, canvas, parent): self.canvas = canvas self.marker = None QDockWidget.__init__(self, parent) self.setAllowedAreas(Qt.TopDockWidgetArea) self.initGui() def setApiKey(self, apikey): self.w3w = what3words(apikey=apikey) def initGui(self): self.setWindowTitle("Zoom to 3 word address") self.label = QLabel('3 Word Address') self.coordBox = QLineEdit() ''' When the button is pressed, we try to zoom to the coordinate entered by the user. That is done in the zoomToPressed method, but since this is a method that might take a while to be executed (it connects the w3w API), we use the 'execute' function in the qgiscommons library, which will take care of changing the mouse pointer to an hourglass until the method execution is finished. ''' self.coordBox.returnPressed.connect( lambda: execute(self.zoomToPressed)) self.zoomToButton = QPushButton("Zoom to") self.zoomToButton.clicked.connect(self.zoomToPressed) self.removeMarkerButton = QPushButton("Remove marker") self.removeMarkerButton.clicked.connect(self.removeMarker) self.removeMarkerButton.setDisabled(True) self.hlayout = QHBoxLayout() self.hlayout.setSpacing(6) self.hlayout.setMargin(9) self.hlayout.addWidget(self.label) self.hlayout.addWidget(self.coordBox) self.hlayout.addWidget(self.zoomToButton) self.hlayout.addWidget(self.removeMarkerButton) self.dockWidgetContents = QWidget() self.dockWidgetContents.setLayout(self.hlayout) self.setWidget(self.dockWidgetContents) def zoomToPressed(self): try: '''We convert the w3w address into a EPSG:4326 coord''' w3wCoord = str(self.coordBox.text()).replace(" ", "") json = self.w3w.forwardGeocode(w3wCoord) lat = float(json["geometry"]["lat"]) lon = float(json["geometry"]["lng"]) ''' We convert the 4326 coord into a coord in the CRS of the canvas ''' canvasCrs = self.canvas.mapSettings().destinationCrs() epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326") transform4326 = QgsCoordinateTransform(epsg4326, canvasCrs) center = transform4326.transform(lon, lat) '''We zoom to that coord and set a marker''' self.canvas.zoomByFactor(1, center) self.canvas.refresh() if self.marker is None: self.marker = QgsVertexMarker(self.canvas) self.marker.setCenter(center) self.marker.setIconSize(8) self.marker.setPenWidth(4) '''Allow user to remove marker''' self.removeMarkerButton.setDisabled(False) self.coordBox.setStyleSheet("QLineEdit{background: white}") except Exception as e: self.coordBox.setStyleSheet("QLineEdit{background: yellow}") def removeMarker(self): self.canvas.scene().removeItem(self.marker) self.marker = None def closeEvent(self, evt): '''Remove marker when closing widget''' if self.marker is not None: self.removeMarker()
class QgepMapToolAddReach(QgepMapToolAddFeature): """ Create a new reach with the mouse. Will snap to wastewater nodes for the first and last point and auto-connect these. """ first_snapping_match = None last_snapping_match = None last_feature_attributes = None def __init__(self, iface: QgisInterface, layer): QgepMapToolAddFeature.__init__(self, iface, layer) self.snapping_marker = None self.node_layer = QgepLayerManager.layer('vw_wastewater_node') assert self.node_layer is not None self.reach_layer = QgepLayerManager.layer('vw_qgep_reach') assert self.reach_layer is not None self.setAdvancedDigitizingAllowed(True) self.setAutoSnapEnabled(True) layer_snapping_configs = [{ 'layer': self.node_layer, 'mode': QgsSnappingConfig.Vertex }, { 'layer': self.reach_layer, 'mode': QgsSnappingConfig.VertexAndSegment }] self.snapping_configs = [] self.snapping_utils = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) for lsc in layer_snapping_configs: config = QgsSnappingConfig() config.setMode(QgsSnappingConfig.AdvancedConfiguration) config.setEnabled(True) settings = QgsSnappingConfig.IndividualLayerSettings( True, lsc['mode'], 10, QgsTolerance.Pixels) config.setIndividualLayerSettings(lsc['layer'], settings) self.snapping_configs.append(config) def left_clicked(self, event): """ The mouse is clicked: snap to neary points which are on the wastewater node layer and update the rubberband :param event: The coordinates etc. """ point3d, match = self.snap(event) if self.rubberband.numberOfVertices() == 0: self.first_snapping_match = match self.last_snapping_match = match self.rubberband.addPoint3D(point3d) self.temp_rubberband.reset() self.temp_rubberband.addPoint(QgsPointXY(point3d.x(), point3d.y())) if self.snapping_marker is not None: self.iface.mapCanvas().scene().removeItem(self.snapping_marker) self.snapping_marker = None def mouse_move(self, event): _, match = self.snap(event) # snap indicator if not match.isValid(): if self.snapping_marker is not None: self.iface.mapCanvas().scene().removeItem(self.snapping_marker) self.snapping_marker = None return # TODO QGIS 3: see if vertices can be removed # we have a valid match if self.snapping_marker is None: self.snapping_marker = QgsVertexMarker(self.iface.mapCanvas()) self.snapping_marker.setPenWidth(3) self.snapping_marker.setColor(QColor(Qt.magenta)) if match.hasVertex(): if match.layer(): icon_type = QgsVertexMarker.ICON_BOX # vertex snap else: icon_type = QgsVertexMarker.ICON_X # intersection snap else: icon_type = QgsVertexMarker.ICON_DOUBLE_TRIANGLE # must be segment snap self.snapping_marker.setIconType(icon_type) self.snapping_marker.setCenter(match.point()) def snap(self, event): """ Snap to nearby points on the wastewater node layer which may be used as connection points for this reach. :param event: The mouse event :return: The snapped position in map coordinates """ for config in self.snapping_configs: self.snapping_utils.setConfig(config) match = self.snapping_utils.snapToMap( QgsPointXY(event.originalMapPoint())) if match.isValid(): return QgsPoint(match.point()), match # if no match, snap to all layers (according to map settings) and try to grab Z match = self.iface.mapCanvas().snappingUtils().snapToMap( QgsPointXY(event.originalMapPoint())) if match.isValid() and match.hasVertex(): if match.layer(): req = QgsFeatureRequest(match.featureId()) f = next(match.layer().getFeatures(req)) assert f.isValid() if match.layer().geometryType() == QgsWkbTypes.PointGeometry: point = QgsPoint(f.geometry().constGet()) else: (ok, vertex_id) = f.geometry().vertexIdFromVertexNr( match.vertexIndex()) assert ok point = f.geometry().constGet().vertexAt(vertex_id) assert type(point) == QgsPoint return point, match else: return QgsPoint(match.point()), match return QgsPoint(event.originalMapPoint()), match def right_clicked(self, _): """ The party is over, the reach digitized. Create a feature from the rubberband and show the feature form. """ self.temp_rubberband.reset() if self.snapping_marker is not None: self.iface.mapCanvas().scene().removeItem(self.snapping_marker) self.snapping_marker = None if len(self.rubberband.points) >= 2: fields = self.layer.fields() f = QgsFeature(fields) if not self.last_feature_attributes: self.last_feature_attributes = [None] * fields.count() for idx, field in enumerate(fields): if field.name() in [ 'clear_height', 'material', 'ch_usage_current', 'ch_function_hierarchic', 'ch_function_hydraulic', 'horizontal_positioning', 'ws_status', 'ws_year_of_construction', 'ws_fk_owner', 'ws_fk_operator', 'inside_coating', 'fk_pipe_profile', 'remark' ]: f.setAttribute(idx, self.last_feature_attributes[idx]) else: # try client side default value first v = self.layer.defaultValue(idx, f) if v != NULL: f.setAttribute(idx, v) else: f.setAttribute( idx, self.layer.dataProvider().defaultValue(idx)) f.setGeometry(self.rubberband.asGeometry3D()) snapping_results = { 'from': self.first_snapping_match, 'to': self.last_snapping_match } for dest, match in list(snapping_results.items()): level_field_index = self.layer.fields().indexFromName( 'rp_{dest}_level'.format(dest=dest)) pt_idx = 0 if dest == 'from' else -1 if match.isValid() and match.layer() in (self.node_layer, self.reach_layer): request = QgsFeatureRequest(match.featureId()) network_element = next(match.layer().getFeatures(request)) assert network_element.isValid() # set the related network element field = self.layer.fields().indexFromName( 'rp_{dest}_fk_wastewater_networkelement'.format( dest=dest)) f.setAttribute(field, network_element.attribute('obj_id')) # assign level if the match is a node or if we have 3D from snapping if match.layer() == self.node_layer: level = network_element['bottom_level'] f.setAttribute(level_field_index, level) elif self.rubberband.points[pt_idx].z() != 0: level = self.rubberband.points[pt_idx].z() level = level if not math.isnan(level) else NULL f.setAttribute(level_field_index, level) dlg = self.iface.getFeatureForm(self.layer, f) dlg.setMode(QgsAttributeEditorContext.AddFeatureMode) dlg.exec_() self.last_feature_attributes = dlg.feature().attributes() self.rubberband.reset3D()
class geopunt4QgisElevationDialog(QDialog): def __init__(self, iface): QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~Qt.WindowContextHelpButtonHint ) self.iface = iface # initialize locale locale = QSettings().value("locale/userLocale", "en") if not locale: locale == 'en' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_elevationDlg() self.ui.setupUi(self) #get settings self.s = QSettings() self.loadSettings() self.gh = geometryHelper( self.iface ) self.eh = elevationHelper( self.iface, self.startDir) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QPushButton("Sluiten"), QDialogButtonBox.RejectRole ) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) ##graph global vars self.Rubberline = None self.profile = None self.pt = None self.ax = None self.ano = None self.anoLbl = None self.counter = 0 self.xscaleUnit = (1, "m") # a figure instance to plot on self.figure = Figure() #create the Canvas widget and toolbar and set graphWgt as parent self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) ### #self.ui.toolbar.layout().insertWidget(0, self.toolbar) self.ui.graphWgt.layout().addWidget(self.canvas) self.createCanvasToolbar() #events self.ui.drawBtn.clicked.connect(self.drawBtnClicked) self.figure.canvas.mpl_connect('motion_notify_event', self.showGraphMotion) self.ui.saveLineBtn.clicked.connect(self.saveLineClicked) self.ui.savePntBtn.clicked.connect(self.savePntClicked) self.ui.addDHMbtn.clicked.connect(self.addDHMasWMS) self.ui.refreshBtn.clicked.connect( self.onRefresh ) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.rejected.connect(self.clean ) def createCanvasToolbar (self): ''' 1 Reset original view 2 Back to previous view 3 Forward to next view 4 Pan axes with left mouse, zoom with right 5 Zoom to rectangle 6 Save the figure 7 Edit curves line and axes parameters ''' self.toolbar.setVisible(False) toolbarBtns = self.ui.toolbar.findChildren(QToolButton) self.ui.toolbar.setStyleSheet("""QToolButton {border-width: 2px; border-style: outset; border-color: #fbd837; border-radius: 5px ; background-color: white } QToolButton:pressed { border-style: inset; background-color: grey } """) toolbarBtns[0].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Keer terug naar overzicht")) toolbarBtns[0].setIcon( QIcon(":/plugins/geopunt4Qgis/images/full_extent.png")) toolbarBtns[0].clicked.connect( self.toolbar.home ) toolbarBtns[1].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Vorige")) toolbarBtns[1].setIcon( QIcon(":/plugins/geopunt4Qgis/images/previous.png")) toolbarBtns[1].clicked.connect( self.toolbar.back ) toolbarBtns[2].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Volgende")) toolbarBtns[2].setIcon( QIcon(":/plugins/geopunt4Qgis/images/next.png")) toolbarBtns[2].clicked.connect( self.toolbar.forward ) toolbarBtns[3].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Pannen")) toolbarBtns[3].setIcon( QIcon(":/plugins/geopunt4Qgis/images/pan.png")) toolbarBtns[3].clicked.connect( self.toolbar.pan ) toolbarBtns[4].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Zoom naar rechthoek")) toolbarBtns[4].setIcon( QIcon(":/plugins/geopunt4Qgis/images/rectangleZoom.png")) toolbarBtns[4].clicked.connect( self.toolbar.zoom ) toolbarBtns[5].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Opslaan als afbeelding")) toolbarBtns[5].setIcon( QIcon(":/plugins/geopunt4Qgis/images/save.png")) toolbarBtns[5].clicked.connect( self.save_fig ) #semf.toolbar.save_figure toolbarBtns[6].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Vorm grafiek aanpassen")) toolbarBtns[6].setIcon( QIcon(":/plugins/geopunt4Qgis/images/wrench.png")) toolbarBtns[6].clicked.connect( self.toolbar.edit_parameters) toolbarBtns[7].setIcon( QIcon(":/plugins/geopunt4Qgis/images/fill.png")) toolbarBtns[7].setToolTip( QCoreApplication.translate("geopunt4QgisElevationDialog", "Kies de vulkleur")) toolbarBtns[7].clicked.connect( self.setFill) def loadSettings(self): self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.samplesSavetoFile = int( self.s.value("geopunt4qgis/samplesSavetoFile" , 1)) sampleLayer = self.s.value("geopunt4qgis/sampleLayerTxt", "") if sampleLayer: self.sampleLayerTxt = sampleLayer self.profileLineSavetoFile = int( self.s.value("geopunt4qgis/profileLineSavetoFile" , 1)) profileLineLayer= self.s.value("geopunt4qgis/profileLineLayerTxt", "") if profileLineLayer: self.profileLineLayerTxt = profileLineLayer self.startDir = self.s.value("geopunt4qgis/startDir", os.path.expanduser("~")) self.elevation = elevation(self.timeout, self.proxy ) def resizeEvent(self, event): QDialog.resizeEvent(self, event) if self.ax: self.figure.tight_layout() #eventhandlers def save_fig(self): formats = ( "Joint Photographic Experts Group (*.jpg) (*.jpg);;Scalable Vector Grapics (*.svg) (*.svg);;"+ "Portable Document Format (*.pdf) (*.pdf);;Tagged Image File Format (*.tif) (*.tif)"+ ";;Encapsulated Postscript (*.eps) (*.eps)") if not(sys.platform == 'win32'): formats += ";;Portable Network Graphics (*.png) (*.png)" fileName, __ = QFileDialog.getSaveFileName( self , "Save File", self.startDir, formats); self.figure.savefig(fileName) def onRefresh(self): if self.ano: self.ano.remove() self.ano = None if self.anoLbl: self.anoLbl.remove() self.anoLbl = None self.plot() def onResize(self, event): self.figure.tight_layout() def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/hoogteprofiel") def drawBtnClicked(self): self.clean() #self.reSetFigure() self.tool = lineTool(self.iface, self.callBack ) self.iface.mapCanvas().setMapTool(self.tool) self.showMinimized() self.counter += 1 def showGraphMotion(self, event): if self.ax == None: return if event.xdata != None and event.ydata != None: if self.ano != None: self.ano.remove() self.ano = None if self.anoLbl != None: self.anoLbl.remove() self.anoLbl = None xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] )# if n[3] > -9999 ] zx = np.interp( event.xdata, xdata, ydata ) xmax = np.max( xdata ) xmin = np.min( xdata ) zmax = np.max( ydata ) zmin = np.max( [n[3] for n in self.profile if n[3] > -9999 ] ) if event.xdata <= xmax and event.xdata >= xmin : self.ano = self.ax.arrow( event.xdata , -9999, 0, zx + 9999, fc="k", ec="k" ) box_props = dict(boxstyle="Round,pad=0.3", fc="cyan", ec="b", lw=2) self.anoLbl = self.ax.annotate( str( round(zx, 2)) + " m", xy= (event.xdata, zx ) , xytext= (event.xdata , zx + (0.2 * ( zmax - zmin )) ), bbox=box_props ) self.setMapPt( event.xdata / self.xscaleUnit[0] ) else: self.setMapPt() event.canvas.draw() def saveLineClicked(self): if not hasattr(self, 'profileLineLayerTxt'): layerName, accept = QInputDialog.getText(None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return else: self.profileLineLayerTxt = layerName if self.profile != None and self.Rubberline != None: title = self.ax.get_title() self.eh.save_profile( self.Rubberline.asGeometry(), self.profile, title, self.profileLineLayerTxt, self.profileLineSavetoFile, sender=self ) def savePntClicked(self): if not hasattr(self, 'sampleLayerTxt'): layerName, accept = QInputDialog.getText(None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return else: self.sampleLayerTxt = layerName if self.profile != None: title = self.ax.get_title() self.eh.save_sample_points( self.profile, title, self.sampleLayerTxt, self.samplesSavetoFile, sender=self ) def setFill( self ): if self.profile == None: return if self.ax == None: return clr = QColorDialog.getColor( Qt.white, self, QCoreApplication.translate( "geopunt4QgisElevationDialog", "Kies de vulkleur") ) if clr.isValid(): xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] ) self.ax.fill_between( xdata, ydata, -9999, color=clr.name() ) def addDHMasWMS(self): crs = self.gh.getGetMapCrs(self.iface).authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' dhmUrl = "url=https://geoservices.informatievlaanderen.be/raadpleegdiensten/DHMV/wms&layers=DHMVII_DTM_1m&&format=image/png&styles=default&crs="+ crs try: rlayer = QgsRasterLayer(dhmUrl, 'Hoogtemodel', 'wms') if rlayer.isValid(): rlayer.renderer().setOpacity(0.8) QgsProject.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisElevationDialog", "Kan WMS niet laden"), level=Qgis.Critical, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=Qgis.Critical, duration=10) return def plot(self): if self.Rubberline == None: return wgsLine = self.gh.prjLineFromMapCrs( self.Rubberline.asGeometry() ) lineString = [ list(n) for n in wgsLine.asPolyline()] nrSamples = self.ui.nrOfSampleSpin.value() #try: self.profile = self.elevation.fetchElevaton( lineString, 4326, nrSamples) #except geopuntError as ge: # self.bar.pushMessage("Error", ge.message, level=Qgis.Critical, duration=10) # return if np.max( [n[0] for n in self.profile ] ) > 1000: self.xscaleUnit = (0.001 , "km" ) else: self.xscaleUnit = (1 , "m" ) xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] ) #need at least 3 values if len(xdata) <= 2 or len([n for n in self.profile if n[3] > -9999 ]) <= 2: self.bar.pushMessage("Error", QCoreApplication.translate( "geopunt4QgisElevationDialog", "Er werd geen of onvoldoende data gevonden"), level=Qgis.Warning, duration=5) self.profile = None return ymin = np.min( [n[3] for n in self.profile if n[3] > -9999 ] ) ymax = np.max( ydata ) # create an axis self.ax = self.figure.add_subplot(111) # discards the old graph self.ax.hold(False) # plot data self.ax.plot( xdata, ydata,'r*') self.ax.fill_between(xdata, ydata, -9999, color='#F8E6E0' ) self.ax.set_ylim([ymin , ymax]) self.ax.set_xlim([0 , None ]) self.ax.set_ylabel("hoogte (m)") self.ax.set_xlabel("afstand (%s)" % self.xscaleUnit[1] ) self.ax.set_title("Hoogteprofiel " + str( self.counter) ) # refresh canvas self.figure.tight_layout() self.canvas.draw() def callBack(self, geom): self.iface.mapCanvas().unsetMapTool(self.tool) self.Rubberline = geom self.showNormal() self.activateWindow() self.plot() self.ui.saveWgt.setEnabled(True) def setMapPt(self, dist=None ): if self.pt: self.iface.mapCanvas().scene().removeItem(self.pt) if dist==None: return if self.Rubberline == None: return # dist is measured in lambert 72 in meters lb72Line = self.gh.prjLineFromMapCrs( self.Rubberline.asGeometry() , 31370 ) lb72pt = lb72Line.interpolate(dist).asPoint() pt = self.gh.prjPtToMapCrs(lb72pt, 31370) self.pt = QgsVertexMarker(self.iface.mapCanvas()) self.pt.setCenter( pt ) self.pt.setColor(QColor(0,255,250)) self.pt.setIconSize(5) self.pt.setIconType(QgsVertexMarker.ICON_BOX ) # or ICON_CROSS, ICON_X self.pt.setPenWidth(7) if self.xscaleUnit[0] != 1: msg= "lengte= %s %s" % (round( dist * self.xscaleUnit[0], 2) , self.xscaleUnit[1]) else: msg= "lengte= %s %s" % (int( dist * self.xscaleUnit[0]) , self.xscaleUnit[1]) self.ui.mgsLbl.setText( msg ) def clean(self): if self.pt: self.iface.mapCanvas().scene().removeItem(self.pt) if self.Rubberline: self.iface.mapCanvas().scene().removeItem(self.Rubberline) if self.ano: self.ano.remove() self.ano = None if self.anoLbl: self.anoLbl.remove() self.anoLbl = None if self.ax: self.ax.hold(False) self.ax.clear() self.ax = None self.figure.clf() self.canvas.draw() self.ui.saveWgt.setEnabled(False) self.profile = None self.Rubberline = None self.ui.mgsLbl.setText("")
class QGISRedCreatePipeTool(QgsMapTool): def __init__(self, button, iface, projectDirectory, netwName, method): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.method = method self.setAction(button) self.startMarker = QgsVertexMarker(self.iface.mapCanvas()) self.startMarker.setColor(QColor(255, 87, 51)) self.startMarker.setIconSize(15) self.startMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.startMarker.setPenWidth(3) self.startMarker.hide() self.endMarker = QgsVertexMarker(self.iface.mapCanvas()) self.endMarker.setColor(QColor(255, 87, 51)) self.endMarker.setIconSize(15) self.endMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.endMarker.setPenWidth(3) self.endMarker.hide() self.snapper = None self.rubberBand1 = None self.rubberBand2 = None self.resetProperties() def activate(self): QgsMapTool.activate(self) # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(1) # Vertex config.setMode(2) # All layers config.setTolerance(10) config.setUnits(1) # Pixels config.setEnabled(True) self.snapper.setConfig(config) def deactivate(self): self.resetProperties() QgsMapTool.deactivate(self) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def resetProperties(self): # self.toolbarButton.setChecked(False) if self.rubberBand1 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand1) if self.rubberBand2 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand2) self.startMarker.hide() self.endMarker.hide() self.mousePoints = [] self.firstClicked = False self.objectSnapped = None self.rubberBand1 = None self.rubberBand2 = None def createRubberBand(self, points): myPoints1 = [] for p in points: myPoints1.append(QgsPoint(p.x(), p.y())) myPoints1.remove(myPoints1[-1]) if self.rubberBand1 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand1) self.rubberBand1 = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand1.setToGeometry(QgsGeometry.fromPolyline(myPoints1), None) self.rubberBand1.setColor(QColor(240, 40, 40)) self.rubberBand1.setWidth(1) self.rubberBand1.setLineStyle(Qt.SolidLine) myPoints2 = [] myPoints2.append(QgsPoint(points[-2].x(), points[-2].y())) myPoints2.append(QgsPoint(points[-1].x(), points[-1].y())) if self.rubberBand2 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand2) self.rubberBand2 = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand2.setToGeometry(QgsGeometry.fromPolyline(myPoints2), None) self.rubberBand2.setColor(QColor(240, 40, 40)) self.rubberBand2.setWidth(1) self.rubberBand2.setLineStyle(Qt.DashLine) """Events""" def canvasPressEvent(self, event): if event.button() == Qt.LeftButton: if not self.firstClicked: self.firstClicked = True point = self.toMapCoordinates(event.pos()) if self.objectSnapped is not None: point = self.objectSnapped.point() self.mousePoints.append(point) self.mousePoints.append(point) else: self.mousePoints.append(self.mousePoints[-1]) self.createRubberBand(self.mousePoints) if event.button() == Qt.RightButton: self.mousePoints.remove(self.mousePoints[-1]) if self.firstClicked: if (len(self.mousePoints) == 2 and self.mousePoints[0] == self.mousePoints[1]): createdPipe = False elif len(self.mousePoints) < 2: createdPipe = False else: createdPipe = True if createdPipe: self.method(self.mousePoints) self.resetProperties() def canvasMoveEvent(self, event): # Mouse not clicked if not self.firstClicked: match = self.snapper.snapToMap(self.toMapCoordinates(event.pos())) if match.isValid(): self.objectSnapped = match self.startMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.startMarker.show() else: self.objectSnapped = None self.startMarker.hide() # Mouse clicked else: point = self.toMapCoordinates(event.pos()) match = self.snapper.snapToMap(point) if match.isValid(): self.objectSnapped = match self.endMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.endMarker.show() self.mousePoints[-1] = match.point() else: self.objectSnapped = None self.endMarker.hide() self.mousePoints[-1] = point self.createRubberBand(self.mousePoints)
class ReverseGeocodeTool(QgsMapTool): def __init__(self, iface, settings): self.canvas = iface.mapCanvas() QgsMapTool.__init__(self, self.canvas) self.iface = iface self.settings = settings self.reverseGeoCodeDialog = ReverseGeocodeDialog( self, self.iface, self.iface.mainWindow()) self.iface.addDockWidget(Qt.TopDockWidgetArea, self.reverseGeoCodeDialog) self.reverseGeoCodeDialog.hide() self.epsg4326 = QgsCoordinateReferenceSystem('EPSG:4326') self.marker = None # Set up a polygon/line rubber band self.rubber = QgsRubberBand(self.canvas) self.rubber.setColor(QColor(255, 70, 0, 200)) self.rubber.setWidth(5) self.rubber.setBrushStyle(Qt.NoBrush) def activate(self): '''When activated set the cursor to a crosshair.''' self.canvas.setCursor(Qt.CrossCursor) self.show() def unload(self): self.iface.removeDockWidget(self.reverseGeoCodeDialog) self.reverseGeoCodeDialog = None if self.rubber: self.canvas.scene().removeItem(self.rubber) del self.rubber self.removeMarker() def addMarker(self, lat, lon): if self.marker: self.removeMarker() canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(self.epsg4326, canvasCrs, QgsProject.instance()) center = transform.transform(lon, lat) self.marker = QgsVertexMarker(self.canvas) self.marker.setCenter(center) self.marker.setColor(QColor(255, 70, 0)) self.marker.setIconSize(15) self.marker.setIconType(QgsVertexMarker.ICON_X) self.marker.setPenWidth(3) self.marker.show() def removeMarker(self): if self.marker: self.canvas.scene().removeItem(self.marker) self.marker = None def clearSelection(self): self.removeMarker() self.rubber.reset() def transform_geom(self, geometry): canvasCrs = self.canvas.mapSettings().destinationCrs() geom = QgsGeometry(geometry) geom.transform( QgsCoordinateTransform(self.epsg4326, canvasCrs, QgsProject.instance())) return geom def show(self): self.reverseGeoCodeDialog.show() def request(self, url): fetcher = QgsNetworkContentFetcher() fetcher.fetchContent(QUrl(url)) evloop = QEventLoop() fetcher.finished.connect(evloop.quit) evloop.exec_(QEventLoop.ExcludeUserInputEvents) fetcher.finished.disconnect(evloop.quit) return fetcher.contentAsString() def canvasReleaseEvent(self, event): # Make sure the point is transfored to 4326 self.clearSelection() pt = self.toMapCoordinates(event.pos()) canvasCRS = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(canvasCRS, self.epsg4326, QgsProject.instance()) pt = transform.transform(pt.x(), pt.y()) url = '{}?format=json&lat={:f}&lon={:f}&zoom={:d}&addressdetails=0&polygon_text=1'.format( self.settings.reverseURL(), pt.y(), pt.x(), self.settings.levelOfDetail) # print( url ) jsondata = self.request(url) try: jd = json.loads(jsondata) try: display_name = jd['display_name'] self.setText(display_name) except KeyError: self.setText("[Could not find address]") try: wkt = jd['geotext'] geometry = QgsGeometry.fromWkt(wkt) if geometry.wkbType() == QgsWkbTypes.Point: pt = geometry.asPoint() lon = pt.x() lat = pt.y() self.addMarker(lat, lon) else: geometry = self.transform_geom(geometry) self.rubber.addGeometry(geometry, None) self.rubber.show() except KeyError: try: lon = float(jd['lon']) lat = float(jd['lat']) self.addMarker(lat, lon) except: pass except Exception: self.setText("Error: " + jsondata) if not self.reverseGeoCodeDialog.isVisible(): self.show() def setText(self, text): self.reverseGeoCodeDialog.addressLineEdit.setText(text)
class mapillary_cursor(): def transformToWGS84(self, pPoint): # transformation from the current SRS to WGS84 crcMappaCorrente = self.iface.mapCanvas().mapSettings().destinationCrs() # get current crs crsSrc = crcMappaCorrente crsDest = QgsCoordinateReferenceSystem(4326) # WGS 84 xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) return xform.transform(pPoint) # forward transformation: src -> dest def transformToCurrentSRS(self, pPoint): # transformation from the current SRS to WGS84 crcMappaCorrente = self.iface.mapCanvas().mapSettings().destinationCrs() # get current crs crsDest = crcMappaCorrente crsSrc = QgsCoordinateReferenceSystem(4326) # WGS 84 xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) return xform.transform(pPoint) # forward transformation: src -> dest def __init__(self,parentInstance): self.parentInstance = parentInstance self.iface = parentInstance.iface self.mapCanvas = self.iface.mapCanvas() self.lineOfSight = QgsRubberBand(self.mapCanvas, QgsWkbTypes.LineGeometry) self.sightDirection = QgsRubberBand(self.mapCanvas, QgsWkbTypes.LineGeometry) self.pointOfView = QgsVertexMarker(self.mapCanvas) self.cursor = QgsVertexMarker(self.mapCanvas) self.sightDirection.setColor(QColor(CURSOR_COLOR)) self.lineOfSight.setColor(QColor(CURSOR_COLOR)) self.pointOfView.setColor(QColor(CURSOR_COLOR)) self.cursor.setColor(QColor(CURSOR_COLOR)) self.lineOfSight.setWidth(2) self.sightDirection.setWidth(1) self.sightDirection.setLineStyle(Qt.DashLine) self.pointOfView.setIconType(QgsRubberBand.ICON_CIRCLE) self.cursor.setIconType(QgsRubberBand.ICON_CIRCLE) self.pointOfView.setIconSize(20) self.cursor.setIconSize(20) self.cursor.setPenWidth(2) self.pointOfView.setPenWidth(2) self.samples_datasource = '' #self.update_ds(self.parentInstance.sample_settings.settings['sample_source']) def getSamplesLayer(self, samples_datasource): if samples_datasource != 'memory': if not os.path.exists(samples_datasource): self.create_datasource_from_template(samples_datasource) samples_lyr = QgsVectorLayer(samples_datasource, SAMPLES_LAYER_NAME, 'ogr') else: samples_lyr = QgsVectorLayer("Point?crs=epsg:4326&index=yes", SAMPLES_LAYER_NAME, 'memory') self.checkForTemplateFields(samples_lyr) return samples_lyr def getFieldFromDefinition(self, field_type): type_pack = field_type[1].split("|") if field_type[2]: comment = field_type[2] else: comment = field_type[0] return QgsField(name=field_type[0], type=int(type_pack[0]), len=int(type_pack[1]), prec=int(type_pack[2]),comment=comment) def checkForTemplateFields(self, layer): layerFieldNamesList = [] for field in layer.fields().toList(): layerFieldNamesList.append(field.name()) for fieldDef in FIELDS_TEMPLATE: if not fieldDef[0] in layerFieldNamesList: layer.startEditing() layer.addAttribute(self.getFieldFromDefinition(fieldDef)) layer.commitChanges() def update_ds(self,ds): if self.samples_datasource != ds: self.samples_datasource = ds self.samplesLayer = self.getSamplesLayer(ds) self.samplesLayer.triggerRepaint() self.samplesLayer.loadNamedStyle(os.path.join(os.path.dirname(__file__), "res", "mapillary_samples.qml")) self.samplesLayer.featureAdded.connect(self.newAddedFeat) def create_datasource_from_template(self, datasource): fieldSet = QgsFields() for fieldDef in FIELDS_TEMPLATE: fieldSet.append(self.getFieldFromDefinition(fieldDef)) writer = QgsVectorFileWriter(datasource, 'UTF-8', fieldSet, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(4326),"ESRI Shapefile") if writer.hasError(): print ("error",writer.errorMessage()) del writer def draw(self,pointOfView_coords,orig_pointOfView_coords,cursor_coords,endOfSight_coords): self.cursor.show() self.pointOfView.show() self.lineOfSight.reset() self.sightDirection.reset() pointOfView = self.transformToCurrentSRS(QgsPointXY(pointOfView_coords[1],pointOfView_coords[0])) cursor = self.transformToCurrentSRS(QgsPointXY(cursor_coords[1],cursor_coords[0])) endOfSight = self.transformToCurrentSRS(QgsPointXY(endOfSight_coords[1],endOfSight_coords[0])) self.pointOfView.setCenter (pointOfView) self.cursor.setCenter (cursor) self.lineOfSight.addPoint(pointOfView) self.lineOfSight.addPoint(cursor) self.sightDirection.addPoint(pointOfView) self.sightDirection.addPoint(endOfSight) self.cursor.updatePosition() def delete(self): self.cursor.hide() self.pointOfView.hide() self.lineOfSight.reset() self.sightDirection.reset() def addSampleLayerToCanvas(self): if not QgsProject.instance().mapLayer(self.samplesLayer.id()): QgsProject.instance().addMapLayer(self.samplesLayer) self.restoreMarkers() def sample(self, type, id,key,sample_coords, img_coords=None): self.samplesLayer.startEditing() samplePoint = QgsPointXY(sample_coords[1],sample_coords[0]) #sampleDevicePoint = self.iface.mapCanvas().getCoordinateTransform().transform(samplePoint.x(),samplePoint.y()) self.addSampleLayerToCanvas() sampleFeat = QgsFeature(self.samplesLayer.fields()) sampleFeat['type'] = type sampleFeat['id'] = id sampleFeat['key'] = key if img_coords: sampleFeat['img_coords'] = img_coords sampleFeat.setGeometry(QgsGeometry.fromPointXY(samplePoint)) #self.samplesLayer.dataProvider().addFeatures([sampleFeat]) print ('',self.samplesLayer.addFeature(sampleFeat)) self.samplesLayer.commitChanges() def newAddedFeat(self,featId): if featId < 0: return self.samplesLayer.triggerRepaint() if self.parentInstance.sample_settings.settings['auto_open_form']: newFeat = self.samplesLayer.getFeature(featId) self.parentInstance.samples_form.open(newFeat) def getSamplesList(self): samples = [] id = 1 for feat in self.samplesLayer.getFeatures(): samples.append({ "id":id, "latLon":{ 'lat':feat.geometry().asPoint().y(), 'lon':feat.geometry().asPoint().x(), } }) def restoreTags(self,key): exp = QgsExpression('"type" = \'tag\' and "key" = \'%s\'' % key) tags = [] for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): if feat['cat']: color = self.parentInstance.sample_settings.settings['categories'][str(feat['cat'])] else: color = '#ffffff' tags.append({ 'id': str(feat['id']), 'key': str(feat['key']), 'note': str(feat['note']), 'cat': str(feat['cat']), 'color': color, 'geometry': json.loads(feat['img_coords']) }) return tags def editSample(self,type,key,id): exp = QgsExpression('"type" = \'%s\' and "key" = \'%s\' and "id" = \'%s\'' % (type,key,id)) for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): self.parentInstance.samples_form.open(feat) def moveMarker(self,key,id,sample_coords): exp = QgsExpression('"type" = \'marker\' and "key" = \'%s\' and "id" = \'%s\'' % (key,id)) for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): samplePoint = QgsPointXY(sample_coords[1], sample_coords[0]) self.samplesLayer.startEditing() self.samplesLayer.changeGeometry(feat.id(),QgsGeometry.fromPointXY(samplePoint)) self.samplesLayer.commitChanges() self.samplesLayer.triggerRepaint() def restoreMarkers(self): if self.parentInstance.sample_settings.settings['sample_source'] != 'memory': exp = QgsExpression('"type" = \'marker\'') markersDef = [] for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): if feat['cat']: color = self.parentInstance.sample_settings.settings['categories'][str(feat['cat'])] else: color = '#ffffff' markersDef.append({ 'key': str(feat['key']), 'id': str(feat['id']), 'color': color, 'loc': { 'lon': feat.geometry().asPoint().x(), 'lat': feat.geometry().asPoint().y() } }) self.parentInstance.viewer.addMarkers(markersDef)
class ShowOnMapTool(QgsMapToolEmitPoint): '''Class to interact with the map canvas to capture the coordinate when the mouse button is pressed and to display the coordinate in in the status bar.''' def __init__(self, iface): QgsMapToolEmitPoint.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.canvasClicked.connect(self.clicked) self.marker = None def activate(self): '''When activated set the cursor to a crosshair.''' self.canvas.setCursor(Qt.CrossCursor) def deactivate(self): self.removeMarker() def clicked(self, pt, b): '''Capture the coordinate when the mouse button has been released, format it, and copy it to the clipboard.''' if settings.externalMapShowLocation: if self.marker is None: self.marker = QgsVertexMarker(self.canvas) self.marker.setIconSize(18) self.marker.setPenWidth(2) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.setCenter(pt) else: self.removeMarker(); canvasCRS = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) lat = pt4326.y() lon = pt4326.x() if settings.googleEarthMapProvider(): f = tempfile.NamedTemporaryFile(mode='w', suffix=".kml", delete=False) f.write('<?xml version="1.0" encoding="UTF-8"?>') f.write('<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">') f.write('<Document>') f.write(' <name>QGIS Location</name>') f.write(' <description>{:.8f}, {:.8f}</description>'.format(lon, lat)) f.write(' <Placemark>') f.write(' <name>QGIS Location</name>') f.write(' <Point>') f.write(' <coordinates>{:.8f},{:.8f},0</coordinates>'.format(lon, lat)) f.write(' </Point>') f.write(' </Placemark>') f.write('</Document>') f.write('</kml>') f.close() if platform.system() == 'Windows': os.startfile(f.name) else: webbrowser.open(f.name) self.iface.messageBar().pushMessage("", "Viewing Coordinate %f,%f in Google Earth" % (lat, lon), level=Qgis.Info, duration=3) else: mapprovider = settings.getMapProviderString(lat, lon) url = QUrl(mapprovider).toString() webbrowser.open(url, new=2) self.iface.messageBar().pushMessage("", "Viewing Coordinate %f,%f in external map" % (lat, lon), level=Qgis.Info, duration=3) def removeMarker(self): if self.marker is not None: self.canvas.scene().removeItem(self.marker) self.marker = None
class DeleteNodeMapTool(ParentMapTool): ''' Button 17. User select one node. Execute SQL function: 'gw_fct_delete_node' ''' def __init__(self, iface, settings, action, index_action): ''' Class constructor ''' # Call ParentMapTool constructor super(DeleteNodeMapTool, self).__init__(iface, settings, action, index_action) # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(255, 25, 25)) self.vertexMarker.setIconSize(12) self.vertexMarker.setIconType(QgsVertexMarker.ICON_CIRCLE) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) ''' QgsMapTools inherited event functions ''' def canvasMoveEvent(self, event): # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer.name() == self.layer_node.name(): # Get the point point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() break def canvasReleaseEvent(self, event): # With left click the digitizing is finished if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) snappFeat = None # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer.name() == self.layer_node.name(): # Get the point point = QgsPoint(result[0].snappedVertex) snappFeat = next( result[0].layer.getFeatures(QgsFeatureRequest().setFilterFid(result[0].snappedAtGeometry))) break if snappFeat is not None: # Get selected features and layer type: 'node' feature = snappFeat node_id = feature.attribute('node_id') # Execute SQL function and show result to the user function_name = "gw_fct_delete_node" sql = "SELECT " + self.schema_name + "." + function_name + "('" + str(node_id) + "');" status = self.controller.execute_sql(sql) if status: message = "Node deleted successfully" self.controller.show_warning(message, context_name='ui_message' ) # Refresh map canvas self.iface.mapCanvas().refresh() def activate(self): # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to node self.snapperManager.snapToNode() # Change cursor self.canvas.setCursor(self.cursor) # Show help message when action is activated if self.show_help: message = "Select the node inside a pipe by clicking on it and it will be removed" self.controller.show_warning(message, context_name='ui_message') # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_node) except: self.canvas.setCurrentLayer(self.layer_node) def deactivate(self): # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) # Removehighlight self.h = None
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() self.marker = QgsVertexMarker(self.iface.mapCanvas()) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.hide() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/gazetteersearch" # initialize locale localePath = "" if QGis.QGIS_VERSION_INT < 10900: locale = QSettings().value("locale/userLocale").toString()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/gazetteersearch/icon.png"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search",self.action) self.iface.removeToolBarIcon(self.action) self.iface.mapCanvas().scene().removeItem(self.marker) self.marker = None def _hideMarker(self): self.marker.hide() # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): searchString = searchString.encode('utf-8') gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) def callback(data): try: self.results = list(gazetteer.parseRequestResults(data, self.iface)) except: self.results = [] if len(self.results) == 0: self.widget.addError('No results found for "%s"' % searchString) for res in self.results: self.widget.addResult(res.description) common.search(url, callback) def clearResults(self): self.widget.clearResults() self.marker.hide() def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteersearch.gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, name): for res in self.results: if unicode(res.description) == unicode(name): dest_crs = self.canvas.mapRenderer().destinationCrs() if QGis.QGIS_VERSION_INT < 10900: src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(res.epsg) else: src_crs = QgsCoordinateReferenceSystem(res.epsg, QgsCoordinateReferenceSystem.EpsgCrsId) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(res.x, res.y) x = new_point.x() y = new_point.y() self.canvas.setExtent(QgsRectangle(x,y,x,y)) self.canvas.zoomScale(res.zoom) self.canvas.refresh() self.marker.setCenter(new_point) self.marker.show() return
class rmVertexTool(QgsMapTool): def __init__(self, canvas, layer, iface, action): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.layer = layer self.iface = iface self.action = action self.rb = None self.vx = None self.threshold = float(QSettings().value('ale/threshold')) def canvasPressEvent(self, event): pass def canvasMoveEvent(self, event): if self.rb: self.canvas.scene().removeItem(self.rb) if self.vx: self.canvas.scene().removeItem(self.vx) layerPoint = self.toLayerCoordinates(self.layer, event.pos()) (closestPointID, closestFeature) = finder.closestpoint(self.layer, layerPoint) polyline = closestFeature.geometry().asPolyline() shortestDistance = QgsGeometry.fromPointXY( polyline[closestPointID]).distance( QgsGeometry.fromPointXY(layerPoint)) if closestPointID and shortestDistance < self.threshold: self.rb = QgsRubberBand(self.canvas, False) linePart = polyline[closestPointID - 1:closestPointID + 2] self.rb.setToGeometry(QgsGeometry.fromPolylineXY(linePart), None) self.rb.setColor(QColor(0, 0, 255)) self.rb.setWidth(3) self.vx = QgsVertexMarker(self.canvas) self.vx.setCenter(QgsPointXY(polyline[closestPointID])) self.vx.setColor(QColor(0, 255, 0)) self.vx.setIconSize(5) self.vx.setIconType( QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_BOX self.vx.setPenWidth(3) def canvasReleaseEvent(self, event): layerPoint = self.toLayerCoordinates(self.layer, event.pos()) (closestPointID, closestFeature) = finder.closestpoint(self.layer, layerPoint) polyline = closestFeature.geometry().asPolyline() shortestDistance = QgsGeometry.fromPointXY( polyline[closestPointID]).distance( QgsGeometry.fromPointXY(layerPoint)) if closestPointID and shortestDistance < self.threshold: ftNew = QgsFeature() ptsNew = polyline[closestPointID + 1:] if len(ptsNew) > 1: # can be a linestring plNew = QgsGeometry.fromPolylineXY(ptsNew) ftNew.setGeometry(plNew) pr = self.layer.dataProvider() pr.addFeatures([ftNew]) ptsOld = polyline[:closestPointID] if len(ptsOld) > 1: # can be a linestring plOld = QgsGeometry.fromPolylineXY(ptsOld) self.layer.changeGeometry(closestFeature.id(), plOld) else: self.layer.deleteFeature(closestFeature.id()) if self.rb: self.canvas.scene().removeItem(self.rb) if self.vx: self.canvas.scene().removeItem(self.vx) self.iface.mapCanvas().refresh() def activate(self): self.action.setChecked(True) def deactivate(self): if self.rb: self.canvas.scene().removeItem(self.rb) if self.vx: self.canvas.scene().removeItem(self.vx) self.action.setChecked(False) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() self.marker = QgsVertexMarker(self.iface.mapCanvas()) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.hide() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path( ) + "/python/plugins/gazetteersearch" # initialize locale localePath = "" locale = QSettings().value("locale/userLocale").toString()[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/gazetteersearch/icon.png"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search", self.action) self.iface.removeToolBarIcon(self.action) self.iface.mapCanvas().scene().removeItem(self.marker) self.marker = None def _hideMarker(self): self.marker.hide() # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) data = common.search(url) try: self.results = list(gazetteer.parseRequestResults(data)) except ValueError: self.results = [] if len(self.results) == 0: self.widget.addError('No results found for "%s"' % searchString) for res in self.results: self.widget.addResult(res.description) def clearResults(self): self.widget.clearResults() self.marker.hide() def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, name): for res in self.results: if unicode(res.description) == unicode(name): dest_crs = self.canvas.mapRenderer().destinationCrs() src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(res.epsg) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(res.x, res.y) x = new_point.x() y = new_point.y() self.canvas.setExtent(QgsRectangle(x, y, x, y)) self.canvas.zoomScale(res.zoom) self.canvas.refresh() self.marker.setCenter(new_point) self.marker.show() return
class W3WCoordInputDialog(QDockWidget): def __init__(self, canvas, parent): self.canvas = canvas self.marker = None QDockWidget.__init__(self, parent) self.setAllowedAreas(Qt.TopDockWidgetArea) self.initGui() def setApiKey(self, apikey): self.w3w = what3words(apikey=apikey) def initGui(self): self.setWindowTitle("Zoom to 3 word address") self.label = QLabel('3 Word Address') self.coordBox = QLineEdit() self.coordBox.returnPressed.connect(self.zoomToPressed) self.zoomToButton = QPushButton("Zoom to") self.zoomToButton.clicked.connect(self.zoomToPressed) self.removeMarkerButton = QPushButton("Remove marker") self.removeMarkerButton.clicked.connect(self.removeMarker) self.removeMarkerButton.setDisabled(True) self.hlayout = QHBoxLayout() self.hlayout.setSpacing(6) self.hlayout.setMargin(9) self.hlayout.addWidget(self.label) self.hlayout.addWidget(self.coordBox) self.hlayout.addWidget(self.zoomToButton) self.hlayout.addWidget(self.removeMarkerButton) self.dockWidgetContents = QWidget() self.dockWidgetContents.setLayout(self.hlayout) self.setWidget(self.dockWidgetContents) def zoomToPressed(self): try: w3wCoord = str(self.coordBox.text()).replace(" ", "") QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) json = self.w3w.forwardGeocode(w3wCoord) lat = float(json["geometry"]["lat"]) lon = float(json["geometry"]["lng"]) canvasCrs = self.canvas.mapSettings().destinationCrs() epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326") transform4326 = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) center = transform4326.transform(lon, lat) self.canvas.zoomByFactor(1, center) self.canvas.refresh() if self.marker is None: self.marker = QgsVertexMarker(self.canvas) self.marker.setCenter(center) self.marker.setIconSize(8) self.marker.setPenWidth(4) self.removeMarkerButton.setDisabled(False) self.coordBox.setStyleSheet("QLineEdit{background: white}") except Exception as e: raise self.coordBox.setStyleSheet("QLineEdit{background: yellow}") finally: QApplication.restoreOverrideCursor() def removeMarker(self): self.canvas.scene().removeItem(self.marker) self.marker = None def closeEvent(self, evt): if self.marker is not None: self.canvas.scene().removeItem(self.marker) self.marker = None
class MultipleSnapping(QgsMapTool): canvasClicked = pyqtSignal() def __init__(self, iface, controller, group): """ Class constructor """ self.group_layers = group self.iface = iface self.canvas = self.iface.mapCanvas() # Call superclass constructor and set current action QgsMapTool.__init__(self, self.canvas) self.controller = controller self.rubber_band = QgsRubberBand(self.canvas, QGis.Polygon) mFillColor = QColor(254, 178, 76, 63) self.rubber_band.setColor(mFillColor) self.rubber_band.setWidth(1) self.reset() self.snapper = QgsMapCanvasSnapper(self.canvas) self.selected_features = [] # Vertex marker self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setColor(QColor(255, 0, 255)) self.vertex_marker.setIconSize(11) self.vertex_marker.setIconType( QgsVertexMarker.ICON_CROSS) # or ICON_CROSS, ICON_X, ICON_BOX self.vertex_marker.setPenWidth(3) def reset(self): self.start_point = self.end_point = None self.is_emitting_point = False self.rubber_band.reset(QGis.Polygon) def canvasPressEvent(self, e): if e.button() == Qt.LeftButton: self.start_point = self.toMapCoordinates(e.pos()) self.end_point = self.start_point self.is_emitting_point = True self.show_rect(self.start_point, self.end_point) def canvasReleaseEvent(self, e): self.is_emitting_point = False r = self.rectangle() # Use CTRL button to unselect features key = QApplication.keyboardModifiers() number_features = 0 if e.button() == Qt.LeftButton: for layer in self.group_pointers: # Check number of selections #number_features = layer.selectedFeatureCount() if r is not None: # Selection by rectange lRect = self.canvas.mapSettings().mapToLayerCoordinates( layer, r) layer.select(lRect, True) # True for leave previous selection # if CTRL pressed : unselect features if key == Qt.ControlModifier: layer.selectByRect(lRect, layer.RemoveFromSelection) else: # Selection one by one x = e.pos().x() y = e.pos().y() eventPoint = QPoint(x, y) (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) if result: # Check feature for snap_point in result: # Get the point #point = QgsPoint(snap_point.snappedVertex) snapp_feat = next( snap_point.layer.getFeatures( QgsFeatureRequest().setFilterFid( snap_point.snappedAtGeometry))) # LEAVE SELECTION snap_point.layer.select( [snap_point.snappedAtGeometry]) self.rubber_band.hide() def canvasMoveEvent(self, e): if not self.is_emitting_point: return self.end_point = self.toMapCoordinates(e.pos()) self.show_rect(self.start_point, self.end_point) def show_rect(self, start_point, end_point): self.rubber_band.reset(QGis.Polygon) if start_point.x() == end_point.x() or start_point.y() == end_point.y( ): return point1 = QgsPoint(start_point.x(), start_point.y()) point2 = QgsPoint(start_point.x(), end_point.y()) point3 = QgsPoint(end_point.x(), end_point.y()) point4 = QgsPoint(end_point.x(), start_point.y()) self.rubber_band.addPoint(point1, False) self.rubber_band.addPoint(point2, False) self.rubber_band.addPoint(point3, False) self.rubber_band.addPoint(point4, True) # true to update canvas self.rubber_band.show() def rectangle(self): 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 deactivate(self): self.rubber_band.hide() QgsMapTool.deactivate(self) def activate(self): self.group_layers = ["Wjoin", "Fountain", "Greentap", "Tap"] self.group_pointers = [] for layer_name in self.group_layers: layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name) if layer: layer = layer[0] self.group_pointers.append(layer) # Set active layer self.layer_connec = None self.layer_connec = QgsMapLayerRegistry.instance().mapLayersByName( "Edit connec")[0] self.iface.setActiveLayer(self.layer_connec) self.canvas.connect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint&)"), self.mouse_move) def mouse_move(self, p): map_point = self.canvas.getCoordinateTransform().transform(p) x = map_point.x() y = map_point.y() event_point = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToCurrentLayer(event_point, 2) # @UnusedVariable # That's the snapped point if result: # Check feature for snapPoint in result: #self.controller.log_info(str(snapPoint)) if snapPoint.layer.name() == 'Edit connec': point = QgsPoint(snapPoint.snappedVertex) # Add marker self.vertex_marker.setCenter(point) self.vertex_marker.show() else: self.vertex_marker.hide()
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath()).path() + "/python/plugins/pdokservicesplugin" self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration runIcon = QIcon(os.path.join(self.plugin_dir, 'icon_add_service.svg')) self.run_action = QAction(runIcon, \ "PDOK Services plugin", self.iface.mainWindow()) self.servicesLoaded = False # connect the action to the run method # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if "True" == self.docked or "true" == self.docked or True == self.docked: # self.run_action.triggered.connect(self.showAndRaise) # self.dlg.radioDocked.setChecked(True) # # docked the dialog is immidiately visible, so should run NOW # else: # self.run_action.triggered.connect(self.run) # self.dlg.radioDocked.setChecked(False) # self.setupfq() self.run_action.triggered.connect(self.run) #self.dlg.radioDocked.setChecked(False) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") self.toolbar.addAction(self.run_action) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup eraserIcon = QIcon(os.path.join(self.plugin_dir, 'icon_remove_cross.svg')) self.clean_action = QAction(eraserIcon, \ "Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.clean_action.setEnabled(False) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/icon_help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText("PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect(self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText("een of meer zoekwoorden uit resultaat") #self.dlg.radioDocked.toggled.connect(self.set_docked) self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson) #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen('http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen('http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(self.plugin_dir +'/pdok.json', 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str(pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) # def set_docked(self, foo): # self.setSettingsValue('docked', self.dlg.radioDocked.isChecked()) # #if Qgis.QGIS_VERSION_INT < 10900: # # # qgis <= 1.8 # # QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked())) # #else: # # QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked()) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu("&Pdok Services Plugin", self.run_action) self.iface.removePluginMenu("&Pdok Services Plugin", self.aboutAction) del self.toolbarSearch del self.run_action del self.aboutAction def showService(self, selectedIndexes): if len(selectedIndexes)==0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo(self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data(Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)]=str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale ='' if 'minscale' in self.currentLayer and self.currentLayer['minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:"+self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer['maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:"+self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml('<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype=="WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype=="WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location,query = urllib.parse.splitquery(url) url = location # RD: 20200820: lijkt of het quoten van de query problemen geeft bij WFS, is/was dit nodig??? #if query != None and query != '': # url +=('?'+urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) [""], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs="+crs+"&layers="+layers+"&styles="+style+"&format="+imgformat+"&url="+url; self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs=tilematrixset i = crs.find(':', 5) if i > -1: crs=crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs='EPSG:3857' uri = "tileMatrixSet="+tilematrixset+"&crs="+crs+"&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='"+layers+"' url='"+url+"' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers=='ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper()) ) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData( serviceLayer, Qt.UserRole ) itemType.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem("%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) ) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["servicetitle"] )) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem("%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"]) ) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) self.sourceModel.appendRow( [ itemLayername, itemType, itemServicetitle, itemFilter ] ) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount()>0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data(Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data= data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml( '<h4>{}</h4><lu>{}</lu>'.format(adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) self.clean_action.setEnabled(True) def removePointer(self): if self.pointer is not None and self.pointer.scene() is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) self.pointer = None self.clean_action.setEnabled(False) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
class CopyLatLonTool(QgsMapToolEmitPoint): '''Class to interact with the map canvas to capture the coordinate when the mouse button is pressed and to display the coordinate in in the status bar.''' capturesig = pyqtSignal(QgsPointXY) def __init__(self, settings, iface): QgsMapToolEmitPoint.__init__(self, iface.mapCanvas()) self.iface = iface self.canvas = iface.mapCanvas() self.settings = settings self.capture4326 = False self.canvasClicked.connect(self.clicked) self.marker = None def activate(self): '''When activated set the cursor to a crosshair.''' self.canvas.setCursor(Qt.CrossCursor) def deactivate(self): self.removeMarker() def formatCoord(self, pt, delimiter): '''Format the coordinate string according to the settings from the settings dialog.''' if self.settings.captureProjIsWgs84(): # ProjectionTypeWgs84 # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) lat = pt4326.y() lon = pt4326.x() if self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDMS: # DMS msg = formatDmsString(lat, lon, True, self.settings.dmsPrecision, self.settings.coordOrder, delimiter) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDDMMSS: # DDMMSS msg = formatDmsString(lat, lon, False, self.settings.dmsPrecision, self.settings.coordOrder, delimiter) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeWKT: # WKT msg = 'POINT({:.{prec}f} {:.{prec}f})'.format( pt4326.x(), pt4326.y(), prec=self.settings.decimalDigits) elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeGeoJSON: # GeoJSON msg = '{{"type": "Point","coordinates": [{:.{prec}f},{:.{prec}f}]}}'.format( pt4326.x(), pt4326.y(), prec=self.settings.decimalDigits) else: # decimal degrees if self.settings.coordOrder == self.settings.OrderYX: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt4326.y(), delimiter, pt4326.x(), prec=self.settings.decimalDigits) else: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt4326.x(), delimiter, pt4326.y(), prec=self.settings.decimalDigits) elif self.settings.captureProjIsProjectCRS(): # Projection in the project CRS if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.y(), delimiter, pt.x(), prec=self.settings.decimalDigits) else: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.x(), delimiter, pt.y(), prec=self.settings.decimalDigits) else: msg = 'POINT({:.{prec}f} {:.{prec}f})'.format( pt.x(), pt.y(), prec=self.settings.decimalDigits) elif self.settings.captureProjIsCustomCRS(): # Projection is a custom CRS canvasCRS = self.canvas.mapSettings().destinationCrs() customCRS = self.settings.captureCustomCRS() transform = QgsCoordinateTransform(canvasCRS, customCRS, QgsProject.instance()) pt = transform.transform(pt.x(), pt.y()) if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.y(), delimiter, pt.x(), prec=self.settings.decimalDigits) else: msg = '{:.{prec}f}{}{:.{prec}f}'.format( pt.x(), delimiter, pt.y(), prec=self.settings.decimalDigits) else: msg = 'POINT({:.{prec}f} {:.{prec}f})'.format( pt.x(), pt.y(), prec=self.settings.decimalDigits) elif self.settings.captureProjIsMGRS(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) try: msg = mgrs.toMgrs(pt4326.y(), pt4326.x()) except: msg = None elif self.settings.captureProjIsPlusCodes(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) try: msg = olc.encode(pt4326.y(), pt4326.x(), self.settings.plusCodesLength) except: msg = None elif self.settings.captureProjIsUTM(): # Make sure the coordinate is transformed to EPSG:4326 canvasCRS = self.canvas.mapSettings().destinationCrs() if canvasCRS == epsg4326: pt4326 = pt else: transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) msg = latLon2UtmString(pt4326.y(), pt4326.x(), self.settings.dmsPrecision) if msg == '': msg = None msg = '{}{}{}'.format(self.settings.capturePrefix, msg, self.settings.captureSuffix) return msg def canvasMoveEvent(self, event): '''Capture the coordinate as the user moves the mouse over the canvas. Show it in the status bar.''' try: pt = self.toMapCoordinates(event.pos()) msg = self.formatCoord(pt, ', ') formatString = self.coordFormatString() if msg == None: self.iface.statusBarIface().showMessage("") else: self.iface.statusBarIface().showMessage( "{} - {}".format(msg, formatString), 4000) except: self.iface.statusBarIface().showMessage("") def coordFormatString(self): if self.settings.captureProjIsWgs84(): if self.settings.wgs84NumberFormat == self.settings.Wgs84TypeDecimal: if self.settings.coordOrder == self.settings.OrderYX: s = 'Lat Lon' else: s = 'Lon Lat' elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeWKT: s = 'WKT' elif self.settings.wgs84NumberFormat == self.settings.Wgs84TypeGeoJSON: s = 'GeoJSON' else: s = 'DMS' elif self.settings.captureProjIsProjectCRS(): crsID = self.canvas.mapSettings().destinationCrs().authid() if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: s = '{} - Y,X'.format(crsID) else: s = '{} - X,Y'.format(crsID) else: # WKT s = 'WKT' elif self.settings.captureProjIsMGRS(): s = 'MGRS' elif self.settings.captureProjIsUTM(): s = 'Standard UTM' elif self.settings.captureProjIsPlusCodes(): s = 'Plus Codes' elif self.settings.captureProjIsCustomCRS(): if self.settings.otherNumberFormat == 0: # Numerical if self.settings.coordOrder == self.settings.OrderYX: s = '{} - Y,X'.format(self.settings.captureCustomCRSID()) else: s = '{} - X,Y'.format(self.settings.captureCustomCRSID()) else: # WKT s = 'WKT' else: # Should never happen s = '' return s def clicked(self, pt, b): '''Capture the coordinate when the mouse button has been released, format it, and copy it to the clipboard.''' if settings.captureShowLocation: if self.marker is None: self.marker = QgsVertexMarker(self.canvas) self.marker.setIconSize(18) self.marker.setPenWidth(2) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.setCenter(pt) else: self.removeMarker() try: if self.capture4326: canvasCRS = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(canvasCRS, epsg4326, QgsProject.instance()) pt4326 = transform.transform(pt.x(), pt.y()) self.capturesig.emit(pt4326) return msg = self.formatCoord(pt, self.settings.delimiter) formatString = self.coordFormatString() if msg != None: clipboard = QApplication.clipboard() clipboard.setText(msg) self.iface.messageBar().pushMessage( "", "{} coordinate {} copied to the clipboard".format( formatString, msg), level=Qgis.Info, duration=4) except Exception as e: self.iface.messageBar().pushMessage( "", "Invalid coordinate: {}".format(e), level=Qgis.Warning, duration=4) def removeMarker(self): if self.marker is not None: self.canvas.scene().removeItem(self.marker) self.marker = None
class captureGPSFeatures(FieldRestrictionTypeUtilsMixin): def __init__(self, iface, featuresWithGPSToolbar): TOMsMessageLog.logMessage("In captureGPSFeatures::init", level=Qgis.Info) FieldRestrictionTypeUtilsMixin.__init__(self, iface) # Save reference to the QGIS interface self.iface = iface self.canvas = self.iface.mapCanvas() self.featuresWithGPSToolbar = featuresWithGPSToolbar self.gpsMapTool = False self.marker = None # This will set up the items on the toolbar # Create actions self.gnssToolGroup = QActionGroup(featuresWithGPSToolbar) self.actionCreateRestriction = QAction(QIcon(":/plugins/featureswithgps/resources/mActionAddTrack.svg"), QCoreApplication.translate("MyPlugin", "Create Restriction"), self.iface.mainWindow()) self.actionCreateRestriction.setCheckable(True) self.actionAddGPSLocation = QAction(QIcon(":/plugins/featureswithgps/resources/greendot3.png"), QCoreApplication.translate("MyPlugin", "Add vertex from gnss"), self.iface.mainWindow()) #self.actionAddGPSLocation.setCheckable(True) self.actionRemoveRestriction = QAction(QIcon(":plugins/featureswithgps/resources/mActionDeleteTrack.svg"), QCoreApplication.translate("MyPlugin", "Remove Restriction"), self.iface.mainWindow()) self.actionRemoveRestriction.setCheckable(True) self.actionRestrictionDetails = QAction(QIcon(":/plugins/featureswithgps/resources/mActionGetInfo.svg"), QCoreApplication.translate("MyPlugin", "Get Restriction Details"), self.iface.mainWindow()) self.actionRestrictionDetails.setCheckable(True) self.gnssToolGroup.addAction(self.actionRestrictionDetails) self.actionCreateSign = QAction(QIcon(":/plugins/featureswithgps/resources/mActionSetEndPoint.svg"), QCoreApplication.translate("MyPlugin", "Create sign from gnss"), self.iface.mainWindow()) self.actionCreateSign.setCheckable(True) self.actionCreateMTR = QAction(QIcon(":/plugins/featureswithgps/resources/UK_traffic_sign_606F.svg"), QCoreApplication.translate("MyPlugin", "Create moving traffic restriction"), self.iface.mainWindow()) self.actionCreateMTR.setCheckable(True) self.actionMoveFeatureToDifferentLayer = QAction(QIcon(""), QCoreApplication.translate("MyPlugin", "Move feature to different layer"), self.iface.mainWindow()) self.actionMoveFeatureToDifferentLayer.setCheckable(True) self.gnssToolGroup.addAction(self.actionMoveFeatureToDifferentLayer) # Add actions to the toolbar self.featuresWithGPSToolbar.addAction(self.actionCreateRestriction) self.featuresWithGPSToolbar.addAction(self.actionAddGPSLocation) self.featuresWithGPSToolbar.addAction(self.actionRestrictionDetails) #self.featuresWithGPSToolbar.addAction(self.actionRemoveRestriction) self.featuresWithGPSToolbar.addAction(self.actionCreateSign) #self.featuresWithGPSToolbar.addAction(self.actionCreateMTR) self.featuresWithGPSToolbar.addAction(self.actionMoveFeatureToDifferentLayer) self.gnssToolGroup.addAction(self.actionCreateRestriction) #self.gnssToolGroup.addAction(self.actionAddGPSLocation) #self.gnssToolGroup.addAction(self.actionRemoveRestriction) self.gnssToolGroup.addAction(self.actionRestrictionDetails) #self.gnssToolGroup.addAction(self.actionCreateSign) #self.gnssToolGroup.addAction(self.actionCreateMTR) self.gnssToolGroup.addAction(self.actionMoveFeatureToDifferentLayer) self.gnssToolGroup.setExclusive(True) self.gnssToolGroup.triggered.connect(self.onGroupTriggered) # Connect action signals to slots self.actionCreateRestriction.triggered.connect(self.doCreateRestriction) self.actionAddGPSLocation.triggered.connect(self.doAddGPSLocation) self.actionRestrictionDetails.triggered.connect(self.doRestrictionDetails) #self.actionRemoveRestriction.triggered.connect(self.doRemoveRestriction) self.actionCreateSign.triggered.connect(self.doCreateSign) #self.actionCreateMTR.triggered.connect(self.doCreateMTR) self.actionMoveFeatureToDifferentLayer.triggered.connect(self.doMoveFeatureToDifferentLayer) self.actionCreateRestriction.setEnabled(False) self.actionAddGPSLocation.setEnabled(False) self.actionRestrictionDetails.setEnabled(False) #self.actionRemoveRestriction.setEnabled(False) self.actionCreateSign.setEnabled(False) #self.actionCreateMTR.setEnabled(False) self.actionMoveFeatureToDifferentLayer.setEnabled(False) self.searchBar = searchBar(self.iface, self.featuresWithGPSToolbar) self.searchBar.disableSearchBar() self.mapTool = None self.currGnssAction = None self.gpsConnection = None self.createMapToolDict = {} def enableFeaturesWithGPSToolbarItems(self): TOMsMessageLog.logMessage("In enablefeaturesWithGPSToolbarItems", level=Qgis.Warning) self.gpsAvailable = False self.closeTOMs = False self.tableNames = TOMsLayers(self.iface) self.params = gpsParams() self.tableNames.TOMsLayersNotFound.connect(self.setCloseTOMsFlag) #self.tableNames.gpsLayersNotFound.connect(self.setCloseCaptureGPSFeaturesFlag) self.params.TOMsParamsNotFound.connect(self.setCloseCaptureGPSFeaturesFlag) self.TOMsConfigFileObject = TOMsConfigFile() self.TOMsConfigFileObject.TOMsConfigFileNotFound.connect(self.setCloseTOMsFlag) self.TOMsConfigFileObject.initialiseTOMsConfigFile() self.tableNames.getLayers(self.TOMsConfigFileObject) self.prj = QgsProject().instance() self.dest_crs = self.prj.crs() TOMsMessageLog.logMessage("In captureGPSFeatures::init project CRS is " + self.dest_crs.description(), level=Qgis.Warning) self.transformation = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:4326"), self.dest_crs, self.prj) self.params.getParams() if self.closeTOMs: QMessageBox.information(self.iface.mainWindow(), "ERROR", ("Unable to start editing tool ...")) #self.actionProposalsPanel.setChecked(False) return # TODO: allow function to continue without GPS enabled ... # Now check to see if the port is set. If not assume that just normal tools gpsPort = self.params.setParam("gpsPort") TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems: GPS port is: {}".format(gpsPort), level=Qgis.Warning) self.gpsConnection = None if gpsPort: self.gpsAvailable = True if self.gpsAvailable == True: self.curr_gps_location = None self.curr_gps_info = None TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems - GPS port is specified ", level=Qgis.Info) self.gps_thread = GPS_Thread(self.dest_crs, gpsPort) thread = QThread() self.gps_thread.moveToThread(thread) self.gps_thread.gpsActivated.connect(self.gpsStarted) self.gps_thread.gpsPosition.connect(self.gpsPositionProvided) self.gps_thread.gpsDeactivated.connect(functools.partial(self.gpsStopped)) self.gps_thread.gpsError.connect(self.gpsErrorEncountered) #self.gps_thread.progress.connect(progressBar.setValue) thread.started.connect(self.gps_thread.startGPS) #thread.finished.connect(functools.partial(self.gpsStopped, thread)) thread.start() self.thread = thread TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems - attempting connection ", level=Qgis.Info) time.sleep(1.0) try: self.roamDistance = float(self.params.setParam("roamDistance")) except Exception as e: TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems:init: roamDistance issue: {}".format(e), level=Qgis.Warning) self.roamDistance = 5.0 self.enableToolbarItems() self.createMapToolDict = {} def enableToolbarItems(self): TOMsMessageLog.logMessage("In enableToolbarItems", level=Qgis.Warning) self.actionCreateRestriction.setEnabled(True) self.actionRestrictionDetails.setEnabled(True) #self.actionRemoveRestriction.setEnabled(True) #self.actionCreateSign.setEnabled(True) #self.actionCreateMTR.setEnabled(True) self.actionMoveFeatureToDifferentLayer.setEnabled(True) self.searchBar.enableSearchBar() self.currMapTool = None self.theCurrentMapTool = None self.iface.currentLayerChanged.connect(self.changeCurrLayer2) self.canvas.mapToolSet.connect(self.changeMapTool2) self.canvas.extentsChanged.connect(self.changeExtents) # transaction for move ... self.localTransaction = MoveLayerTransaction(self.iface) def enableGnssToolbarItem(self): if self.gpsConnection: self.actionAddGPSLocation.setEnabled(True) self.actionCreateSign.setEnabled(True) self.lastCentre = QgsPointXY(0,0) def disableGnssToolbarItem(self): self.actionAddGPSLocation.setEnabled(False) self.actionCreateSign.setEnabled(False) def disableToolbarItems(self): self.actionCreateRestriction.setEnabled(False) self.actionRestrictionDetails.setEnabled(False) self.actionRemoveRestriction.setEnabled(False) self.actionCreateSign.setEnabled(False) #self.actionCreateMTR.setEnabled(False) self.actionMoveFeatureToDifferentLayer.setEnabled(False) self.searchBar.disableSearchBar() """if self.gpsConnection: self.actionAddGPSLocation.setEnabled(False)""" def setCloseTOMsFlag(self): self.closeTOMs = True QMessageBox.information(self.iface.mainWindow(), "ERROR", ("Now closing TOMs ...")) def disableFeaturesWithGPSToolbarItems(self): TOMsMessageLog.logMessage("In disablefeaturesWithGPSToolbarItems", level=Qgis.Warning) if self.gpsConnection and not self.closeTOMs: self.gps_thread.endGPS() self.disableToolbarItems() # TODO: Need to delete any tools ... for layer, mapTool in self.createMapToolDict.items (): try: status = layer.rollBack() except Exception as e: None """reply = QMessageBox.information(None, "Information", "Problem rolling back changes" + str(self.currLayer.commitErrors()), QMessageBox.Ok)""" del mapTool self.createMapToolDict = {} try: self.iface.currentLayerChanged.disconnect(self.changeCurrLayer2) except Exception as e: TOMsMessageLog.logMessage( "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for currentLayerChanged {}".format(e), level=Qgis.Warning) try: self.canvas.mapToolSet.disconnect(self.changeMapTool2) except Exception as e: TOMsMessageLog.logMessage( "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for mapToolSet {}".format( e), level=Qgis.Warning) try: self.canvas.extentsChanged.disconnect(self.changeExtents) except Exception as e: TOMsMessageLog.logMessage( "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for extentsChanged {}".format( e), level=Qgis.Warning) self.tableNames.removePathFromLayerForms() def setCloseCaptureGPSFeaturesFlag(self): self.closeCaptureGPSFeatures = True self.gpsAvailable = True def onGroupTriggered(self, action): # hold the current action self.currGnssAction = action TOMsMessageLog.logMessage("In onGroupTriggered: curr action is {}".format(action.text()), level=Qgis.Info) """ Using signals for ChangeTool and ChangeLayer to manage the tools - with the following functions """ def isGnssTool(self, mapTool): if (isinstance(mapTool, CreateRestrictionTool) or isinstance(mapTool, GeometryInfoMapTool) or isinstance(mapTool, RemoveRestrictionTool)): return True return False def changeMapTool2(self): TOMsMessageLog.logMessage( "In changeMapTool2 ...", level=Qgis.Info) currMapTool = self.iface.mapCanvas().mapTool() if not self.isGnssTool(currMapTool): TOMsMessageLog.logMessage( "In changeMapTool2. Unchecking action ...", level=Qgis.Info) if self.currGnssAction: self.currGnssAction.setChecked(False) else: TOMsMessageLog.logMessage( "In changeMapTool2. No action for gnssTools.", level=Qgis.Info) TOMsMessageLog.logMessage( "In changeMapTool2. finished.", level=Qgis.Info) #print('tool unset') def changeCurrLayer2(self): TOMsMessageLog.logMessage("In changeLayer2 ... ", level=Qgis.Info) try: currMapTool = self.iface.mapCanvas().mapTool() self.currGnssAction.setChecked(False) except Exception as e: None """if self.isGnssTool(currMapTool): TOMsMessageLog.logMessage("In changeLayer2. Action triggered ... ", level=Qgis.Info) self.currGnssAction.trigger() # assumption is that there is an action associated with the tool else: TOMsMessageLog.logMessage( "In changeLayer2. No action for currentMapTool.", level=Qgis.Info)""" TOMsMessageLog.logMessage( "In changeLayer2. finished.", level=Qgis.Info) print('layer changed') def doCreateRestriction(self): TOMsMessageLog.logMessage("In doCreateRestriction", level=Qgis.Info) self.currLayer = self.iface.activeLayer() if not self.currLayer: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return # TODO: Check that this is a restriction layer if self.actionCreateRestriction.isChecked(): TOMsMessageLog.logMessage("In doCreateRestriction - tool activated", level=Qgis.Info) TOMsMessageLog.logMessage( "In doCreateRestriction: current map tool {}".format(type(self.iface.mapCanvas().mapTool()).__name__), level=Qgis.Info) self.createRestrictionMapTool = self.createMapToolDict.get(self.currLayer) if not self.createRestrictionMapTool: TOMsMessageLog.logMessage("In doCreateRestriction. creating new map tool", level=Qgis.Info) self.createRestrictionMapTool = CreateRestrictionTool(self.iface, self.currLayer) self.createMapToolDict[self.currLayer] = self.createRestrictionMapTool TOMsMessageLog.logMessage("In doCreateRestriction. Here 1", level=Qgis.Info) self.iface.mapCanvas().setMapTool(self.createRestrictionMapTool) TOMsMessageLog.logMessage("In doCreateRestriction. Here 2", level=Qgis.Info) if not self.createRestrictionMapTool.isCapturing(): if self.currLayer.isEditable() == True: if self.currLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(self.currLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In doCreateRestriction: changes committed", level=Qgis.Info) if self.currLayer.readOnly() == True: TOMsMessageLog.logMessage("In doCreateRestriction - Not able to start transaction ...", level=Qgis.Info) else: if self.currLayer.startEditing() == False: reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok) return TOMsMessageLog.logMessage("In doCreateRestriction. Here 3", level=Qgis.Info) else: TOMsMessageLog.logMessage("In doCreateRestriction - tool deactivated", level=Qgis.Info) if self.createRestrictionMapTool: self.iface.mapCanvas().unsetMapTool(self.createRestrictionMapTool) self.currMapTool = None self.currentlySelectedLayer = None self.actionCreateRestriction.setChecked(False) # TODO: stop editting on layers?? TOMsMessageLog.logMessage("In doCreateRestriction. Here 4", level=Qgis.Info) # -- end of tools for signals def changeExtents(self): TOMsMessageLog.logMessage("In changeExtents ... ", level=Qgis.Info) def doAddGPSLocation(self): # need to have a addPointFromGPS function within each tool TOMsMessageLog.logMessage("In doAddGPSLocation", level=Qgis.Info) if self.gpsConnection: if self.curr_gps_location: try: status = self.createRestrictionMapTool.addPointFromGPS(self.curr_gps_location, self.curr_gps_info) except Exception as e: TOMsMessageLog.logMessage("In doAddGPSLocation: Problem adding gnss location: {}".format(e), level=Qgis.Warning) reply = QMessageBox.information(self.iface.mainWindow(), "Error", "Problem adding gnss location ... ", QMessageBox.Ok) else: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "No position found ...", QMessageBox.Ok) else: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "You need to activate the tool first ...", QMessageBox.Ok) def doRestrictionDetails(self): """ Select point and then display details. Assume that there is only one of these map tools in existence at any one time ?? """ TOMsMessageLog.logMessage("In doRestrictionDetails", level=Qgis.Info) # TODO: Check whether or not there is a create maptool available. If so, stop this and finish using that/those tools if not self.iface.activeLayer(): reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return if self.actionRestrictionDetails.isChecked(): TOMsMessageLog.logMessage("In doRestrictionDetails - tool activated", level=Qgis.Warning) self.showRestrictionMapTool = GeometryInfoMapTool(self.iface) self.iface.mapCanvas().setMapTool(self.showRestrictionMapTool) self.showRestrictionMapTool.notifyFeatureFound.connect(self.showRestrictionDetails) else: TOMsMessageLog.logMessage("In doRestrictionDetails - tool deactivated", level=Qgis.Warning) if self.showRestrictionMapTool: self.iface.mapCanvas().unsetMapTool(self.showRestrictionMapTool) self.actionRestrictionDetails.setChecked(False) #@pyqtSlot(str) def showRestrictionDetails(self, closestLayer, closestFeature): TOMsMessageLog.logMessage( "In showRestrictionDetails ... Layer: " + str(closestLayer.name()), level=Qgis.Info) self.showRestrictionMapTool.notifyFeatureFound.disconnect(self.showRestrictionDetails) # TODO: could improve ... basically check to see if transaction in progress ... if closestLayer.isEditable() == True: reply = QMessageBox.question(None, "Information", "There is a transaction in progress on this layer. This action will rollback back any changes. Do you want to continue?", QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.No: return if closestLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(closestLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In showRestrictionDetails: changes committed", level=Qgis.Info) """if self.iface.activeLayer().readOnly() == True: TOMsMessageLog.logMessage("In showSignDetails - Not able to start transaction ...", level=Qgis.Info) else: if self.iface.activeLayer().startEditing() == False: reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok) return""" self.dialog = self.iface.getFeatureForm(closestLayer, closestFeature) #self.TOMsUtils.setupRestrictionDialog(self.dialog, closestLayer, closestFeature) self.setupFieldRestrictionDialog(self.dialog, closestLayer, closestFeature) self.dialog.show() """ Decided that it is best to use the QGIS select/delete tools to manage removals. So these functions are not used """ def doRemoveRestriction(self): TOMsMessageLog.logMessage("In doRemoveRestriction", level=Qgis.Info) self.currLayer = self.iface.activeLayer() if not self.currLayer: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return if self.currLayer.readOnly() == True: """reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok)""" TOMsMessageLog.logMessage("In doRemoveRestriction - Not able to start transaction ...", level=Qgis.Info) self.actionRemoveRestriction.setChecked(False) return if self.actionRemoveRestriction.isChecked(): TOMsMessageLog.logMessage("In doRemoveRestriction - tool activated", level=Qgis.Warning) """self.mapTool = self.deleteMapToolDict.get(self.currLayer) if not self.mapTool: self.mapTool = RemoveRestrictionTool(self.iface) self.deleteMapToolDict[self.currLayer] = self.mapTool""" self.mapTool = RemoveRestrictionTool(self.iface) #self.removeRestrictionMapTool.setAction(self.actionRemoveRestriction) self.iface.mapCanvas().setMapTool(self.removeRestrictionMapTool) #self.gpsMapTool = True #self.removeRestrictionMapTool.deactivated.connect(functools.partial(self.deactivateAction, self.actionRemoveRestriction)) #self.iface.currentLayerChanged.connect(self.changeCurrLayer) #self.canvas.mapToolSet.connect(self.changeMapTool) self.removeRestrictionMapTool.notifyFeatureFound.connect(self.removeRestriction) else: TOMsMessageLog.logMessage("In doRemoveRestriction - tool deactivated", level=Qgis.Warning) self.removeRestrictionMapTool.notifyFeatureFound.disconnect(self.removeRestriction) #self.canvas.mapToolSet.disconnect(self.changeMapTool) #self.iface.currentLayerChanged.disconnect(self.changeCurrLayer) self.iface.mapCanvas().unsetMapTool(self.removeRestrictionMapTool) #self.removeRestrictionMapTool.deactivate() #self.mapTool = None self.actionRemoveRestriction.setChecked(False) #@pyqtSlot(str) def removeRestriction(self, closestLayer, closestFeature): TOMsMessageLog.logMessage( "In removeRestriction ... Layer: " + str(closestLayer.name()), level=Qgis.Info) if closestLayer.isEditable() == True: if closestLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(closestLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In removeRestriction: changes committed", level=Qgis.Info) if self.currLayer.startEditing() == False: reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok) return # TODO: Sort out this for UPDATE # self.setDefaultRestrictionDetails(closestFeature, closestLayer) closestLayer.deleteFeature(closestFeature.id()) if closestLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(closestLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In removeRestriction: changes committed", level=Qgis.Info) """ This is a tool for adding a point feature. currently only used for signs, but could be used for any point """ def doCreateSign(self): TOMsMessageLog.logMessage("In doCreateSign", level=Qgis.Info) if self.actionCreateSign.isChecked(): self.currMapTool = self.canvas.mapTool() self.currentlySelectedLayer = self.iface.activeLayer() self.signsLayer = self.tableNames.setLayer("Signs") self.iface.setActiveLayer(self.signsLayer) self.createPointMapTool = CreatePointTool(self.iface, self.signsLayer) TOMsMessageLog.logMessage("In doCreateSign - tool activated", level=Qgis.Info) self.signsLayer.editingStopped.connect(self.reinstateMapTool) self.actionCreateSign.setChecked(False) self.iface.mapCanvas().setMapTool(self.createPointMapTool) """ add the point from the gnss """ try: status = self.canvas.mapTool().addPointFromGPS(self.curr_gps_location, self.curr_gps_info) except Exception as e: TOMsMessageLog.logMessage("In doCreateSign: Problem adding gnss location: {}".format(e), level=Qgis.Warning) reply = QMessageBox.information(self.iface.mainWindow(), "Error", "Problem adding gnss location ... ", QMessageBox.Ok) """ Not currently used, but want to develop ... """ def doCreateMTR(self): TOMsMessageLog.logMessage("In doCreateMTR", level=Qgis.Info) if self.actionCreateMTR.isChecked(): TOMsMessageLog.logMessage("In doCreateMTR - tool activated", level=Qgis.Info) # Open MTR form ... try: self.thisMtrForm except AttributeError: self.thisMtrForm = mtrForm(self.iface) #res = mtrFormFactory.prepareForm(self.iface, self.dbConn, self.dialog) #self.mtrTypeCB = self.dialog.findChild(QComboBox, "cmb_MTR_list") #self.mtrTypeCB.activated[str].connect(self.onLocalChanged) #self.currDialog.findChild(QComboBox, "cmb_MTR_list").activated[str].connect(self.onChanged) """ Need to setup dialog: a. create drop down b. link structure of form to different options from drop down, e.g., Access Restriction needs ?? attributes and one point, Turn Restriction needs ?? attributes and two points c. link getPoint actions to buttons """ status = self.thisMtrForm.show() # Run the dialog event loop result = self.thisMtrForm.exec_() # else: TOMsMessageLog.logMessage("In doCreateMTR - tool deactivated", level=Qgis.Info) #self.iface.mapCanvas().unsetMapTool(self.mapTool) #self.mapTool = None self.actionCreateMTR.setChecked(False) self.gpsMapTool = False def onLocalChanged(self, text): TOMsMessageLog.logMessage( "In generateFirstStageForm::selectionchange. " + text, level=Qgis.Info) res = mtrFormFactory.prepareForm(self.iface, self.dbConn, self.dialog, text) """ Used with the createSign tool to reinstate the last used maptool, i.e., to allow the interupt of feature creation """ def reinstateMapTool(self): TOMsMessageLog.logMessage("In reinstateMapTool ... ", level=Qgis.Info) self.iface.activeLayer().editingStopped.disconnect(self.reinstateMapTool) if self.currMapTool: TOMsMessageLog.logMessage( "In reinstateMapTool. layer to be reinstated {} using tool {}".format(self.currentlySelectedLayer.name(), self.currMapTool.toolName()), level=Qgis.Warning) # now reinstate if self.currentlySelectedLayer: self.iface.setActiveLayer(self.currentlySelectedLayer) self.iface.mapCanvas().setMapTool(self.currMapTool) def doMoveFeatureToDifferentLayer(self): """ Select point and then display details. Assume that there is only one of these map tools in existence at any one time ?? """ TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer", level=Qgis.Info) # TODO: Check whether or not there is a create maptool available. If so, stop this and finish using that/those tools if not self.iface.activeLayer(): reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return if self.actionMoveFeatureToDifferentLayer.isChecked(): TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer - tool activated", level=Qgis.Warning) self.moveFeatureToDifferentLayerMapTool = ChangeLayerMapTool(self.iface, self.localTransaction) self.iface.mapCanvas().setMapTool(self.moveFeatureToDifferentLayerMapTool) #self.showRestrictionMapTool.notifyFeatureFound.connect(self.showRestrictionDetails) else: TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer - tool deactivated", level=Qgis.Warning) if self.moveFeatureToDifferentLayerMapTool: self.iface.mapCanvas().unsetMapTool(self.moveFeatureToDifferentLayerMapTool) self.actionMoveFeatureToDifferentLayer.setChecked(False) #@pyqtSlot(QgsGpsConnection) def gpsStarted(self, connection): TOMsMessageLog.logMessage("In enableTools - GPS connection found ", level=Qgis.Info) self.gpsConnection = connection # marker self.marker = QgsVertexMarker(self.canvas) self.marker.setColor(QColor(255, 0, 0)) # (R,G,B) self.marker.setIconSize(10) self.marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.marker.setPenWidth(3) self.enableGnssToolbarItem() reply = QMessageBox.information(None, "Information", "Connection found", QMessageBox.Ok) #@pyqtSlot() def gpsStopped(self): TOMsMessageLog.logMessage("In enableTools - GPS connection stopped ", level=Qgis.Warning) self.gps_thread.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() if self.gpsConnection: if self.canvas is not None: self.marker.hide() self.canvas.scene().removeItem(self.marker) self.gpsConnection = None self.disableGnssToolbarItem() #@pyqtSlot() def gpsPositionProvided(self, mapPointXY, gpsInfo): """reply = QMessageBox.information(None, "Information", "Position provided", QMessageBox.Ok)""" TOMsMessageLog.logMessage("In enableTools - ******** initial GPS location provided " + mapPointXY.asWkt(), level=Qgis.Info) self.curr_gps_location = mapPointXY self.curr_gps_info = gpsInfo wgs84_pointXY = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude) wgs84_point = QgsPoint(wgs84_pointXY) wgs84_point.transform(self.transformation) x = wgs84_point.x() y = wgs84_point.y() new_mapPointXY = QgsPointXY(x, y) TOMsMessageLog.logMessage("In enableTools - ******** transformed GPS location provided " + str(gpsInfo.longitude) + ":" + str(gpsInfo.latitude) + "; " + new_mapPointXY.asWkt(), level=Qgis.Info) if gpsInfo.pdop >= 1: # gps ok self.marker.setColor(QColor(0, 200, 0)) else: self.marker.setColor(QColor(255, 0, 0)) self.marker.setCenter(mapPointXY) self.marker.show() #self.canvas.setCenter(mapPointXY) """TOMsMessageLog.logMessage("In enableTools: distance from last fix: {}".format(self.lastCentre.distance(mapPointXY)), level=Qgis.Info)""" if self.lastCentre.distance(mapPointXY) > self.roamDistance: self.lastCentre = mapPointXY self.canvas.setCenter(mapPointXY) TOMsMessageLog.logMessage( "In enableTools: distance from last fix: {}".format(self.lastCentre.distance(mapPointXY)), level=Qgis.Warning) self.canvas.refresh() # TODO: populate message bar with details about satellites, etc #@pyqtSlot(Exception, str) def gpsErrorEncountered(self, e): TOMsMessageLog.logMessage("In enableTools - GPS connection has error {}".format(e), level=Qgis.Warning) """self.actionCreateRestriction.setEnabled(False) self.actionAddGPSLocation.setEnabled(False)""" self.disableGnssToolbarItem()
class SplineTool(QgsMapToolEdit): def __init__(self, iface): super(SplineTool, self).__init__(iface.mapCanvas()) self.iface = iface self.canvas = self.iface.mapCanvas() self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.snap_marker = QgsVertexMarker(self.canvas) self.snapping_utils = self.canvas.snappingUtils() self.points = [] # digitized, not yet interpolated points self.type = QgsWkbTypes.LineGeometry # layer geometry type self.tolerance = None self.tightness = None self.is_polygon = None self.cursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #FF0000", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ ", ])) s = QgsSettings() self.snap_col = s.value("/qgis/digitizing/snap_color", QColor("#ff00ff")) def canvasMoveEvent(self, event): color = QColor(255, 0, 0, 100) self.rb.setColor(color) self.rb.setWidth(1) point = self.toMapCoordinates(event.pos()) # try to snap to a feature result = self.snapping_utils.snapToMap(point) if result.isValid(): point = result.point() self.update_snap_marker(snapped_pt=point) else: self.update_snap_marker() points = list(self.points) points.append(QgsPoint(point)) points = interpolate(points) self.set_rubber_band_points(points) def canvasReleaseEvent(self, event): color = QColor(255, 0, 0, 100) self.rb.setColor(color) self.rb.setWidth(1) point = self.toMapCoordinates(event.pos()) if event.button() == Qt.LeftButton: # try to snap to a feature result = self.snapping_utils.snapToMap(point) if result.isValid(): point = result.point() self.points.append(QgsPoint(point)) points = interpolate(self.points) self.set_rubber_band_points(points) else: if len(self.points) >= 2: # refresh without last point self.refresh() self.create_feature() self.reset_points() self.reset_rubber_band() self.canvas.refresh() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.deactivate() self.activate() elif e.key() == Qt.Key_Backspace: if self.points: self.points.pop() points = interpolate(self.points) self.set_rubber_band_points(points) self.canvas.refresh() def reset_points(self): self.points = [] # Create feature from digitized points, i.e. without the last moving point # where right click happened. This the same way how core QGIS Add Feature works. def create_feature(self): layer = self.iface.activeLayer() provider = layer.dataProvider() fields = provider.fields() f = QgsFeature(fields) coords = [QgsPointXY(pt) for pt in interpolate(self.points)] proj = QgsProject.instance() if layer.crs() != proj.crs(): trans_context = proj.transformContext() transf = QgsCoordinateTransform(proj.crs(), layer.crs(), trans_context) coords_tmp = coords[:] coords = [] for point in coords_tmp: transformed_pt = transf.transform(point) coords.append(transformed_pt) # Add geometry to feature if self.is_polygon: g = QgsGeometry.fromPolygonXY([coords]) else: g = QgsGeometry.fromPolylineXY(coords) f.setGeometry(g) # Add attribute fields to feature for field in fields.toList(): ix = fields.indexFromName(field.name()) f[field.name()] = provider.defaultValue(ix) layer.beginEditCommand("Feature added") settings = QSettings() disable_attributes = settings.value( "/qgis/digitizing/disable_enter_attribute_values_dialog", False, type=bool) layer.addFeature(f) if disable_attributes: layer.endEditCommand() else: dlg = self.iface.getFeatureForm(layer, f) if dlg.exec_(): layer.endEditCommand() else: layer.destroyEditCommand() def refresh(self): if self.points: points = interpolate(self.points) self.set_rubber_band_points(points) def canvasPressEvent(self, event): pass def showSettingsWarning(self): pass def activate(self): self.canvas.setCursor(self.cursor) layer = self.iface.activeLayer() self.type = layer.geometryType() self.is_polygon = False if self.type == QgsWkbTypes.PolygonGeometry: self.is_polygon = True def reset_rubber_band(self): self.rb.reset(self.type) def set_rubber_band_points(self, points): self.reset_rubber_band() for point in points: update = point is points[-1] if isinstance(point, QgsPoint): point = QgsPointXY(point) self.rb.addPoint(point, update) def update_snap_marker(self, snapped_pt=None): self.canvas.scene().removeItem(self.snap_marker) if snapped_pt is None: return self.snap_marker = QgsVertexMarker(self.canvas) self.snap_marker.setCenter(snapped_pt) self.snap_marker.setIconSize(16) self.snap_marker.setIconType(QgsVertexMarker.ICON_BOX) self.snap_marker.setPenWidth(3) self.snap_marker.setColor(self.snap_col) def deactivate(self): self.reset_points() self.update_snap_marker() self.reset_rubber_band() QgsMapToolEdit.deactivate(self) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True
class ZoomToLatLon(QDockWidget, FORM_CLASS): def __init__(self, lltools, iface, parent): super(ZoomToLatLon, self).__init__(parent) self.setupUi(self) self.canvas = iface.mapCanvas() self.marker = None self.zoomToolButton.setIcon( QIcon(':/images/themes/default/mActionZoomIn.svg')) self.clearToolButton.setIcon( QIcon(':/images/themes/default/mIconClearText.svg')) self.zoomToolButton.clicked.connect(self.zoomToPressed) self.clearToolButton.clicked.connect(self.removeMarker) self.lltools = lltools self.settings = lltools.settingsDialog self.iface = iface self.coordTxt.returnPressed.connect(self.zoomToPressed) self.canvas.destinationCrsChanged.connect(self.crsChanged) self.configure() def showEvent(self, e): self.configure() def closeEvent(self, event): self.removeMarker() event.accept() def crsChanged(self): if self.isVisible(): self.configure() def is_number(self, s): try: float(s) return True except ValueError: return False def configure(self): self.coordTxt.setText("") self.removeMarker() if self.settings.zoomToProjIsMGRS(): # This is an MGRS coordinate self.label.setText("Enter MGRS Coordinate") elif self.settings.zoomToProjIsPlusCodes(): self.label.setText("Enter Plus Codes") elif self.settings.zoomToProjIsStandardUtm(): self.label.setText("Enter Standard UTM") elif self.settings.zoomToProjIsWgs84(): if self.settings.zoomToCoordOrder == 0: self.label.setText("Enter 'Latitude, Longitude'") else: self.label.setText("Enter 'Longitude, Latitude'") elif self.settings.zoomToProjIsProjectCRS(): crsID = self.canvas.mapSettings().destinationCrs().authid() if self.settings.zoomToCoordOrder == 0: self.label.setText("Enter {} Y,X".format(crsID)) else: self.label.setText("Enter {} X,Y".format(crsID)) else: # Default to custom CRS crsID = self.settings.zoomToCustomCrsId() if self.settings.zoomToCoordOrder == 0: self.label.setText("Enter {} Y,X".format(crsID)) else: self.label.setText("Enter {} X,Y".format(crsID)) def convertCoordinate(self, text): try: if self.settings.zoomToProjIsMGRS(): # An MGRS coordinate only format has been specified. This will result in an exception # if it is not a valid MGRS coordinate text2 = re.sub(r'\s+', '', str(text)) # Remove all white space lat, lon = mgrs.toWgs(text2) return (lat, lon, epsg4326) if self.settings.zoomToProjIsPlusCodes(): # A Plus Codes coordinate has been selected. This will result in an exception # if it is not a valid plus codes coordinate. coord = olc.decode(text) lat = coord.latitudeCenter lon = coord.longitudeCenter return (lat, lon, epsg4326) if self.settings.zoomToProjIsStandardUtm(): # A Standard UTM coordinate has been selected. This will result in an exception # if it is not a valid utm coordinate. pt = utmString2Crs(text) return (pt.y(), pt.x(), epsg4326) # Check for other formats if text[0] == '{': # This may be a GeoJSON point codec = QTextCodec.codecForName("UTF-8") fields = QgsJsonUtils.stringToFields(text, codec) fet = QgsJsonUtils.stringToFeatureList(text, fields, codec) if (len(fet) == 0) or not fet[0].isValid(): raise ValueError('Invalid Coordinates') geom = fet[0].geometry() if geom.isEmpty() or (geom.wkbType() != QgsWkbTypes.Point): raise ValueError('Invalid GeoJSON Geometry') pt = geom.asPoint() return (pt.y(), pt.x(), epsg4326) # Check to see if it is standard UTM if isUtm(text): pt = utmString2Crs(text) return (pt.y(), pt.x(), epsg4326) # Check to see if it is an MGRS coordinate try: text2 = re.sub(r'\s+', '', str(text)) lat, lon = mgrs.toWgs(text2) return (lat, lon, epsg4326) except Exception: pass # Check to see if it is a plus codes string try: coord = olc.decode(text) lat = coord.latitudeCenter lon = coord.longitudeCenter return (lat, lon, epsg4326) except Exception: pass # Check to see if it is a WKT POINT format if re.search(r'POINT\(', text) is not None: m = re.findall( r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text) if len(m) != 1: raise ValueError('Invalid Coordinates') lon = float(m[0][0]) lat = float(m[0][1]) if self.settings.zoomToProjIsWgs84(): srcCrs = epsg4326 elif self.settings.zoomToProjIsProjectCRS(): srcCrs = self.canvas.mapSettings().destinationCrs() else: srcCrs = self.settings.zoomToCustomCRS() return (lat, lon, srcCrs) # We are left with either DMS or decimal degrees in one of the projections if self.settings.zoomToProjIsWgs84(): lat, lon = parseDMSString(text, self.settings.zoomToCoordOrder) return (lat, lon, epsg4326) # We are left with a non WGS 84 decimal projection coords = re.split(r'[\s,;:]+', text, 1) if len(coords) < 2: raise ValueError('Invalid Coordinates') if self.settings.zoomToCoordOrder == self.settings.OrderYX: lat = float(coords[0]) lon = float(coords[1]) else: lon = float(coords[0]) lat = float(coords[1]) if self.settings.zoomToProjIsProjectCRS(): srcCrs = self.canvas.mapSettings().destinationCrs() else: srcCrs = self.settings.zoomToCustomCRS() return (lat, lon, srcCrs) except Exception: raise ValueError('Invalid Coordinates') def zoomToPressed(self): try: text = self.coordTxt.text().strip() (lat, lon, srcCrs) = self.convertCoordinate(text) pt = self.lltools.zoomTo(srcCrs, lat, lon) if self.settings.persistentMarker: if self.marker is None: self.marker = QgsVertexMarker(self.canvas) self.marker.setCenter(pt) self.marker.setIconSize(18) self.marker.setPenWidth(2) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) elif self.marker is not None: self.removeMarker() except Exception: # traceback.print_exc() self.iface.messageBar().pushMessage("", "Invalid Coordinate", level=Qgis.Warning, duration=2) return def removeMarker(self): if self.marker is not None: self.canvas.scene().removeItem(self.marker) self.marker = None self.coordTxt.clear()