def punto2(self, pointTool2): coords2 = "{}, {}".format(pointTool2.x(), pointTool2.y()) # dibujar punto ptRb = QgsRubberBand(self.canvas, False) ptRb.setColor(QColor(0, 255, 0)) ptRb.setIconSize(7) ptRb.setWidth(5) ptRb.addPoint(QgsPointXY(pointTool2.x(), pointTool2.y())) # poner el texto del punto self.dlg.lineEdit2.setText(str(coords2))
class CurrentSelection(QgsRubberBand): """ Position marker for the current location in the viewer. """ class AniObject(QObject): def __init__(self, band): super(CurrentSelection.AniObject, self).__init__() self.color = QColor() @pyqtProperty(int) def alpha(self): return self.color.alpha() @alpha.setter def alpha(self, value): self.color.setAlpha(value) def __init__(self, canvas): super(CurrentSelection, self).__init__(canvas) self.outline = QgsRubberBand(canvas) self.outline.setBrushStyle(Qt.NoBrush) self.outline.setWidth(5) self.outline.setIconSize(30) self.aniobject = CurrentSelection.AniObject(self) self.anim = QPropertyAnimation(self.aniobject, "alpha") self.anim.setDuration(500) self.anim.setStartValue(50) self.anim.setEndValue(100) self.anim.valueChanged.connect(self.value_changed) def setOutlineColour(self, color): self.outline.setColor(color) def setToGeometry(self, geom, layer): super(CurrentSelection, self).setToGeometry(geom, layer) self.outline.setToGeometry(geom, layer) self.anim.stop() self.anim.start() def reset(self, geomtype=QGis.Line): super(CurrentSelection, self).reset(geomtype) self.outline.reset(geomtype) self.anim.stop() def value_changed(self, value): self.setColor(self.aniobject.color) self.update() def setColor(self, color): self.aniobject.color = color super(CurrentSelection, self).setColor(color)
class RubberBandResultRenderer(): def __init__(self, iface, color=QColor('magenta'), size=12): self.iface = iface self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor(color) self.rb.setIconSize(size) self.srs_wgs84 = QgsCoordinateReferenceSystem(4326) self.transformation = QgsCoordinateTransform(self.srs_wgs84, self.srs_wgs84) def show_point(self, point, center=False): #check srs if self.need_transform(): point = self.transform_point(point) self.rb.addPoint(point) if center: self.center_to_point(point) def clear(self): self.rb.reset(QGis.Point) def need_transform(self): return self.iface.mapCanvas().mapRenderer().destinationCrs( ).postgisSrid() != 4326 def transform_point(self, point): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs( ).srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transform(point) except: print 'Error on transform!' # DEBUG! need message??? return def center_to_point(self, point): canvas = self.iface.mapCanvas() new_extent = QgsRectangle(canvas.extent()) new_extent.scale(1, point) canvas.setExtent(new_extent) canvas.refresh()
class RubberBandResultRenderer(): def __init__(self, iface, color = QColor('magenta'), size = 12): self.iface = iface self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor(color) self.rb.setIconSize(size) self.srs_wgs84 = QgsCoordinateReferenceSystem(4326) self.transformation = QgsCoordinateTransform(self.srs_wgs84, self.srs_wgs84) def show_point(self, point, center=False): #check srs if self.need_transform(): point = self.transform_point(point) self.rb.addPoint(point) if center: self.center_to_point(point) def clear(self): self.rb.reset(QGis.Point) def need_transform(self): return self.iface.mapCanvas().mapRenderer().destinationCrs().postgisSrid() != 4326 def transform_point(self, point): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transform(point) except: print 'Error on transform!' # DEBUG! need message??? return def center_to_point(self, point): canvas = self.iface.mapCanvas() new_extent = QgsRectangle(canvas.extent()) new_extent.scale(1, point) canvas.setExtent(new_extent) canvas.refresh()
class DistanceDialog(QDialog, Ui_place_distance): def __init__(self, distance, canvas): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() # this is a reference, distance observation is modified in outer class self.distance = distance self.rubber = QgsRubberBand(canvas) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.x.setText("%.3f" % distance.point.x()) self.y.setText("%.3f" % distance.point.y()) self.observation.setValue(distance.observation) self.precision.setValue(distance.precision) self.observation.selectAll() @pyqtSignature("on_observation_valueChanged(double)") def on_observation_valueChanged(self, v): self.distance.observation = v self.rubber.setToGeometry(self.distance.geometry(), None) @pyqtSignature("on_precision_valueChanged(double)") def on_precision_valueChanged(self, v): self.distance.precision = v def accept(self): self.rubber.reset() QDialog.accept(self) def reject(self): self.rubber.reset() QDialog.reject(self) def closeEvent(self, e): self.rubber.reset()
class Create_points(QgsMapToolEmitPoint): def __init__(self, canvas): super(Create_points, self).__init__(canvas) self.points = QgsRubberBand(canvas, QgsWkbTypes.PointGeometry) def create_points_window(self): self.crt_pts_win = CreatePointsWindow() self.crt_pts_win.show() def canvasClicked(self, przycisk, punkt): print(przycisk, punkt) def deactivate(self): super(Create_points, self).deactivate() def canvasReleaseEvent(self, mouse_event): point = mouse_event.mapPoint() #print(point) dada = self.points.addPoint(point) #print(dada) self.points.setWidth(5) self.points.setIconSize(20) self.points.setIcon(QgsRubberBand.ICON_BOX) self.points.setColor(QColor('red'))
class RectangleTemplateWidget(QWidget, Ui_RectangleTemplateWidget): def __init__(self, canvas, msglog, current_missiontrack, parent=None): super(RectangleTemplateWidget, self).__init__(parent) self.setupUi(self) self.canvas = canvas self.msglog = msglog self.current_missiontrack = current_missiontrack self.setAttribute(Qt.WA_DeleteOnClose) self.rubber_band = None self.rubber_band_points = None self.initial_point = None self.template_type = 'rectangle_template' self.wp = list() self.mission = None self.distance = QgsDistanceArea() self.distance.setSourceCrs(QgsCoordinateReferenceSystem(4326), QgsProject.instance().transformContext()) self.distance.setEllipsoid('WGS84') self.wp_list = [] self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubber_band.setWidth(2) self.rubber_band.setColor(QColor("green")) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setColor(QColor("green")) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.initial_point = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.initial_point.setColor(QColor("red")) self.initial_point.setIcon(QgsRubberBand.ICON_CIRCLE) self.initial_point.setIconSize(10) self.onTarget = False self.area_points = None self.missionAreaDefined = False # Get the tools self.rectBy3Points_tool = RectBy3PointsTool(self.canvas) self.rectByFixedExtentTool = RectByFixedExtentTool(self.canvas, 0.0, 0.0) self.rect_from_center_tool = RectFromCenterTool(self.canvas) self.rect_from_center_fixed_tool = RectFromCenterFixedTool(self.canvas, 0.0, 0.0) self.drawRectangleButton.clicked.connect(self.draw_mission_area) self.centerOnTargetButton.clicked.connect(self.draw_mission_area) self.rectBy3Points_tool.msgbar.connect(self.pass_message_bar) self.depthButton.setAutoExclusive(False) self.altitudeButton.setAutoExclusive(False) self.depthButton.setChecked(True) self.altitudeButton.setChecked(False) self.alongTrackLabel.setEnabled(False) self.acrossTrackLabel.setEnabled(False) self.alongTLength.setEnabled(False) self.acrossTLength.setEnabled(False) self.fixedExtent.toggled.connect(self.fixed_extend_toggled) self.depthButton.toggled.connect(self.depth_toggled) self.altitudeButton.toggled.connect(self.altitude_toggled) self.initial_point_comboBox.currentIndexChanged.connect(self.change_initial_point) def get_template_mission(self): return self.mission def fixed_extend_toggled(self): is_checked = self.fixedExtent.isChecked() self.alongTrackLabel.setEnabled(is_checked) self.acrossTrackLabel.setEnabled(is_checked) self.alongTLength.setEnabled(is_checked) self.acrossTLength.setEnabled(is_checked) def depth_toggled(self): if self.depthButton.isChecked(): self.altitudeButton.setChecked(False) def altitude_toggled(self): if self.altitudeButton.isChecked(): self.depthButton.setChecked(False) def pass_message_bar(self, msg): self.msglog.logMessage("") self.msglog.logMessage(msg, "Lawn Mower", 0) def draw_mission_area(self): self.onTarget = False sender = self.sender().objectName() if not self.missionAreaDefined: if self.automaticExtent.isChecked(): if sender == self.drawRectangleButton.objectName(): self.msglog.logMessage("Click starting point", "Lawn Mower", 0) # Draw mission area self.rectBy3Points_tool = RectBy3PointsTool(self.canvas) self.canvas.setMapTool(self.rectBy3Points_tool) self.rectBy3Points_tool.msgbar.connect(self.pass_message_bar) self.rectBy3Points_tool.rb_reset_signal.connect(self.clear_initial_point) self.rectBy3Points_tool.rbFinished.connect(self.create_mission_area) elif sender == self.centerOnTargetButton.objectName(): self.onTarget = True self.msglog.logMessage("Click center point", "Lawn Mower", 0) # Draw mission area self.rect_from_center_tool = RectFromCenterTool(self.canvas) self.canvas.setMapTool(self.rect_from_center_tool) self.rect_from_center_tool.msgbar.connect(self.pass_message_bar) self.rect_from_center_tool.rb_reset_signal.connect(self.clear_initial_point) self.rect_from_center_tool.rbFinished.connect(self.create_mission_area) self.missionAreaDefined = True return elif self.fixedExtent.isChecked(): if self.alongTLength.value() != 0.0 and self.acrossTLength.value() != 0.0: if sender == self.drawRectangleButton.objectName(): self.msglog.logMessage("Click starting point", "Lawn Mower", 0) self.rectByFixedExtentTool = RectByFixedExtentTool(self.canvas, self.alongTLength.value(), self.acrossTLength.value()) self.canvas.setMapTool(self.rectByFixedExtentTool) self.rectByFixedExtentTool.msgbar.connect(self.pass_message_bar) self.rectByFixedExtentTool.rb_reset_signal.connect(self.clear_initial_point) self.rectByFixedExtentTool.rbFinished.connect(self.create_mission_area) self.missionAreaDefined = True elif sender == self.centerOnTargetButton.objectName(): self.onTarget = True self.msglog.logMessage("Click center point", "Lawn Mower", 0) self.rect_from_center_fixed_tool = RectFromCenterFixedTool(self.canvas, self.alongTLength.value(), self.acrossTLength.value()) self.canvas.setMapTool(self.rect_from_center_fixed_tool) self.rect_from_center_fixed_tool.msgbar.connect(self.pass_message_bar) self.rect_from_center_fixed_tool.rb_reset_signal.connect(self.clear_initial_point) self.rect_from_center_fixed_tool.rbFinished.connect(self.create_mission_area) return else: QMessageBox.warning(None, "Mission Template", "<center>Track lengths must be different from zero. </center>", QMessageBox.Close) return else: self.deactivate_tool() self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.initial_point.reset(QgsWkbTypes.PointGeometry) self.missionAreaDefined = False self.draw_mission_area() def clear_initial_point(self): """ Reset the initial point rubber band from canvas """ if self.initial_point is not None: self.initial_point.reset(QgsWkbTypes.PointGeometry) def change_initial_point(self): """ Change the initial point """ self.clear_initial_point() if self.area_points is not None: area = self.area_points index = self.initial_point_comboBox.currentIndex() self.initial_point.addPoint(area[index]) def create_mission_area(self, geom): self.pass_message_bar("") self.clear_initial_point() if geom is not None: # Store points to variables self.area_points = [QgsPointXY(geom.vertexAt(0)), QgsPointXY(geom.vertexAt(1)), QgsPointXY(geom.vertexAt(2)), QgsPointXY(geom.vertexAt(3))] self.change_initial_point() self.missionAreaDefined = True def preview_tracks(self): """ preview tracks on the canvas""" if self.missionAreaDefined: if self.altitudeButton.isChecked() and self.depthAltitudeBox.value() == 0: QMessageBox.warning(None, "Mission Template", "<center>Altitude must be different from zero. </center>", QMessageBox.Close) else: self.wp_list = self.compute_tracks(self.get_area_points()) self.track_to_mission(self.wp_list, self.get_z(), self.get_altitude_mode(), self.get_speed(), self.get_x_tolerance(), self.get_y_tolerance(), self.get_z_tolerance()) # show rubber band with temporal tracks self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.initial_point.reset(QgsWkbTypes.PointGeometry) for wp in self.wp_list: self.rubber_band.addPoint(QgsPointXY(wp.x(), wp.y())) self.rubber_band_points.addPoint(QgsPointXY(wp.x(), wp.y())) self.rubber_band_points.show() self.rubber_band.show() self.unset_map_tool() else: QMessageBox.warning(None, "Mission Template", "<center>Define first an area for the mission. </center>", QMessageBox.Close) def get_area_points(self): return self.area_points def get_num_across_tracks(self): return int(self.numAcrossTracks.value()) def get_altitude_mode(self): return self.altitudeButton.isChecked() def get_z(self): return self.depthAltitudeBox.value() def get_speed(self): return self.speed_doubleSpinBox.value() def get_x_tolerance(self): return self.x_tolerance_doubleSpinBox.value() def get_y_tolerance(self): return self.y_tolerance_doubleSpinBox.value() def get_z_tolerance(self): return self.z_tolerance_doubleSpinBox.value() def get_mission_type(self): return self.template_type def compute_tracks(self, area_points): """ Compute rectangle tracks :param area_points: points defining the extent of the tracks, they should be in WGS 84 lat/lon. first two points define the along track direction. :return: list of ordered waypoints of rectangle trajectory """ index = self.initial_point_comboBox.currentIndex() if index == 0: new_area = [area_points[0], area_points[1], area_points[2], area_points[3]] elif index == 1: new_area = [area_points[1], area_points[2], area_points[3], area_points[0]] elif index == 2: new_area = [area_points[2], area_points[3], area_points[0], area_points[1]] elif index == 3: new_area = [area_points[3], area_points[0], area_points[1], area_points[2]] new_area.append(area_points[index]) return new_area def track_to_mission(self, wp_list, z, altitude_mode, speed, tolerance_x, tolerance_y, tolerance_z): self.mission = Mission() for wp in range(len(wp_list)): if wp == 0: # first step type waypoint step = MissionStep() mwp = MissionWaypoint(MissionPosition(wp_list[wp].y(), wp_list[wp].x(), z, altitude_mode), speed, MissionTolerance(tolerance_x, tolerance_y, tolerance_z)) step.add_maneuver(mwp) self.mission.add_step(step) else: # rest of steps type section step = MissionStep() mwp = MissionSection(MissionPosition(wp_list[wp - 1].y(), wp_list[wp - 1].x(), z, altitude_mode), MissionPosition(wp_list[wp].y(), wp_list[wp].x(), z, altitude_mode), speed, MissionTolerance(tolerance_x, tolerance_y, tolerance_z)) step.add_maneuver(mwp) self.mission.add_step(step) def delete_widget(self): self.delete_all(self.layout()) self.deleteLater() self.close() def delete_all(self, layout): """ delete all widget from layout :param layout: layout is a qt layout """ if layout is not None: for i in reversed(range(layout.count())): item = layout.takeAt(i) widget = item.widget() if widget is not None: widget.deleteLater() else: self.delete_all(item.layout()) def unset_map_tool(self): """ Unset map tool from canvas. """ if self.automaticExtent.isChecked(): if self.onTarget: self.canvas.unsetMapTool(self.rect_from_center_tool) else: self.canvas.unsetMapTool(self.rectBy3Points_tool) if self.fixedExtent.isChecked(): if self.onTarget: self.canvas.unsetMapTool(self.rect_from_center_fixed_tool) else: self.canvas.unsetMapTool(self.rectByFixedExtentTool) def deactivate_tool(self): """ Deactivate tool. """ if self.rectBy3Points_tool: self.rectBy3Points_tool.deactivate() elif self.rectByFixedExtentTool: self.rectByFixedExtentTool.deactivate() if self.rect_from_center_tool: self.rect_from_center_tool.deactivate() elif self.rect_from_center_fixed_tool: self.rect_from_center_fixed_tool.deactivate() def close(self): self.unset_map_tool() self.deactivate_tool() if self.rubber_band is not None: self.canvas.scene().removeItem(self.rubber_band) del self.rubber_band self.rubber_band = None if self.rubber_band_points is not None: self.canvas.scene().removeItem(self.rubber_band_points) del self.rubber_band_points self.rubber_band_points = None if self.initial_point is not None: self.canvas.scene().removeItem(self.initial_point) del self.initial_point self.initial_point = None
class IntersectionDialog(QDialog, Ui_Intersection, SettingDialog): def __init__(self, iface, observations, initPoint): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() SettingDialog.__init__(self, self.settings, False, False) self.processButton.clicked.connect(self.doIntersection) self.okButton.clicked.connect(self.accept) self.finished.connect(self.resetRubber) self.initPoint = initPoint self.observations = [] self.solution = None self.report = "" self.rubber = QgsRubberBand(iface.mapCanvas(), QGis.Point) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIcon(self.settings.value("rubberIcon")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.observationTableWidget.displayRows(observations) self.observationTableWidget.itemChanged.connect(self.disbaleOKbutton) self.doIntersection() def resetRubber(self, dummy=0): self.rubber.reset() def disbaleOKbutton(self): self.okButton.setDisabled(True) def doIntersection(self): self.observations = [] self.solution = None self.report = "" self.rubber.reset() observations = self.observationTableWidget.getObservations() nObs = len(observations) if nObs < 2: self.reportBrowser.setText(QCoreApplication.translate("IntersectIt", "No intersection can be done " "with less than 2 observations.")) return if nObs == 2: if observations[0]["type"] == "distance" and observations[1]["type"] == "distance": intersection = TwoCirclesIntersection(observations, self.initPoint) elif observations[0]["type"] == "orientation" and observations[1]["type"] == "orientation": intersection = TwoOrientationIntersection(observations) else: intersection = DistanceOrientationIntersection(observations, self.initPoint) else: maxIter = self.advancedIntersecLSmaxIteration.value() threshold = self.advancedIntersecLSconvergeThreshold.value() intersection = LeastSquares(observations, self.initPoint, maxIter, threshold) self.reportBrowser.setText(intersection.report) if intersection.solution is not None: self.solution = intersection.solution self.observations = observations self.report = intersection.report self.okButton.setEnabled(True) self.rubber.setToGeometry(QgsGeometry().fromPoint(self.solution), None)
class SwissLocatorFilter(QgsLocatorFilter): HEADERS = { b'User-Agent': b'Mozilla/5.0 QGIS Swiss Geoportal Locator Filter' } message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget) def __init__(self, filter_type: FilterType, iface: QgisInterface = None, crs: str = None): """" :param filter_type: the type of filter :param locale_lang: the language of the locale. :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise :param crs: if iface is not given, it shall be provided, see clone() """ super().__init__() self.type = filter_type self.rubber_band = None self.feature_rubber_band = None self.iface = iface self.map_canvas = None self.settings = Settings() self.transform_ch = None self.transform_4326 = None self.map_tip = None self.current_timer = None self.crs = None self.event_loop = None self.result_found = False self.access_managers = {} self.nam_map_tip = None self.nam_fetch_feature = None if crs: self.crs = crs self.lang = get_language() self.searchable_layers = searchable_layers(self.lang, restrict=True) if iface is not None: # happens only in main thread self.map_canvas = iface.mapCanvas() self.map_canvas.destinationCrsChanged.connect( self.create_transforms) self.rubber_band = QgsRubberBand(self.map_canvas, QgsWkbTypes.PointGeometry) self.rubber_band.setColor(QColor(255, 255, 50, 200)) self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE) self.rubber_band.setIconSize(15) self.rubber_band.setWidth(4) self.rubber_band.setBrushStyle(Qt.NoBrush) self.feature_rubber_band = QgsRubberBand( self.map_canvas, QgsWkbTypes.PolygonGeometry) self.feature_rubber_band.setColor(QColor(255, 50, 50, 200)) self.feature_rubber_band.setFillColor(QColor(255, 255, 50, 160)) self.feature_rubber_band.setBrushStyle(Qt.SolidPattern) self.feature_rubber_band.setLineStyle(Qt.SolidLine) self.feature_rubber_band.setWidth(4) self.create_transforms() def name(self): return '{}_{}'.format(self.__class__.__name__, FilterType(self.type).name) def clone(self): return SwissLocatorFilter(self.type, crs=self.crs) def priority(self): return self.settings.value( '{type}_priority'.format(type=self.type.value)) def displayName(self): if self.type is FilterType.Location: return self.tr('Swiss Geoportal locations') elif self.type is FilterType.WMS: return self.tr('Swiss Geoportal / opendata.swiss WMS layers') elif self.type is FilterType.Feature: return self.tr('Swiss Geoportal features') else: raise NameError('Filter type is not valid.') def prefix(self): if self.type is FilterType.Location: return 'chl' elif self.type is FilterType.WMS: return 'chw' elif self.type is FilterType.Feature: return 'chf' else: raise NameError('Filter type is not valid.') def clearPreviousResults(self): self.rubber_band.reset(QgsWkbTypes.PointGeometry) self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry) if self.map_tip is not None: del self.map_tip self.map_tip = None if self.current_timer is not None: self.current_timer.stop() self.current_timer.deleteLater() self.current_timer = None def hasConfigWidget(self): return True def openConfigWidget(self, parent=None): dlg = ConfigDialog(parent) wid = dlg.findChild(QTabWidget, "tabWidget", Qt.FindDirectChildrenOnly) tab = wid.findChild(QWidget, self.type.value) wid.setCurrentWidget(tab) dlg.exec_() def create_transforms(self): # this should happen in the main thread self.crs = self.settings.value('crs') if self.crs == 'project': map_crs = self.map_canvas.mapSettings().destinationCrs() if map_crs.isValid(): self.crs = map_crs.authid().split(':')[1] if self.crs not in AVAILABLE_CRS: self.crs = '2056' assert self.crs in AVAILABLE_CRS src_crs_ch = QgsCoordinateReferenceSystem('EPSG:{}'.format(self.crs)) assert src_crs_ch.isValid() dst_crs = self.map_canvas.mapSettings().destinationCrs() self.transform_ch = QgsCoordinateTransform(src_crs_ch, dst_crs, QgsProject.instance()) src_crs_4326 = QgsCoordinateReferenceSystem('EPSG:4326') self.transform_4326 = QgsCoordinateTransform(src_crs_4326, dst_crs, QgsProject.instance()) def group_info(self, group: str) -> (str, str): groups = { 'zipcode': { 'name': self.tr('ZIP code'), 'layer': 'ch.swisstopo-vd.ortschaftenverzeichnis_plz' }, 'gg25': { 'name': self.tr('Municipal boundaries'), 'layer': 'ch.swisstopo.swissboundaries3d-gemeinde-flaeche.fill' }, 'district': { 'name': self.tr('District'), 'layer': 'ch.swisstopo.swissboundaries3d-bezirk-flaeche.fill' }, 'kantone': { 'name': self.tr('Cantons'), 'layer': 'ch.swisstopo.swissboundaries3d-kanton-flaeche.fill' }, 'gazetteer': { 'name': self.tr('Index'), 'layer': 'ch.swisstopo.swissnames3d' }, # there is also: ch.bav.haltestellen-oev ? 'address': { 'name': self.tr('Address'), 'layer': 'ch.bfs.gebaeude_wohnungs_register' }, 'parcel': { 'name': self.tr('Parcel'), 'layer': None } } if group not in groups: self.info('Could not find group {} in dictionary'.format(group)) return None, None return groups[group]['name'], groups[group]['layer'] @staticmethod def rank2priority(rank) -> float: """ Translate the rank from geoportal to the priority of the result see https://api3.geo.admin.ch/services/sdiservices.html#search :param rank: an integer from 1 to 7 :return: the priority as a float from 0 to 1, 1 being a perfect match """ return float(-rank / 7 + 1) @staticmethod def box2geometry(box: str) -> QgsRectangle: """ Creates a rectangle from a Box definition as string :param box: the box as a string :return: the rectangle """ coords = re.findall(r'\b(\d+(?:\.\d+)?)\b', box) if len(coords) != 4: raise InvalidBox('Could not parse: {}'.format(box)) return QgsRectangle(float(coords[0]), float(coords[1]), float(coords[2]), float(coords[3])) @staticmethod def url_with_param(url, params) -> str: url = QUrl(url) q = QUrlQuery(url) for key, value in params.items(): q.addQueryItem(key, value) url.setQuery(q) return url.url() def fetchResults(self, search: str, context: QgsLocatorContext, feedback: QgsFeedback): try: self.dbg_info("start Swiss locator search...") if len(search) < 2: return if len(search) < 4 and self.type is FilterType.Feature: return self.result_found = False swisstopo_base_url = 'https://api3.geo.admin.ch/rest/services/api/SearchServer' swisstopo_base_params = { 'type': self.type.value, 'searchText': str(search), 'returnGeometry': 'true', 'lang': self.lang, 'sr': self.crs, 'limit': str( self.settings.value( '{type}_limit'.format(type=self.type.value))) # bbox Must be provided if the searchText is not. # A comma separated list of 4 coordinates representing # the bounding box on which features should be filtered (SRID: 21781). } # Locations, WMS layers if self.type is not FilterType.Feature: nam = NetworkAccessManager() feedback.canceled.connect(nam.abort) search_urls = [(swisstopo_base_url, swisstopo_base_params)] if self.settings.value('layers_include_opendataswiss' ) and self.type is FilterType.WMS: search_urls.append( ('https://opendata.swiss/api/3/action/package_search?', { 'q': 'q=WMS+%C3' + str(search) })) for (swisstopo_base_url, swisstopo_base_params) in search_urls: swisstopo_base_url = self.url_with_param( swisstopo_base_url, swisstopo_base_params) self.dbg_info(swisstopo_base_url) try: (response, content) = nam.request(swisstopo_base_url, headers=self.HEADERS, blocking=True) self.handle_response(response, search, feedback) except RequestsExceptionUserAbort: pass except RequestsException as err: self.info(err) # Feature search else: # Feature search is split in several requests # otherwise URL is too long self.access_managers = {} try: layers = list(self.searchable_layers.keys()) assert len(layers) > 0 step = 30 for l in range(0, len(layers), step): last = min(l + step - 1, len(layers) - 1) swisstopo_base_params['features'] = ','.join( layers[l:last]) self.access_managers[self.url_with_param( swisstopo_base_url, swisstopo_base_params)] = None except IOError: self.info( 'Layers data file not found. Please report an issue.', Qgis.Critical) # init event loop # wait for all requests to end self.event_loop = QEventLoop() def reply_finished(response): self.handle_response(response, search, feedback) if response.url in self.access_managers: self.access_managers[response.url] = None for nam in self.access_managers.values(): if nam is not None: return self.event_loop.quit() feedback.canceled.connect(self.event_loop.quit) # init the network access managers, create the URL for swisstopo_base_url in self.access_managers: self.dbg_info(swisstopo_base_url) nam = NetworkAccessManager() self.access_managers[swisstopo_base_url] = nam nam.finished.connect(reply_finished) nam.request(swisstopo_base_url, headers=self.HEADERS, blocking=False) feedback.canceled.connect(nam.abort) # Let the requests end and catch all exceptions (and clean up requests) if len(self.access_managers) > 0: try: self.event_loop.exec_( QEventLoop.ExcludeUserInputEvents) except RequestsExceptionUserAbort: pass except RequestsException as err: self.info(str(err)) if not self.result_found: result = QgsLocatorResult() result.filter = self result.displayString = self.tr('No result found.') result.userData = NoResult().as_definition() self.resultFetched.emit(result) except Exception as e: self.info(e, Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] self.info( '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) self.info( traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) def handle_response(self, response, search: str, feedback: QgsFeedback): try: if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): self.info( "Error in main response with status code: {} from {}". format(response.status_code, response.url)) return data = json.loads(response.content.decode('utf-8')) # self.dbg_info(data) if self.is_opendata_swiss_response(data): visited_capabilities = [] for loc in data['result']['results']: display_name = loc['title'].get(self.lang, "") if not display_name: # Fallback to german display_name = loc['title']['de'] for res in loc['resources']: url = res['url'] url_components = urlparse(url) wms_url = url_components.scheme + '://' + url_components.netloc + '/' + url_components.path + '?' result = QgsLocatorResult() result.filter = self result.group = 'opendata.swiss' result.icon = QgsApplication.getThemeIcon( "/mActionAddWmsLayer.svg") if 'wms' in url.lower(): if res['media_type'] == 'WMS': result.displayString = display_name result.description = url if res['title']['de'] == 'GetMap': layers = parse_qs( url_components.query)['LAYERS'] result.userData = WMSLayerResult( layer=layers[0], title=display_name, url=wms_url).as_definition() self.result_found = True self.resultFetched.emit(result) elif 'request=getcapabilities' in url.lower( ) and url_components.netloc not in visited_capabilities: visited_capabilities.append( url_components.netloc) def parse_capabilities_result(response): capabilities = ET.fromstring( response.content) # Get xml namespace match = re.match(r'\{.*\}', capabilities.tag) namespace = match.group(0) if match else '' # Search for layers containing the search term in the name or title for layer in capabilities.findall( './/{}Layer'.format(namespace)): layername = self.find_text( layer, '{}Name'.format(namespace)) layertitle = self.find_text( layer, '{}Title'.format(namespace)) if layername and ( search in layername.lower() or search in layertitle.lower()): if not layertitle: layertitle = layername result.displayString = layertitle result.description = '{}?LAYERS={}'.format( url.replace( 'GetCapabilities', 'GetMap'), layername) result.userData = WMSLayerResult( layer=layername, title=layertitle, url=wms_url).as_definition() self.result_found = True self.resultFetched.emit(result) self.event_loop.quit() # Retrieve Capabilities xml self.event_loop = QEventLoop() nam = NetworkAccessManager() nam.finished.connect(parse_capabilities_result) nam.request(url, headers=self.HEADERS, blocking=False) feedback.canceled.connect(self.event_loop.quit) try: self.event_loop.exec_( QEventLoop.ExcludeUserInputEvents) except RequestsExceptionUserAbort: pass except RequestsException as err: self.info(err) else: for loc in data['results']: self.dbg_info("keys: {}".format(loc['attrs'].keys())) result = QgsLocatorResult() result.filter = self result.group = 'Swiss Geoportal' if loc['attrs']['origin'] == 'layer': # available keys: ['origin', 'lang', 'layer', 'staging', 'title', 'topics', 'detail', 'label', 'id'] for key, val in loc['attrs'].items(): self.dbg_info('{}: {}'.format(key, val)) result.displayString = loc['attrs']['title'] result.description = loc['attrs']['layer'] result.userData = WMSLayerResult( layer=loc['attrs']['layer'], title=loc['attrs']['title'], url='http://wms.geo.admin.ch/?VERSION%3D2.0.0' ).as_definition() result.icon = QgsApplication.getThemeIcon( "/mActionAddWmsLayer.svg") self.result_found = True self.resultFetched.emit(result) elif loc['attrs']['origin'] == 'feature': for key, val in loc['attrs'].items(): self.dbg_info('{}: {}'.format(key, val)) layer = loc['attrs']['layer'] point = QgsPointXY(loc['attrs']['lon'], loc['attrs']['lat']) if layer in self.searchable_layers: layer_display = self.searchable_layers[layer] else: self.info( self. tr('Layer {} is not in the list of searchable layers.' ' Please report issue.'.format(layer)), Qgis.Warning) layer_display = layer result.group = layer_display result.displayString = loc['attrs']['detail'] result.userData = FeatureResult( point=point, layer=layer, feature_id=loc['attrs'] ['feature_id']).as_definition() result.icon = QIcon( ":/plugins/swiss_locator/icons/swiss_locator.png") self.result_found = True self.resultFetched.emit(result) else: # locations for key, val in loc['attrs'].items(): self.dbg_info('{}: {}'.format(key, val)) group_name, group_layer = self.group_info( loc['attrs']['origin']) if 'layerBodId' in loc['attrs']: self.dbg_info("layer: {}".format( loc['attrs']['layerBodId'])) if 'featureId' in loc['attrs']: self.dbg_info("feature: {}".format( loc['attrs']['featureId'])) result.displayString = strip_tags( loc['attrs']['label']) # result.description = loc['attrs']['detail'] # if 'featureId' in loc['attrs']: # result.description = loc['attrs']['featureId'] result.group = group_name result.userData = LocationResult( point=QgsPointXY(loc['attrs']['y'], loc['attrs']['x']), bbox=self.box2geometry( loc['attrs']['geom_st_box2d']), layer=group_layer, feature_id=loc['attrs']['featureId'] if 'featureId' in loc['attrs'] else None, html_label=loc['attrs']['label']).as_definition() result.icon = QIcon( ":/plugins/swiss_locator/icons/swiss_locator.png") self.result_found = True self.resultFetched.emit(result) except Exception as e: self.info(str(e), Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] self.info( '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) self.info( traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) def triggerResult(self, result: QgsLocatorResult): # this should be run in the main thread, i.e. mapCanvas should not be None # remove any map tip self.clearPreviousResults() user_data = NoResult try: swiss_result = result_from_data(result) except SystemError: self.message_emitted.emit( self.displayName(), self. tr('QGIS Swiss Locator encountered an error. Please <b>update to QGIS 3.16.2</b> or newer.' ), Qgis.Warning, None) if type(swiss_result) == NoResult: return # WMS if type(swiss_result) == WMSLayerResult: url_with_params = 'contextualWMSLegend=0' \ '&crs=EPSG:{crs}' \ '&dpiMode=7' \ '&featureCount=10' \ '&format=image/png' \ '&layers={layer}' \ '&styles=' \ '&url={url}' \ .format(crs=self.crs, layer=swiss_result.layer, url=swiss_result.url) wms_layer = QgsRasterLayer(url_with_params, result.displayString, 'wms') label = QLabel() label.setTextFormat(Qt.RichText) label.setTextInteractionFlags(Qt.TextBrowserInteraction) label.setOpenExternalLinks(True) if 'geo.admin.ch' in swiss_result.url.lower(): label.setText( '<a href="https://map.geo.admin.ch/' '?lang={}&bgLayer=ch.swisstopo.pixelkarte-farbe&layers={}">' 'Open layer in map.geo.admin.ch</a>'.format( self.lang, swiss_result.layer)) if not wms_layer.isValid(): msg = self.tr('Cannot load WMS layer: {} ({})'.format( swiss_result.title, swiss_result.layer)) level = Qgis.Warning self.info(msg, level) else: msg = self.tr('WMS layer added to the map: {} ({})'.format( swiss_result.title, swiss_result.layer)) level = Qgis.Info QgsProject.instance().addMapLayer(wms_layer) self.message_emitted.emit(self.displayName(), msg, level, label) # Feature elif type(swiss_result) == FeatureResult: point = QgsGeometry.fromPointXY(swiss_result.point) point.transform(self.transform_4326) self.highlight(point) if self.settings.value('show_map_tip'): self.show_map_tip(swiss_result.layer, swiss_result.feature_id, point) # Location else: point = QgsGeometry.fromPointXY(swiss_result.point) if swiss_result.bbox.isNull(): bbox = None else: bbox = QgsGeometry.fromRect(swiss_result.bbox) bbox.transform(self.transform_ch) layer = swiss_result.layer feature_id = swiss_result.feature_id if not point: return point.transform(self.transform_ch) self.highlight(point, bbox) if layer and feature_id: self.fetch_feature(layer, feature_id) if self.settings.value('show_map_tip'): self.show_map_tip(layer, feature_id, point) else: self.current_timer = QTimer() self.current_timer.timeout.connect(self.clearPreviousResults) self.current_timer.setSingleShot(True) self.current_timer.start(5000) def highlight(self, point, bbox=None): if bbox is None: bbox = point self.rubber_band.reset(QgsWkbTypes.PointGeometry) self.rubber_band.addGeometry(point, None) rect = bbox.boundingBox() rect.scale(1.1) self.map_canvas.setExtent(rect) self.map_canvas.refresh() def fetch_feature(self, layer, feature_id): # Try to get more info self.nam_fetch_feature = NetworkAccessManager() url_detail = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}' \ .format(layer=layer, feature_id=feature_id) params = {'lang': self.lang, 'sr': self.crs} url_detail = self.url_with_param(url_detail, params) self.dbg_info(url_detail) self.nam_fetch_feature.finished.connect(self.parse_feature_response) self.nam_fetch_feature.request(url_detail, headers=self.HEADERS, blocking=False) def parse_feature_response(self, response): if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): self.info( "Error in feature response with status code: {} from {}". format(response.status_code, response.url)) return data = json.loads(response.content.decode('utf-8')) self.dbg_info(data) if 'feature' not in data or 'geometry' not in data['feature']: return if 'rings' in data['feature']['geometry']: rings = data['feature']['geometry']['rings'] self.dbg_info(rings) for r in range(0, len(rings)): for p in range(0, len(rings[r])): rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1]) geometry = QgsGeometry.fromPolygonXY(rings) geometry.transform(self.transform_ch) self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.feature_rubber_band.addGeometry(geometry, None) def show_map_tip(self, layer, feature_id, point): if layer and feature_id: url_html = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}/htmlPopup' \ .format(layer=layer, feature_id=feature_id) params = {'lang': self.lang, 'sr': self.crs} url_html = self.url_with_param(url_html, params) self.dbg_info(url_html) self.nam_map_tip = NetworkAccessManager() self.nam_map_tip.finished.connect( lambda response: self.parse_map_tip_response(response, point)) self.nam_map_tip.request(url_html, headers=self.HEADERS, blocking=False) def parse_map_tip_response(self, response, point): if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): self.info( "Error in map tip response with status code: {} from {}". format(response.status_code, response.url)) return self.dbg_info(response.content.decode('utf-8')) self.map_tip = MapTip(self.iface, response.content.decode('utf-8'), point.asPoint()) self.map_tip.closed.connect(self.clearPreviousResults) def info(self, msg="", level=Qgis.Info): self.logMessage(str(msg), level) def dbg_info(self, msg=""): if DEBUG: self.info(msg) @staticmethod def break_camelcase(identifier): matches = re.finditer( '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', identifier) return ' '.join([m.group(0) for m in matches]) def is_opendata_swiss_response(self, json): return 'opendata.swiss' in json.get("help", []) def find_text(self, xmlElement, match): node = xmlElement.find(match) return node.text if node is not None else ''
class DistanceMapTool(QgsMapTool): def __init__(self, iface): self.iface = iface self.mapCanvas = iface.mapCanvas() self.settings = MySettings() QgsMapTool.__init__(self, self.mapCanvas) def activate(self): QgsMapTool.activate(self) self.rubber = QgsRubberBand(self.mapCanvas, QGis.Point) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIcon(self.settings.value("rubberIcon")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.updateSnapperList() self.mapCanvas.layersChanged.connect(self.updateSnapperList) self.mapCanvas.scaleChanged.connect(self.updateSnapperList) self.messageWidget = self.iface.messageBar().createMessage("Intersect It", "Not snapped.") self.messageWidgetExist = True self.messageWidget.destroyed.connect(self.messageWidgetRemoved) if self.settings.value("obsDistanceSnapping") != "no": self.iface.messageBar().pushWidget(self.messageWidget) def updateSnapperList(self, dummy=None): self.snapperList = [] tolerance = self.settings.value("selectTolerance") units = self.settings.value("selectUnits") scale = self.iface.mapCanvas().mapRenderer().scale() for layer in self.iface.mapCanvas().layers(): if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType(): if not layer.hasScaleBasedVisibility() or layer.minimumScale() < scale <= layer.maximumScale(): snapLayer = QgsSnapper.SnapLayer() snapLayer.mLayer = layer snapLayer.mSnapTo = QgsSnapper.SnapToVertex snapLayer.mTolerance = tolerance if units == "map": snapLayer.mUnitType = QgsTolerance.MapUnits else: snapLayer.mUnitType = QgsTolerance.Pixels self.snapperList.append(snapLayer) def deactivate(self): self.iface.messageBar().popWidget(self.messageWidget) self.rubber.reset() self.mapCanvas.layersChanged.disconnect(self.updateSnapperList) self.mapCanvas.scaleChanged.disconnect(self.updateSnapperList) QgsMapTool.deactivate(self) def messageWidgetRemoved(self): self.messageWidgetExist = False def displaySnapInfo(self, snappingResults): if not self.messageWidgetExist: return nSnappingResults = len(snappingResults) if nSnappingResults == 0: message = "No snap" else: message = "Snapped to: <b>%s" % snappingResults[0].layer.name() + "</b>" if nSnappingResults > 1: layers = [] message += " Nearby: " for res in snappingResults[1:]: layerName = res.layer.name() if layerName not in layers: message += res.layer.name() + ", " layers.append(layerName) message = message[:-2] if self.messageWidgetExist: self.messageWidget.setText(message) def canvasMoveEvent(self, mouseEvent): snappedPoint = self.snapToLayers(mouseEvent.pos()) if snappedPoint is None: self.rubber.reset() else: self.rubber.setToGeometry(QgsGeometry().fromPoint(snappedPoint), None) def canvasPressEvent(self, mouseEvent): if mouseEvent.button() != Qt.LeftButton: return pixPoint = mouseEvent.pos() mapPoint = self.toMapCoordinates(pixPoint) #snap to layers mapPoint = self.snapToLayers(pixPoint, mapPoint) self.rubber.setToGeometry(QgsGeometry().fromPoint(mapPoint), None) distance = Distance(self.iface, mapPoint, 1) dlg = DistanceDialog(distance, self.mapCanvas) if dlg.exec_(): distance.save() self.rubber.reset() def snapToLayers(self, pixPoint, initPoint=None): self.snapping = self.settings.value("obsDistanceSnapping") if self.snapping == "no": return initPoint if self.snapping == "project": ok, snappingResults = QgsMapCanvasSnapper(self.mapCanvas).snapToBackgroundLayers(pixPoint, []) self.displaySnapInfo(snappingResults) if ok == 0 and len(snappingResults) > 0: return QgsPoint(snappingResults[0].snappedVertex) else: return initPoint if self.snapping == "all": if len(self.snapperList) == 0: return initPoint snapper = QgsSnapper(self.mapCanvas.mapRenderer()) snapper.setSnapLayers(self.snapperList) snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances) ok, snappingResults = snapper.snapPoint(pixPoint, []) self.displaySnapInfo(snappingResults) if ok == 0 and len(snappingResults) > 0: return QgsPoint(snappingResults[0].snappedVertex) else: return initPoint
class MoveTool(QgsMapToolAdvancedDigitizing): """ Map tool class to move or copy an object """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__findVertex = False self.__onMove = False self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def deactivate(self): """ When the action is deselected """ self.__cancel() QgsMapToolAdvancedDigitizing.deactivate(self) def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def __cancel(self): """ To cancel used variables """ if self.__rubberBand is not None: self.canvas().scene().removeItem(self.__rubberBand) self.__rubberBand.reset() self.__rubberBand = None if self.__rubberSnap is not None: self.canvas().scene().removeItem(self.__rubberSnap) self.__rubberSnap.reset() self.__rubberSnap = None self.__isEditing = False self.__findVertex = False self.__onMove = False self.__lastFeatureId = None self.__selectedFeature = None self.__confDlg = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.__removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(), self.__iface) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in range(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry(QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry(QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.curveToLine()), None) @staticmethod def __newCurve(curved, line_v2, dx, dy): """ To create a new moved line :param curved: if the line is curved :param line_v2: the original line :param dx: x translation :param dy: y translation :return: the new line """ if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in range(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2(self.__selectedFeature.geometry(), self.__iface) vertex = polygon_v2.vertexAt(GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in range(polygon_v2.numInteriorRings()): line_v2 = self.__newCurve(curved[num+1], polygon_v2.interiorRing(num), dx, dy) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __onConfirmCancel(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__confDlg.accept() self.__cancel() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute(field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \ QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__confDlg.accept() self.__cancel() def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and not self.__onMove: laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), [laySettings]) if f_l is not None and self.__lastFeatureId != f_l[0].id(): self.__lastFeatureId = f_l[0].id() self.__layer.setSelectedFeatures([f_l[0].id()]) if f_l is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: if self.__rubberBand is not None: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry(QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(map_point) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(map_point) else: self.__pointPreview(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(8) if self.__rubberSnap is not None: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(map_point, self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) else: self.__rubberSnap.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections(point, self.canvas(), self) if intersection is not None: self.__rubberSnap.setIcon(1) point = intersection else: self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Select vertex for moving (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.__findVertex = True self.setMode(self.CaptureLine) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) else: self.setMode(self.CaptureNone) self.__onMove = True elif self.__findVertex: self.__findVertex = False self.setMode(self.CaptureNone) closest = self.__selectedFeature.geometry().closestVertex(event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = True elif self.__onMove: self.__onMove = False mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections(mapPoint, self.canvas(), self) if intersection is not None: mapPoint = intersection self.__isEditing = True if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel) self.__confDlg.show()
class RubberBandResultRenderer(): def __init__(self): self.iface = iface self.srs_wgs84 = QgsCoordinateReferenceSystem(4326) self.transformation = QgsCoordinateTransform(self.srs_wgs84, self.srs_wgs84) self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor(QColor('magenta')) self.rb.setIconSize(12) self.features_rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.features_rb.setColor(QColor('green')) self.features_rb.setIconSize(12) self.features_rb.setWidth(3) def show_point(self, point, center=False): #check srs if self.need_transform(): point = self.transform_point(point) self.rb.addPoint(point) if center: self.center_to_point(point) def clear(self): self.rb.reset(QGis.Point) def need_transform(self): return self.iface.mapCanvas().mapRenderer().destinationCrs().postgisSrid() != 4326 def transform_point(self, point): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transform(point) except: print 'Error on transform!' # DEBUG! need message??? return def transform_bbox(self, bbox): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transformBoundingBox(bbox) except: print 'Error on transform!' # DEBUG! need message??? return def transform_geom(self, geom): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: geom.transform(self.transformation) return geom except: print 'Error on transform!' # DEBUG! need message??? return def center_to_point(self, point): canvas = self.iface.mapCanvas() new_extent = QgsRectangle(canvas.extent()) new_extent.scale(1, point) canvas.setExtent(new_extent) canvas.refresh() def zoom_to_bbox(self, bbox): if self.need_transform(): bbox = self.transform_bbox(bbox) self.iface.mapCanvas().setExtent(bbox) self.iface.mapCanvas().refresh() def show_feature(self, geom): if self.need_transform(): geom = self.transform_geom(geom) self.features_rb.setToGeometry(geom, None) def clear_feature(self): self.features_rb.reset(QGis.Point)
class GeorefRasterBy2PointsMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.firstPoint = None self.rubberBandOrigin = QgsRubberBand( self.canvas, QgsWkbTypes.PointGeometry) self.rubberBandOrigin.setColor(Qt.red) self.rubberBandOrigin.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubberBandOrigin.setIconSize(7) self.rubberBandOrigin.setWidth(2) self.rubberBandDisplacement = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.rubberBandExtent = QgsRubberBand( self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(2) self.isLayerVisible = True self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = self.firstPoint = None self.isEmittingPoint = False self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def deactivate(self): QgsMapToolEmitPoint.deactivate(self) self.reset() def canvasPressEvent(self, e): if self.firstPoint is None: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.originalCenter = self.layer.center # this tool do the displacement itself TODO update so it is done by # transformed coordinates + new center) self.originalCornerPoints = \ self.layer.transformedCornerCoordinates( *self.layer.transformParameters()) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) self.showDisplacement(self.startPoint, self.endPoint) self.layer.history.append({"action": "2pointsA", "center": self.layer.center}) else: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.startY = e.pos().y() self.endY = self.startY self.isEmittingPoint = True self.height = self.canvas.height() self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) self.layer.history.append( {"action": "2pointsB", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale, "rotation": self.layer.rotation}) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() if self.firstPoint is None: x = (self.originalCenter.x() + self.endPoint.x() - self.startPoint.x()) y = (self.originalCenter.y() + self.endPoint.y() - self.startPoint.y()) self.layer.setCenter(QgsPointXY(x, y)) self.firstPoint = self.endPoint setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() else: rotation = self.computeRotation() xScale = yScale = self.computeScale() self.layer.moveCenterFromPointRotate( self.firstPoint, rotation, xScale, yScale) self.layer.setRotation(self.layer.rotation + rotation) self.layer.setScale(self.layer.xScale * xScale, self.layer.yScale * yScale) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rasterShadow.reset() self.firstPoint = None self.startPoint = self.endPoint = None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) if self.firstPoint is None: self.showDisplacement(self.startPoint, self.endPoint) else: self.endY = e.pos().y() rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) def computeRotation(self): # The angle is the difference between angle # horizontal/endPoint-firstPoint and horizontal/startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.degrees(math.atan2(-dY, dX) - math.atan2(-dY0, dX0)) def computeScale(self): # The scale is the ratio between endPoint-firstPoint and # startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.sqrt((dX * dX + dY * dY) / (dX0 * dX0 + dY0 * dY0)) def showRotationScale(self, rotation, xScale, yScale): center, _, _, _ = self.layer.transformParameters() # newRotation = rotation + originalRotation cornerPoints = self.layer.transformedCornerCoordinatesFromPoint( self.firstPoint, rotation, xScale, yScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() # Calculate the displacement of the center due to the rotation from # another point. newCenterDX = (cornerPoints[0].x() + cornerPoints[2].x()) / 2 - center.x() newCenterDY = (cornerPoints[0].y() + cornerPoints[2].y()) / 2 - center.y() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(newCenterDX, newCenterDY, False) self.rasterShadow.setDeltaScale(xScale, yScale, False) self.rasterShadow.setDeltaRotation(rotation, True) self.rasterShadow.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point0 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point1 = QgsPointXY(self.firstPoint.x(), self.firstPoint.y()) point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) self.rubberBandDisplacement.addPoint(point0, False) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint( point2, True) # true to update canvas self.rubberBandDisplacement.show() def showDisplacement(self, startPoint, endPoint): self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandOrigin.addPoint(endPoint, True) self.rubberBandOrigin.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(endPoint.x(), endPoint.y()) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint( point2, True) # true to update canvas self.rubberBandDisplacement.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in self.originalCornerPoints: self._addDisplacementToPoint(self.rubberBandExtent, point, False) # for closing self._addDisplacementToPoint( self.rubberBandExtent, self.originalCornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(self.endPoint.x( ) - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True) self.rasterShadow.show() def _addDisplacementToPoint(self, rubberBand, point, doUpdate): x = point.x() + self.endPoint.x() - self.startPoint.x() y = point.y() + self.endPoint.y() - self.startPoint.y() self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
class nominatim_dlg(QDockWidget, Ui_search): def getHttp(self, uri, params): QgsApplication.setOverrideCursor(Qt.WaitCursor) try: rq = QUrl(uri) q = QUrlQuery() for (k, v) in params.items(): q.addQueryItem(k, v) rq.setQuery(q) req = QNetworkRequest(rq) try: reply = self.nominatim_networkAccessManager.blockingGet(req) resource = reply.content().data().decode('utf8') r = json.loads(resource) if (isinstance(r, list)): self.populateTable(r) else: self.populateTable([r]) except: self.tableResult.clearContents() finally: QgsApplication.restoreOverrideCursor() def searchJson(self, params, user, options, options2): contents = str(options).strip() items = contents.split(' ') for (k, v) in options2.items(): if k in ['viewbox']: params["bounded"] = "1" params[k] = v pairs = [] for item in items: pair = item.split('=', 1) if (pair != [''] and pair != [] and len(pair) > 1): pairs.append(pair) for (k, v) in pairs: if k in ['viewbox', 'countrycodes', 'limit', 'exclude_place_ids', 'addressdetails', 'exclude_place_ids', 'bounded', 'routewidth', 'osm_type', 'osm_id'] and not(k in options2.keys()): params[k] = v if k in ['viewbox']: params["bounded"] = "1" params["polygon_text"] = "1" params["format"] = "json" uri = 'https://nominatim.openstreetmap.org/search' self.getHttp(uri, params) def findNearbyJSON(self, params, user, options): uri = "https://nominatim.openstreetmap.org/reverse" params["format"] = "json" self.getHttp(uri, params) """ Gestion de l'évènement "leave", afin d'effacer l'objet sélectionné en sortie du dock """ def eventFilter(self, obj, event): typ = event.type() if typ == event.Leave: try: self.plugin.canvas.scene().removeItem(self.rubber) except: pass return False def __init__(self, parent, plugin): self.plugin = plugin QDockWidget.__init__(self, parent) self.setupUi(self) self.btnApply.setIcon(QIcon(":plugins/nominatim/arrow_green.png")) self.btnMask.setIcon(QIcon(":plugins/nominatim/add_mask.png")) self.btnLayer.setIcon(QIcon(":plugins/nominatim/add_layer.png")) self.tableResult.installEventFilter(self) # cf. eventFilter method self.tableResult.cellDoubleClicked.connect(self.onChoose) self.tableResult.cellEntered.connect(self.cellEntered) self.editSearch.returnPressed.connect(self.onReturnPressed) self.btnSearch.clicked.connect(self.onReturnPressed) self.btnApply.clicked.connect(self.onApply) self.btnHelp.clicked.connect(self.plugin.do_help) self.btnLocalize.clicked.connect(self.doLocalize) self.btnMask.clicked.connect(self.onMask) self.btnLayer.clicked.connect(self.onLayer) self.MultiPolygonLayerId = None self.LineLayerId = None self.PointLayerId = None try: self.cbExtent.setChecked(self.plugin.limitSearchToExtent) except: self.cbExtent.setChecked(self.plugin.limitSearchToExtent) self.currentExtent = self.plugin.canvas.extent() self.tableResult.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) try: self.editSearch.setText(self.plugin.lastSearch) except: pass try: if self.plugin.localiseOnStartup: self.doLocalize() except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, 'Extensions') pass self.nominatim_networkAccessManager = QgsNetworkAccessManager.instance() def cellEntered(self, row, col): item = self.tableResult.item(row, 0) try: self.plugin.canvas.scene().removeItem(self.rubber) self.showItem(item) except: pass def onLayer(self): for r in self.tableResult.selectedRanges(): item = self.tableResult.item(r.topRow(), 0) self.doLayer(item) def onMask(self): for r in self.tableResult.selectedRanges(): item = self.tableResult.item(r.topRow(), 0) self.doMask(item) def populateRow(self, item, idx): id = item['place_id'] name = item['display_name'] try: className = QApplication.translate("nominatim", item['class'], None) except: className = "" try: typeName = QApplication.translate("nominatim", item['type'], None) except: typeName = "" try: wkt = item['geotext'] except: wkt = None try: osm_type = item['osm_type'] except: osm_type = None bbox = {} if osm_type == "node": lat = item['lat'] lng = item['lon'] poFD = ogr.FeatureDefn("Point") poFD.SetGeomType(ogr.wkbPoint) oFLD = ogr.FieldDefn('id', ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn('name', ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) wkt = "POINT({} {})".format(lng, lat) ogrGeom = ogr.CreateGeometryFromWkt(wkt) else: try: bbox = item['boundingbox'] poFD = ogr.FeatureDefn("Rectangle") poFD.SetGeomType(ogr.wkbPolygon) oFLD = ogr.FieldDefn('id', ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn('name', ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) if wkt is None: wkt = "POLYGON(({b[2]} {b[0]}, {b[2]} {b[1]}, {b[3]} {b[1]}, {b[3]} {b[0]}, {b[2]} {b[0]}))".format(b=bbox) ogrGeom = ogr.CreateGeometryFromWkt(wkt) except: lat = item['lat'] lng = item['lon'] poFD = ogr.FeatureDefn("Point") poFD.SetGeomType(ogr.wkbPoint) oFLD = ogr.FieldDefn('id', ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn('name', ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) wkt = "POINT({} {})".format(lng, lat) ogrGeom = ogr.CreateGeometryFromWkt(wkt) ogrFeature.SetGeometry(ogrGeom) ogrFeature.SetFID(int(idx + 1)) ogrFeature.SetField(str('id'), str(id)) ogrFeature.SetField(str('name'), name) item = QTableWidgetItem(name) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) item.setData(Qt.UserRole, ogrFeature) self.tableResult.setItem(idx, 0, item) itemLibelle = QTableWidgetItem(className) itemLibelle.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableResult.setItem(idx, 1, itemLibelle) itemType = QTableWidgetItem(typeName) itemType.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableResult.setItem(idx, 2, itemType) def populateTable(self, r): idx = 0 self.tableResult.clearContents() self.tableResult.setRowCount(len(r)) for item in r: self.populateRow(item, idx) idx = idx + 1 def doLocalize(self): try: # center bbox = self.plugin.canvas.extent() sourceCrs = self.plugin.canvas.mapSettings().destinationCrs() targetCrs = QgsCoordinateReferenceSystem(4326) xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance()) bbox = xform.transform(bbox) params = {"lon": str(bbox.center().x()), "lat": str(bbox.center().y()), "zoom": "10"} self.findNearbyJSON(params, self.plugin.gnUsername, self.plugin.gnOptions) except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, 'Extensions') pass def onReturnPressed(self): try: txt = self.editSearch.text().strip() self.plugin.lastSearch = self.editSearch.text() self.plugin.limitSearchToExtent = (self.cbExtent.isChecked()) options = self.plugin.gnOptions options2 = {} if self.plugin.limitSearchToExtent: sourceCrs = self.plugin.canvas.mapSettings().destinationCrs() targetCrs = QgsCoordinateReferenceSystem() targetCrs.createFromSrid(4326) xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance()) geom = xform.transform(self.plugin.canvas.extent()) vb = "{},{},{},{}".format(geom.xMinimum(), geom.yMaximum(), geom.xMaximum(), geom.yMinimum()) options2 = {'viewbox': vb} params = {'q': txt, 'addressdetails': '0'} self.searchJson(params, self.plugin.gnUsername, options, options2) except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, 'Extensions') pass def onChoose(self, row, col): item = self.tableResult.item(row, 0) self.go(item) def onApply(self): for item in self.tableResult.selectedItems(): self.go(item) break def transform(self, geom): sourceSRS = QgsCoordinateReferenceSystem(4326) mapCrs = self.plugin.canvas.mapSettings().destinationCrs() trsf = QgsCoordinateTransform(sourceSRS, mapCrs, QgsProject.instance()) try: geom.transform(trsf) except TypeError: QgsMessageLog.logMessage("Nominatim - transformation error. Check map projection.", "Extensions") def getBBox(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint): mapextent = self.plugin.canvas.extent() ww = mapextent.width() / 100 mapcrs = self.plugin.canvas.mapSettings().destinationCrs() x = geom.boundingBox().center().x() y = geom.boundingBox().center().y() ww = 50.0 if mapcrs.mapUnits() == QgsUnitTypes.DistanceFeet: ww = 150 if mapcrs.mapUnits() == QgsUnitTypes.DistanceDegrees: ww = 0.0005 bbox = QgsRectangle(x - 10 * ww, y - 10 * ww, x + 10 * ww, y + 10 * ww) return bbox else: bbox = geom.boundingBox() rubberRect = QgsRectangle(bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()) return rubberRect def showItem(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint): self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PointGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(2) self.rubber.setToGeometry(geom, None) else: # dont show if it is larger than the canvas if self.plugin.canvas.extent().contains(geom.boundingBox()): pass else: geom = geom.intersection(QgsGeometry.fromRect(self.plugin.canvas.extent())) self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PolygonGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setWidth(4) self.rubber.setToGeometry(geom, None) def go(self, item, zoom=True): try: self.plugin.canvas.scene().removeItem(self.rubber) except: pass if zoom: bbox = self.getBBox(item) self.plugin.canvas.setExtent(bbox) self.plugin.canvas.refresh() self.showItem(item) def doMask(self, item): mapcrs = self.plugin.canvas.mapSettings().destinationCrs() ogrFeature = item.data(Qt.UserRole) layerName = "OSM "+ogrFeature.GetFieldAsString('id') geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) if (geom.type() == QgsWkbTypes.PolygonGeometry): try: try: from mask import aeag_mask except: from mask_plugin import aeag_mask aeag_mask.do(mapcrs, {geom}, "Mask "+layerName) except: geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) toCrs = self.plugin.canvas.mapSettings().destinationCrs() larg = max(geom.boundingBox().width(), geom.boundingBox().height()) x = geom.boundingBox().center().x() y = geom.boundingBox().center().y() rect = QgsRectangle(x-larg, y-larg, x+larg, y+larg) # geom.boundingBox() rect.scale(4) mask = QgsGeometry.fromRect(rect) mask = mask.difference(geom) maskLayer = QgsVectorLayer("MultiPolygon", "Mask "+layerName, "memory") maskLayer.setCrs(toCrs) QgsProject.instance().addMapLayer(maskLayer) pr = maskLayer.dataProvider() fields = QgsFields() fields.append(QgsField("id", QVariant.String)) fields.append(QgsField("name", QVariant.String)) fet = QgsFeature() fet.initAttributes(2) fet.setGeometry(mask) fet.setFields(fields) fet.setAttribute("id", (ogrFeature.GetFieldAsString('id'))) fet.setAttribute("name", (ogrFeature.GetFieldAsString('name'))) pr.addAttributes(fields.toList()) maskLayer.startEditing() pr.addFeatures([fet]) maskLayer.commitChanges() maskLayer.updateExtents() # transparence, epaisseur renderer = maskLayer.renderer() s = renderer.symbol() s.setOpacity(0.90) s.setColor(QColor(255, 255, 255)) if isinstance(s, QgsLineSymbol): s.setWidth(0) layerTree = QgsProject.instance().layerTreeRoot().findLayer(maskLayer) if layerTree: self.plugin.iface.layerTreeView().layerTreeModel()\ .refreshLayerLegend(layerTree) # Refresh legend self.go(item) def doLayer(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) fields = QgsFields() fields.append(QgsField("id", QVariant.String)) fields.append(QgsField("name", QVariant.String)) fet = QgsFeature() fet.initAttributes(2) fet.setFields(fields) fet.setGeometry(geom) fet.setAttribute("id", (ogrFeature.GetFieldAsString('id'))) fet.setAttribute("name", (ogrFeature.GetFieldAsString('name'))) vl = None if not self.plugin.singleLayer: if geom.type() == QgsWkbTypes.PolygonGeometry: layerName = "OSMPlaceSearch Polygon" layerId = self.MultiPolygonLayerId if geom.type() == QgsWkbTypes.LineGeometry: layerName = "OSMPlaceSearch Line" layerId = self.LineLayerId if geom.type() == QgsWkbTypes.PointGeometry: layerName = "OSMPlaceSearch Point" layerId = self.PointLayerId vl = QgsProject.instance().mapLayer(layerId) if vl is not None: pr = vl.dataProvider() else: if geom.type() == QgsWkbTypes.PolygonGeometry: vl = QgsVectorLayer("MultiPolygon", layerName, "memory") self.MultiPolygonLayerId = vl.id() if geom.type() == QgsWkbTypes.LineGeometry: vl = QgsVectorLayer("MultiLineString", layerName, "memory") self.LineLayerId = vl.id() if geom.type() == QgsWkbTypes.PointGeometry: vl = QgsVectorLayer("Point", layerName, "memory") self.PointLayerId = vl.id() if vl is not None: pr = vl.dataProvider() # ajout de champs pr.addAttributes(fields.toList()) QgsProject.instance().addMapLayer(vl) else: layerName = "OSM "+ogrFeature.GetFieldAsString('id') # creer une nouvelle couche si n'existe pas encore if geom.type() == QgsWkbTypes.PolygonGeometry: vl = QgsVectorLayer("MultiPolygon", layerName, "memory") if geom.type() == QgsWkbTypes.LineGeometry: vl = QgsVectorLayer("MultiLineString", layerName, "memory") if geom.type() == QgsWkbTypes.PointGeometry: vl = QgsVectorLayer("Point", layerName, "memory") if vl is not None: pr = vl.dataProvider() # ajout de champs pr.addAttributes(fields.toList()) vl.setCrs(self.plugin.canvas.mapSettings().destinationCrs()) QgsProject.instance().addMapLayer(vl) if vl is not None: vl.setProviderEncoding('UTF-8') vl.startEditing() pr.addFeatures([fet]) vl.commitChanges() # mise a jour etendue de la couche vl.updateExtents() layerTree = QgsProject.instance().layerTreeRoot().findLayer(vl) if layerTree: self.plugin.iface.layerTreeView()\ .layerTreeModel().refreshLayerLegend(layerTree) # Refresh legend self.go(item, False)
class ProfileTool(QgsMapTool): """ Tool class for making a line elevation profile """ ALT_TOLERANCE = 0.0005 SEARCH_TOLERANCE = 0.001 def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/profile_icon.png' self.text = QCoreApplication.translate("VDLTools", "Profile of a line") self.__lineLayer = None self.setCursor(Qt.ArrowCursor) self.__isChoosed = False self.__lastFeatureId = None self.__lastFeature = None self.__dockWdg = None self.__layDlg = None self.__msgDlg = None self.__confDlg = None self.__zeroDlg = None self.__points = None self.__layers = None self.__features = None self.__inSelection = False self.__selectedIds = None self.__selectedStarts = None self.__selectedDirections = None self.__startVertex = None self.__endVertex = None self.__rubberSit = None self.__rubberDif = None self.ownSettings = None self.__usedMnts = None self.__isfloating = False self.__dockGeom = None def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__dockWdg = ProfileDockWidget(self.__iface, self.__dockGeom) if self.__isfloating: self.__dockWdg.show() else: self.__iface.addDockWidget(Qt.BottomDockWidgetArea, self.__dockWdg) self.__dockWdg.closeSignal.connect(self.__closed) self.__rubberSit = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberDif = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubberSit.setColor(color) self.__rubberSit.setIcon(4) self.__rubberSit.setIconSize(20) self.__rubberDif.setColor(color) self.__rubberDif.setIcon(2) self.__rubberDif.setIconSize(20) def __closed(self): """ When the dock is closed """ self.__dockGeom = self.__dockWdg.geometry() self.__isfloating = self.__dockWdg.isFloating() self.__cancel() self.__iface.actionPan().trigger() def deactivate(self): """ When the action is deselected """ self.canvas().scene().removeItem(self.__rubberDif) self.__rubberDif = None self.canvas().scene().removeItem(self.__rubberSit) self.__rubberSit = None if self.__dockWdg is not None: self.__dockWdg.close() QgsMapTool.deactivate(self) def __cancel(self): """ To cancel used variables """ if self.__lineLayer is not None: self.__lineLayer.removeSelection() self.__lastFeatureId = None self.__lastFeature = None self.__selectedIds = None self.__selectedDirections = None self.__startVertex = None self.__endVertex = None self.__inSelection = False self.__layDlg = None self.__msgDlg = None self.__confDlg = None self.__zeroDlg = None self.__isChoosed = False def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer and QGis.fromOldWkbType(layer.wkbType()) == \ QgsWKBTypes.LineStringZ: self.__lineLayer = layer self.action().setEnabled(True) return self.action().setEnabled(False) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() if self.__dockWdg is not None: self.__dockWdg.close() self.__lineLayer = None def __setLayerDialog(self): """ To create a Profile Layers Dialog """ otherLayers = self.__lineVertices(True) with_mnt = True if self.ownSettings is None or self.ownSettings.mntUrl is None \ or self.ownSettings.mntUrl == "": with_mnt = False if not with_mnt and len(otherLayers) == 0: self.__layers = [] self.__layOk() else: self.__layDlg = ProfileLayersDialog(otherLayers, with_mnt) self.__layDlg.rejected.connect(self.__cancel) self.__layDlg.okButton().clicked.connect(self.__onLayOk) self.__layDlg.cancelButton().clicked.connect(self.__onLayCancel) self.__layDlg.show() def __setMessageDialog(self, situations, differences, names): """ To create a Profile Message Dialog :param situations: elevation differences between line and points :param differences: elevation differences between lines :param names: layers names """ self.__msgDlg = ProfileMessageDialog(situations, differences, names, self.__points) self.__msgDlg.rejected.connect(self.__checkZeros) self.__msgDlg.passButton().clicked.connect(self.__onMsgPass) self.__msgDlg.onLineButton().clicked.connect(self.__onMsgLine) self.__msgDlg.onPointsButton().clicked.connect(self.__onMsgPoints) def __setConfirmDialog(self, origin): """ To create a Profile Confirm Dialog :param origin: '0' if we copy points elevations to line, '1' if we copy line elevation to points """ self.__confDlg = ProfileConfirmDialog() if origin == 0 and not self.__lineLayer.isEditable(): self.__confDlg.setMessage( QCoreApplication.translate("VDLTools", "Do you really want to edit the LineString layer ?")) self.__confDlg.rejected.connect(self.__checkZeros) self.__confDlg.okButton().clicked.connect(self.__onConfirmLine) self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel) self.__confDlg.show() elif origin != 0: situations = self.__msgDlg.getSituations() case = True for s in situations: layer = self.__layers[s['layer'] - 1] if not layer.isEditable(): case = False break if not case: self.__confDlg.setMessage( QCoreApplication.translate("VDLTools", "Do you really want to edit the Point layer(s) ?")) self.__confDlg.rejected.connect(self.__checkZeros) self.__confDlg.okButton().clicked.connect(self.__onConfirmPoints) self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel) self.__confDlg.show() else: self.__confirmPoints() else: self.__confirmLine() def __getOtherLayers(self): """ To get all points layers that can be used :return: layers list """ layerList = [] types = [QgsWKBTypes.PointZ, QgsWKBTypes.LineStringZ, QgsWKBTypes.CircularStringZ, QgsWKBTypes.CompoundCurveZ, QgsWKBTypes.CurvePolygonZ, QgsWKBTypes.PolygonZ] for layer in self.canvas().layers(): if layer.type() == QgsMapLayer.VectorLayer and QGis.fromOldWkbType(layer.wkbType()) in types: layerList.append(layer) return layerList def __onMsgPass(self): """ When the Pass button in Profile Message Dialog is pushed """ self.__msgDlg.reject() def __onConfirmCancel(self): """ When the Cancel button in Profile Confirm Dialog is pushed """ self.__confDlg.reject() def __onMsgLine(self): """ When the Line button in Profile Message Dialog is pushed """ self.__setConfirmDialog(0) def __onMsgPoints(self): """ When the Points button in Profile Message Dialog is pushed """ self.__setConfirmDialog(1) def __onConfirmLine(self): """ When the Line button in Profile Confirm Dialog is pushed """ self.__confDlg.accept() self.__confirmLine() def __checkZeros(self): """ To check if there are zeros in selected objects """ alts = [] nb_not_none = [] for i in range(len(self.__points)): zz = self.__points[i]['z'] alt = 0 nb = 0 for z in zz: if z is not None: nb += 1 if z > alt: alt = z alts.append(alt) nb_not_none.append(nb) zeros = [] for i in range(len(self.__points)): if alts[i] == 0: if i == 0: ap = None app = None j = 1 while True: if i+j > len(self.__points)-1: break if alts[i+j] != 0: ap = j j += 1 while True: if i+j > len(self.__points)-1: break if alts[i+j] != 0: app = j break j += 1 break j += 1 if ap is None or app is None: zeros.append([i, None, None]) else: big_d = Finder.sqrDistForCoords(self.__points[ap]['x'], self.__points[app]['x'], self.__points[ap]['y'], self.__points[app]['y']) small_d = Finder.sqrDistForCoords(self.__points[i]['x'], self.__points[ap]['x'], self.__points[i]['y'], self.__points[ap]['y']) if small_d < (old_div(big_d, 4)): zextra = alts[app] + (1 + old_div(small_d, big_d)) * (alts[ap] - alts[app]) zeros.append([i, zextra, nb_not_none[i]]) else: zeros.append([i, None, None]) elif i == len(self.__points)-1: av = None avv = None j = 1 while True: if i-j < 0: break if alts[i-j] != 0: av = j j += 1 while True: if i-j < 0: break if alts[i-j] != 0: avv = j break j += 1 break j += 1 if av is None or avv is None: zeros.append([i, None, None]) else: big_d = Finder.sqrDistForCoords(self.__points[i-av]['x'], self.__points[i-avv]['x'], self.__points[i-av]['y'], self.__points[i-avv]['y']) small_d = Finder.sqrDistForCoords(self.__points[i]['x'], self.__points[i-av]['x'], self.__points[i]['y'], self.__points[i-av]['y']) if small_d < (old_div(big_d, 4)): zextra = alts[i-avv] + (1 + old_div(small_d, big_d)) * (alts[i-av] - alts[i-avv]) zeros.append([i, zextra, nb_not_none[i]]) else: zeros.append([i, None, None]) else: av = None j = 1 while True: if i-j < 0: break if alts[i-j] != 0: av = j break j += 1 ap = None j = 1 while True: if i+j > len(self.__points)-1: break if alts[i+j] != 0: ap = j break j += 1 if av is None or ap is None: zeros.append([i, None, None]) else: d0 = Finder.sqrDistForCoords( self.__points[i-av]['x'], self.__points[i]['x'], self.__points[i-av]['y'], self.__points[i]['y']) d1 = Finder.sqrDistForCoords( self.__points[i+ap]['x'], self.__points[i]['x'], self.__points[i+ap]['y'], self.__points[i]['y']) zinter = old_div((d0*alts[i+ap] + d1*alts[i-av]), (d0 + d1)) zeros.append([i, zinter, nb_not_none[i]]) if len(zeros) > 0: self.__zeroDlg = ProfileZerosDialog(zeros) self.__zeroDlg.rejected.connect(self.__cancel) self.__zeroDlg.passButton().clicked.connect(self.__onZeroPass) self.__zeroDlg.applyButton().clicked.connect(self.__onZeroApply) self.__zeroDlg.show() else: self.__cancel() def __onZeroPass(self): """ When the Pass button in Profile Zeros Dialog is pushed """ self.__zeroDlg.reject() def __onZeroApply(self): """ When the Apply button in Profile Zeros Dialog is pushed """ self.__zeroDlg.accept() zeros = self.__zeroDlg.getZeros() num_lines = len(self.__selectedIds) lines = [] for iden in self.__selectedIds: for f in self.__lineLayer.selectedFeatures(): if f.id() == iden: line, curved = GeometryV2.asLineV2(f.geometry(), self.__iface) lines.append(line) break for z in zeros: for i in range(num_lines): if self.__points[z[0]]['z'][i] is not None: index = z[0]-self.__selectedStarts[i] if not self.__selectedDirections[i]: index = lines[i].numPoints()-1-index lines[i].setZAt(index, z[1]) if z[2] > 1: zz = self.__points[z[0]]['z'] for p in range(len(zz)-num_lines): if zz[num_lines+p] is not None: feat = self.__features[z[0]][p] layer = self.__layers[p] self.__changePoint(layer, z[0], feat, z[1]) if not self.__lineLayer.isEditable(): self.__lineLayer.startEditing() for i in range(len(lines)): geom = QgsGeometry(lines[i].clone()) self.__lineLayer.changeGeometry(self.__selectedIds[i], geom) # self.__lineLayer.updateExtents() self.__dockWdg.clearData() self.__lineVertices() self.__createProfile() self.__cancel() def __confirmLine(self): """ To change the elevations of some vertices of the line """ situations = self.__msgDlg.getSituations() num_lines = len(self.__selectedIds) points = {} for s in situations: if s['point'] not in points: points[s['point']] = self.__points[s['point']]['z'][s['layer']+num_lines-1] else: diff = abs(self.__points[s['point']]['z'][s['layer']+num_lines-1] - points[s['point']]) if diff > 0.001: QMessageBox.information( None, QCoreApplication.translate("VDLTools", "Elevation"), QCoreApplication.translate("VDLTools", "There is more than one elevation for the point ") + str(s['point']) ) return self.__msgDlg.accept() lines = [] for iden in self.__selectedIds: for f in self.__lineLayer.selectedFeatures(): if f.id() == iden: line, curved = GeometryV2.asLineV2(f.geometry(), self.__iface) lines.append(line) break for s in situations: z = self.__points[s['point']]['z'][s['layer']+num_lines-1] for i in range(num_lines): if self.__points[s['point']]['z'][i] is not None: index = s['point']-self.__selectedStarts[i] if not self.__selectedDirections[i]: index = lines[i].numPoints()-1-index lines[i].setZAt(index, z) if not self.__lineLayer.isEditable(): self.__lineLayer.startEditing() for i in range(len(lines)): geom = QgsGeometry(lines[i].clone()) self.__lineLayer.changeGeometry(self.__selectedIds[i], geom) self.__dockWdg.clearData() self.__lineVertices() self.__createProfile() self.__checkZeros() def __onConfirmPoints(self): """ When the Points button in Profile Confirm Dialog is pushed """ self.__confDlg.accept() self.__confirmPoints() def __confirmPoints(self): """ To change the elevations of certain points """ self.__msgDlg.accept() situations = self.__msgDlg.getSituations() num_lines = len(self.__selectedIds) for s in situations: layer = self.__layers[s['layer']-1] feat = self.__features[s['point']][s['layer']-1] newZ = 0 for i in range(num_lines): if self.__points[s['point']]['z'][i] is not None: newZ = self.__points[s['point']]['z'][i] break self.__changePoint(layer, s['point'], feat, newZ) self.__dockWdg.clearData() self.__lineVertices() self.__createProfile() self.__checkZeros() def __changePoint(self, layer, pos, feat, newZ): """ To change Vertex elevation :param layer: layer containing the object :param pos: vertex position in the object (if not a point) :param feat: QgsFeature of the object :param newZ: new elevation """ if layer.geometryType() == QGis.Polygon: closest = feat.geometry().closestVertex( QgsPoint(self.__points[pos]['x'], self.__points[pos]['y'])) feat_v2, curved = GeometryV2.asPolygonV2(feat.geometry(), self.__iface) position = GeometryV2.polygonVertexId(feat_v2, closest[1]) vertex = feat_v2.vertexAt(position) feat_v2.deleteVertex(position) vertex.setZ(newZ) feat_v2.insertVertex(position, vertex) elif layer.geometryType() == QGis.Line: closest = feat.geometry().closestVertex( QgsPoint(self.__points[pos]['x'], self.__points[pos]['y'])) feat_v2, curved = GeometryV2.asLineV2(feat.geometry(), self.__iface) feat_v2.setZAt(closest[1], newZ) else: feat_v2 = GeometryV2.asPointV2(feat.geometry(), self.__iface) feat_v2.setZ(newZ) if not layer.isEditable(): layer.startEditing() layer.changeGeometry(feat.id(), QgsGeometry(feat_v2)) def __onLayCancel(self): """ When the Cancel button in Profile Layers Dialog is pushed """ self.__layDlg.reject() self.__isChoosed = False def __lineVertices(self, checkLayers=False): """ To check if vertices of others layers are crossing the displaying line :param checkLayers: if we want to get the list of the other layers in return :return: other layers list if requested """ if checkLayers: availableLayers = self.__getOtherLayers() otherLayers = [] self.__points = [] self.__selectedStarts = [] num = 0 num_lines = len(self.__selectedIds) for iden in self.__selectedIds: self.__selectedStarts.append(max(0, len(self.__points)-1)) direction = self.__selectedDirections[num] selected = None for f in self.__lineLayer.selectedFeatures(): if f.id() == iden: selected = f break if selected is None: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error on selected"), level=QgsMessageBar.CRITICAL, duration=0 ) continue line_v2, curved = GeometryV2.asLineV2(selected.geometry(), self.__iface) if direction: rg = range(line_v2.numPoints()) else: rg = range(line_v2.numPoints()-1, -1, -1) rg_positions = [] for i in rg: pt_v2 = line_v2.pointN(i) x = pt_v2.x() y = pt_v2.y() doublon = False for position in rg_positions: if position['x'] == x and position['y'] == y and position['iden'] == iden: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Beware! the line ") + str(iden) + QCoreApplication.translate("VDLTools", " has 2 identical summits on the vertex ") + str(i-1) + QCoreApplication.translate("VDLTools", " same coordinates (X and Y). " "Please correct the line geometry."), level=QgsMessageBar.CRITICAL, duration=0 ) doublon = True break for item in self.__points: if item['x'] == x and item['y'] == y: item['z'][num] = pt_v2.z() rg_positions.append({'x': x, 'y': y, 'iden': iden}) doublon = True break if not doublon: rg_positions.append({'x': x, 'y': y, 'iden': iden}) z = [] for j in range(num_lines): if j == num: if pt_v2.z() == pt_v2.z(): z.append(pt_v2.z()) else: z.append(0) else: z.append(None) self.__points.append({'x': x, 'y': y, 'z': z}) if checkLayers: for layer in availableLayers: if layer in otherLayers: continue laySettings = QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.Vertex, self.SEARCH_TOLERANCE, QgsTolerance.LayerUnits) f_l = Finder.findClosestFeatureAt(self.toMapCoordinates(layer, QgsPoint(x, y)), self.canvas(), [laySettings]) if f_l is not None: if layer == self.__lineLayer: other = False if f_l[0].id() not in self.__selectedIds: other = True else: fs = Finder.findFeaturesAt(QgsPoint(x, y), laySettings, self) for f in fs: if f.id() not in self.__selectedIds: vertex = f.geometry().closestVertex(QgsPoint(x, y)) if vertex[4] < self.SEARCH_TOLERANCE: other = True break if other and layer not in otherLayers: otherLayers.append(layer) elif layer not in otherLayers: otherLayers.append(layer) num += 1 if checkLayers: return otherLayers def __onLayOk(self): """ When the Ok button in Profile Layers Dialog is pushed """ self.__layDlg.accept() self.__layers = self.__layDlg.getLayers() self.__usedMnts = self.__layDlg.getUsedMnts() self.__layOk() def __layOk(self): """ To create the profile """ self.__createProfile() self.__checkSituations() self.__isChoosed = False def __createProfile(self): """ Create the profile in the dock """ self.__features = [] for points in self.__points: feat = [] x = points['x'] y = points['y'] z = points['z'] for layer in self.__layers: laySettings = QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.Vertex, self.SEARCH_TOLERANCE, QgsTolerance.LayerUnits) f_l = Finder.findClosestFeatureAt(self.toMapCoordinates(layer, QgsPoint(x, y)), self.canvas(), [laySettings]) if f_l is None: feat.append(None) z.append(None) else: if f_l[1].geometryType() == QGis.Polygon: closest = f_l[0].geometry().closestVertex(QgsPoint(x, y)) polygon_v2, curved = GeometryV2.asPolygonV2(f_l[0].geometry(), self.__iface) zp = polygon_v2.vertexAt(GeometryV2.polygonVertexId(polygon_v2, closest[1])).z() feat.append(f_l[0]) if zp is None or zp != zp: z.append(0) else: z.append(zp) elif f_l[1].geometryType() == QGis.Line: f_ok = None if layer == self.__lineLayer: if f_l[0].id() not in self.__selectedIds: f_ok = f_l[0] else: fs = Finder.findFeaturesAt(QgsPoint(x, y), laySettings, self) for f in fs: if f.id() not in self.__selectedIds: vertex = f.geometry().closestVertex(QgsPoint(x, y)) if vertex[4] < self.SEARCH_TOLERANCE: f_ok = f break else: f_ok = f_l[0] if f_ok is not None: closest = f_ok.geometry().closestVertex(QgsPoint(x, y)) feat.append(f_ok) line, curved = GeometryV2.asLineV2(f_ok.geometry(), self.__iface) zp = line.zAt(closest[1]) if zp is None or zp != zp: z.append(0) else: z.append(zp) else: feat.append(None) z.append(None) else: zp = GeometryV2.asPointV2(f_l[0].geometry(), self.__iface).z() feat.append(f_l[0]) if zp is None or zp != zp: z.append(0) else: z.append(zp) self.__features.append(feat) self.__calculateProfile() def __getNames(self): """ To get the names from connected lines layers :return: the names list """ names = [self.__lineLayer.name()] for layer in self.__layers: if layer.name() == self.__lineLayer.name(): names.append(layer.name() + " conn.") else: names.append(layer.name()) return names @staticmethod def __contains(line, point): """ To check if a position is a line vertex :param line: the line :param point: the position :return: the vertex id in the line, or -1 """ pos = 0 if point is None: return -1 for pt in line: if pt.x() == point.x() and pt.y() == point.y(): return pos pos += 1 return -1 def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if not self.__isChoosed: if self.__lineLayer is not None: laySettings = QgsSnappingUtils.LayerConfig(self.__lineLayer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(event.mapPoint(), self.canvas(), [laySettings]) if not self.__inSelection: if f_l is not None and self.__lastFeatureId != f_l[0].id(): self.__lastFeature = f_l[0] self.__lastFeatureId = f_l[0].id() self.__lineLayer.setSelectedFeatures([f_l[0].id()]) if f_l is None: self.__cancel() else: if f_l is not None and (self.__selectedIds is None or f_l[0].id() not in self.__selectedIds): line = f_l[0].geometry().asPolyline() if self.__contains(line, self.__endVertex) > -1: self.__lastFeature = f_l[0] self.__lastFeatureId = f_l[0].id() features = self.__selectedIds + [f_l[0].id()] self.__lineLayer.setSelectedFeatures(features) elif self.__contains(line, self.__startVertex) > -1: self.__lastFeature = f_l[0] self.__lastFeatureId = f_l[0].id() features = self.__selectedIds + [f_l[0].id()] self.__lineLayer.setSelectedFeatures(features) else: self.__lineLayer.setSelectedFeatures(self.__selectedIds) self.__lastFeatureId = None self.__lastFeature = None if f_l is None: if self.__selectedIds is not None: self.__lineLayer.setSelectedFeatures(self.__selectedIds) self.__lastFeatureId = None self.__lastFeature = None def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ self.__rubberSit.reset() self.__rubberDif.reset() if event.button() == Qt.RightButton: if self.__lineLayer.selectedFeatures() is not None and self.__selectedIds is not None: self.__isChoosed = True self.__setLayerDialog() elif event.button() == Qt.LeftButton: if self.__lastFeature is not None and \ (self.__selectedIds is None or self.__lastFeature.id() not in self.__selectedIds): self.__inSelection = True line = self.__lastFeature.geometry().asPolyline() self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Select more lines with click left or process " "with click right (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) if self.__selectedIds is None: self.__selectedIds = [] self.__startVertex = line[0] self.__endVertex = line[-1] self.__selectedDirections = [] self.__selectedDirections.append(True) # direction du premier prime self.__selectedIds.append(self.__lastFeatureId) else: pos = self.__contains(line, self.__startVertex) if pos > -1: self.__selectedIds = [self.__lastFeatureId] + self.__selectedIds if pos == 0: direction = False self.__startVertex = line[-1] else: direction = True self.__startVertex = line[0] self.__selectedDirections = [direction] + self.__selectedDirections else: pos = self.__contains(line, self.__endVertex) self.__selectedIds.append(self.__lastFeatureId) if pos == 0: direction = True self.__endVertex = line[-1] else: direction = False self.__endVertex = line[0] self.__selectedDirections.append(direction) self.__lineLayer.setSelectedFeatures(self.__selectedIds) def __calculateProfile(self): """ To calculate the profile and display it """ if self.__points is None: return self.__dockWdg.clearData() if len(self.__points) == 0: return self.__dockWdg.setProfiles(self.__points, len(self.__selectedIds)) self.__dockWdg.attachCurves(self.__getNames(), self.ownSettings, self.__usedMnts) def __checkSituations(self): """ To check if point with no elevation on line, and one or more elevation from other layers, and if there are different elevations at the same point """ situations = [] differences = [] for p in range(len(self.__points)): pt = self.__points[p] num_lines = len(self.__selectedIds) zz = [] for i in range(num_lines): if pt['z'][i] is not None: zz.append(i) if len(zz) == 0: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No line z ?!?"), level=QgsMessageBar.WARNING) elif len(zz) == 1: z0 = pt['z'][zz[0]] for i in range(num_lines, len(pt['z'])): if pt['z'][i] is None: continue if abs(pt['z'][i]-z0) > self.ALT_TOLERANCE: situations.append({'point': p, 'layer': (i-num_lines+1), 'vertex': z0}) elif len(zz) == 2: z0 = pt['z'][zz[0]] if abs(pt['z'][zz[1]] - z0) > self.ALT_TOLERANCE: differences.append({'point': p, 'v1': z0, 'v2': pt['z'][zz[1]]}) else: for i in range(num_lines, len(pt['z'])): if pt['z'][i] is None: continue if abs(pt['z'][i]-z0) > self.ALT_TOLERANCE: situations.append({'point': p, 'layer': (i-num_lines+1), 'vertex': z0}) else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "More than 2 lines z ?!?"), level=QgsMessageBar.WARNING) if (len(situations) > 0) or (len(differences) > 0): self.__setMessageDialog(situations, differences, self.__getNames()) self.__rubberSit.reset() self.__rubberDif.reset() for situation in situations: pt = self.__points[situation['point']] point = QgsPoint(pt['x'], pt['y']) if self.__rubberSit.numberOfVertices() == 0: self.__rubberSit.setToGeometry(QgsGeometry().fromPoint(point), None) else: self.__rubberSit.addPoint(point) for difference in differences: pt = self.__points[difference['point']] point = QgsPoint(pt['x'], pt['y']) if self.__rubberDif.numberOfVertices() == 0: self.__rubberDif.setToGeometry(QgsGeometry().fromPoint(point), None) else: self.__rubberDif.addPoint(point) self.__msgDlg.show() else: self.__checkZeros()
class FinderBox(QComboBox): running = False toFinish = 0 searchStarted = pyqtSignal() searchFinished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.resultView = QTreeView() self.resultView.setHeaderHidden(True) self.resultView.setMinimumHeight(300) self.resultView.activated.connect(self.itemActivated) self.resultView.pressed.connect(self.itemPressed) self.setView(self.resultView) self.resultModel = ResultModel(self) self.setModel(self.resultModel) self.finders = finders for finder in self.finders.values(): finder.resultFound.connect(self.resultFound) finder.limitReached.connect(self.limitReached) finder.finished.connect(self.finished) finder.message.connect(self.message) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def search(self): if self.running: return toFind = self.lineEdit().text() if not toFind or toFind == '': return self.running = True self.searchStarted.emit() self.resultModel.clearResults() self.resultModel.truncateHistory(MySettings().value("historyLength")) self.resultModel.setLoading(True) self.showPopup() QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) # create categories in special order and count activated ones for finder in self.finders.values(): if finder.activated(): self.resultModel.addResult(finder.name) bbox = self.mapCanvas.fullExtent() for finder in self.finders.values(): if finder.activated(): finder.start(toFind, bbox=bbox) def stop(self): for finder in self.finders.values(): if finder.isRunning(): finder.stop() def resultFound(self, finder, layername, value, geometry, srid): self.resultModel.addResult(finder.name, layername, value, geometry, srid) self.resultView.expandAll() def limitReached(self, finder, layername): self.resultModel.addEllipsys(finder.name, layername) def finished(self, finder): for finder in self.finders.values(): if finder.isRunning(): return self.running = False self.searchFinished.emit() self.resultModel.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def message(self, finder, message, level): self.iface.messageBar().pushMessage("Quick Finder", message, level, 3) def itemActivated(self, index): item = self.resultModel.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.resultModel.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.resultModel.setSelected(item, self.resultView.palette()) geometry = self.transformGeom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoomToRubberBand() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.resultModel.setSelected(item, self.resultView.palette()) self.rubber.reset(child.geometry.type()) for i in xrange(0, item.rowCount()): geometry = self.transformGeom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoomToRubberBand() return if item.__class__.__name__ == 'QStandardItem': self.resultModel.setSelected(None, self.resultView.palette()) self.rubber.reset() return def transformGeom(self, item): geometry = item.geometry src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapRenderer().destinationCrs() geom = item.geometry geom.transform( QgsCoordinateTransform(src_crs, dest_crs) ) return geom def zoomToRubberBand(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class IntersectTool(QgsMapTool): """ Map tool class to create temporary circle, with center point """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/intersect_icon.png' self.text = QCoreApplication.translate("VDLTools", "From intersection") self.setCursor(Qt.ArrowCursor) self.__lineLayerID = None self.__pointLayerID = None self.__rubber = None self.ownSettings = None self.__isEditing = False self.__distance = 0 def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__rubber = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setIconSize(20) self.__rubber.setWidth(2) self.__distance = 6.0 def deactivate(self): """ When the action is deselected """ self.__cancel() self.__rubber = None QgsMapTool.deactivate(self) def __cancel(self): """ To cancel used variables """ if self.__rubber is not None: self.__rubber.reset() self.__isEditing = False def __setDistanceDialog(self, mapPoint): """ To create an Intersect Distance Dialog :param mapPoint: radius of the circle """ self.__dstDlg = IntersectDistanceDialog(mapPoint) self.__dstDlg.rejected.connect(self.__cancel) self.__dstDlg.okButton().clicked.connect(self.__onDstOk) self.__dstDlg.cancelButton().clicked.connect(self.__onDstCancel) self.__dstDlg.observation().setValue(self.__distance) self.__dstDlg.observation().selectAll() self.__dstDlg.show() def __onDstOk(self): """ When the Ok button in Intersect Distance Dialog is pushed """ self.__distance = float(self.__dstDlg.observation().text()) circle = QgsCircularStringV2() x = self.__dstDlg.mapPoint().x() y = self.__dstDlg.mapPoint().y() circle.setPoints([QgsPointV2(x + self.__distance * cos(pi / 180 * a), y + self.__distance * sin(pi / 180 * a)) for a in range(0, 361, 90)]) lineLayer = self.__lineLayer() lineLayer.startEditing() feature = QgsFeature() feature.setGeometry(QgsGeometry(circle)) fields = lineLayer.pendingFields() feature.setFields(fields) fieldsNames = [fields.at(pos).name() for pos in range(fields.count())] if "distance" in fieldsNames: feature.setAttribute("distance", self.__distance) if "x" in fieldsNames: feature.setAttribute("x", self.__dstDlg.mapPoint().x()) if "y" in fieldsNames: feature.setAttribute("y", self.__dstDlg.mapPoint().y()) lineLayer.addFeature(feature) # lineLayer.updateExtents() lineLayer.commitChanges() # center pointLayer = self.__pointLayer() pointLayer.startEditing() feature = QgsFeature() feature.setGeometry(QgsGeometry().fromPoint(self.__dstDlg.mapPoint())) fields = pointLayer.pendingFields() feature.setFields(fields) pointLayer.addFeature(feature) pointLayer.commitChanges() self.__dstDlg.accept() self.__cancel() def __onDstCancel(self): """ When the Cancel button in Intersect Distance Dialog is pushed """ self.__dstDlg.reject() def canvasMoveEvent(self, mouseEvent): """ When the mouse is moved :param mouseEvent: mouse event """ if not self.__isEditing: self.__rubber.reset() match = Finder.snap(mouseEvent.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubber.setIcon(4) else: self.__rubber.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections(point, self.canvas(), self) if intersection is not None: self.__rubber.setIcon(1) point = intersection else: self.__rubber.setIcon(3) self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None) def canvasReleaseEvent(self, mouseEvent): """ When the mouse is clicked :param mouseEvent: mouse event """ if mouseEvent.button() != Qt.LeftButton: return match = Finder.snap(mouseEvent.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections(match.point(), self.canvas(), self) if intersection is not None: point = intersection self.__isEditing = True self.__setDistanceDialog(point) def __lineLayer(self): """ To get the line layer to create the circle :return: a line layer """ if self.ownSettings is not None: if self.ownSettings.linesLayer is not None: layer = self.ownSettings.linesLayer self.__lineLayerID = layer.id() return layer layer = QgsMapLayerRegistry.instance().mapLayer(self.__lineLayerID) if layer is None: epsg = self.canvas().mapRenderer().destinationCrs().authid() layer = QgsVectorLayer("LineString?crs=%s&index=yes&field=distance:double&field=x:double&field=y:double" % epsg, "Memory Lines", "memory") QgsMapLayerRegistry.instance().addMapLayer(layer) layer.layerDeleted.connect(self.__lineLayerDeleted) self.__lineLayerID = layer.id() if self.ownSettings is not None: self.ownSettings.linesLayer = layer else: self.__iface.legendInterface().setLayerVisible(layer, True) return layer def __lineLayerDeleted(self): """ To deselect the line layer when it is deleted """ self.lineLayerID = None def __pointLayer(self): """ To get the point layer to create the center :return: a point layer """ if self.ownSettings is not None: if self.ownSettings.pointsLayer is not None: layer = self.ownSettings.pointsLayer self.__pointLayerID = layer.id() return layer layer = QgsMapLayerRegistry.instance().mapLayer(self.__pointLayerID) if layer is None: epsg = self.canvas().mapRenderer().destinationCrs().authid() layer = QgsVectorLayer("Point?crs=%s&index=yes" % epsg, "Memory Points", "memory") QgsMapLayerRegistry.instance().addMapLayer(layer) layer.layerDeleted.connect(self.__pointLayerDeleted) self.__pointLayerID = layer.id() if self.ownSettings is not None: self.ownSettings.pointsLayer = layer else: self.__iface.legendInterface().setLayerVisible(layer, True) return layer def __pointLayerDeleted(self): """ To deselect the point layer when it is deleted """ self.__pointLayerID = None
class GeomapfishLocatorFilter(QgsLocatorFilter): USER_AGENT = b'Mozilla/5.0 QGIS GeoMapFish Locator Filter' def __init__(self, iface: QgisInterface = None): super().__init__() self.rubber_band = None self.settings = Settings() self.iface = None self.map_canvas = None self.current_timer = None self.transform = None # only get map_canvas on main thread, not when cloning if iface is not None: self.iface = iface self.map_canvas = iface.mapCanvas() self.map_canvas.destinationCrsChanged.connect( self.create_transform) self.rubber_band = QgsRubberBand(self.map_canvas) self.rubber_band.setColor(QColor(255, 255, 50, 200)) self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE) self.rubber_band.setIconSize(15) self.rubber_band.setWidth(4) self.rubber_band.setBrushStyle(Qt.NoBrush) self.create_transform() def name(self) -> str: return self.__class__.__name__ def clone(self): return GeomapfishLocatorFilter() def displayName(self) -> str: name = self.settings.value("filter_name") if name != '': return name return self.tr('Geomapfish service') def prefix(self) -> str: return 'gmf' def hasConfigWidget(self) -> bool: return True def openConfigWidget(self, parent=None): ConfigDialog(parent).exec_() def create_transform(self): srv_crs_authid = self.settings.value('geomapfish_crs') src_crs = QgsCoordinateReferenceSystem(srv_crs_authid) assert src_crs.isValid() dst_crs = self.map_canvas.mapSettings().destinationCrs() self.transform = QgsCoordinateTransform(src_crs, dst_crs, QgsProject.instance()) @staticmethod def url_with_param(url, params) -> str: url = QUrl(url) q = QUrlQuery(url) for key, value in params.items(): q.addQueryItem(key, value) url.setQuery(q) return url.url() def emit_bad_configuration(self, err=None): result = QgsLocatorResult() result.filter = self result.displayString = self.tr('Locator filter is not configured.') result.description = err if err else self.tr( 'Double-click to configure.') result.userData = FilterNotConfigured result.icon = QgsApplication.getThemeIcon('mIconWarning.svg') self.resultFetched.emit(result) return @pyqtSlot() def clear_results(self): if self.rubber_band: self.rubber_band.reset(QgsWkbTypes.PointGeometry) if self.current_timer is not None: self.current_timer.timeout.disconnect(self.clear_results) self.current_timer.stop() self.current_timer.deleteLater() self.current_timer = None def fetchResults(self, search, context, feedback): try: self.dbg_info("start GMF locator search...") url = self.settings.value('geomapfish_url') if url == "": self.emit_bad_configuration() return params = { 'query': search, 'limit': str(self.settings.value('total_limit')), 'partitionlimit': str(self.settings.value('category_limit')) } if len(search) < 2: return headers = {b'User-Agent': self.USER_AGENT} if self.settings.value('geomapfish_user') != '': user = self.settings.value('geomapfish_user') password = self.settings.value('geomapfish_pass') auth_data = "{}:{}".format(user, password) b64 = QByteArray(auth_data.encode()).toBase64() headers[QByteArray('Authorization'.encode())] = QByteArray( 'Basic '.encode()) + b64 url = self.url_with_param(url, params) self.dbg_info(url) nam = NetworkAccessManager() feedback.canceled.connect(nam.abort) (response, content) = nam.request(url, headers=headers, blocking=True) self.handle_response(response, content) except RequestsExceptionUserAbort: pass except RequestsException as err: self.emit_bad_configuration(str(err)) self.info(err) except Exception as e: self.info(str(e), Qgis.Critical) #exc_type, exc_obj, exc_traceback = sys.exc_info() #filename = os.path.split(exc_traceback.tb_frame.f_code.co_filename)[1] #self.info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) #self.info(traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) finally: self.finished.emit() def handle_response(self, response, content): try: if response.status_code != 200: self.info("Error with status code: {}".format( response.status_code)) return data = json.loads(content.decode('utf-8')) #self.dbg_info(data) features = data['features'] for f in features: json_geom = json.dumps(f['geometry']) ogr_geom = ogr.CreateGeometryFromJson(json_geom) wkt = ogr_geom.ExportToWkt() geometry = QgsGeometry.fromWkt(wkt) self.dbg_info('---------') self.dbg_info( QgsWkbTypes.geometryDisplayString(geometry.type())) self.dbg_info(f.keys()) self.dbg_info('{} {}'.format(f['properties']['layer_name'], f['properties']['label'])) self.dbg_info(f['bbox']) self.dbg_info(f['geometry']) if geometry is None: continue result = QgsLocatorResult() result.filter = self result.displayString = f['properties']['label'] if Qgis.QGIS_VERSION_INT >= 30100: result.group = self.beautify_group( f['properties']['layer_name']) result.userData = geometry self.resultFetched.emit(result) except Exception as e: self.info(str(e), Qgis.Critical) #exc_type, exc_obj, exc_traceback = sys.exc_info() #filename = os.path.split(exc_traceback.tb_frame.f_code.co_filename)[1] #self.info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) # self.info(traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) def triggerResult(self, result): self.clear_results() if result.userData == FilterNotConfigured: self.openConfigWidget() if self.iface and hasattr(self.iface, 'invalidateLocatorResults'): # from QGIS 3.2 iface has invalidateLocatorResults self.iface.invalidateLocatorResults() return # this should be run in the main thread, i.e. mapCanvas should not be None geometry = result.userData geometry.transform(self.transform) self.rubber_band.reset(geometry.type()) self.rubber_band.addGeometry(geometry, None) rect = geometry.boundingBox() rect.scale(1.5) self.map_canvas.setExtent(rect) self.map_canvas.refresh() self.current_timer = QTimer() self.current_timer.timeout.connect(self.clear_results) self.current_timer.setSingleShot(True) self.current_timer.start(5000) def beautify_group(self, group) -> str: if self.settings.value("remove_leading_digits"): group = re.sub('^[0-9]+', '', group) if self.settings.value("replace_underscore"): group = group.replace("_", " ") if self.settings.value("break_camelcase"): group = self.break_camelcase(group) return group def info(self, msg="", level=Qgis.Info): QgsMessageLog.logMessage('{} {}'.format(self.__class__.__name__, msg), 'QgsLocatorFilter', level) def dbg_info(self, msg=""): if DEBUG: self.info(msg) @staticmethod def break_camelcase(identifier) -> str: matches = re.finditer( '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', identifier) return ' '.join([m.group(0) for m in matches])
class GeomapfishLocatorFilter(QgsLocatorFilter): USER_AGENT = b'Mozilla/5.0 QGIS GeoMapFish Locator Filter' changed = pyqtSignal() def __init__(self, service: Service, iface: QgisInterface = None): super().__init__() self.service = service.clone() self.rubberband = None self.iface = None self.map_canvas = None self.current_timer = None self.settings = Settings() self.crs = QgsCoordinateReferenceSystem(self.service.crs) # only get map_canvas on main thread, not when cloning if iface is not None: self.iface = iface self.map_canvas = iface.mapCanvas() self.rubberband = QgsRubberBand(self.map_canvas) self.reset_rubberband() def name(self) -> str: return slugify(self.displayName()) def clone(self): return GeomapfishLocatorFilter(self.service) def displayName(self) -> str: return self.service.name def prefix(self) -> str: return 'gmf' def hasConfigWidget(self) -> bool: return True def openConfigWidget(self, parent=None): cfg = FilterConfigurationDialog(self.service, parent) if cfg.exec_(): self.service = cfg.service.clone() self.crs = QgsCoordinateReferenceSystem(self.service.crs) self.changed.emit() def reset_rubberband(self): # this should happen on main thread only! self.rubberband.setColor(self.settings.value('point_color')) self.rubberband.setIcon(self.rubberband.ICON_CIRCLE) self.rubberband.setIconSize(self.settings.value('point_size')) self.rubberband.setWidth(self.settings.value('line_width')) self.rubberband.setBrushStyle(Qt.NoBrush) @staticmethod def url_with_param(url, params) -> str: url = QUrl(url) q = QUrlQuery(url) for key, value in params.items(): q.addQueryItem(key, value) url.setQuery(q) return url def emit_bad_configuration(self, err=None): result = QgsLocatorResult() result.filter = self result.displayString = self.tr('Locator filter is not configured.') result.description = err if err else self.tr( 'Double-click to configure it.') result.userData = FilterNotConfigured result.icon = QgsApplication.getThemeIcon('mIconWarning.svg') self.resultFetched.emit(result) return @pyqtSlot() def clear_results(self): if self.rubberband: self.rubberband.reset(QgsWkbTypes.PointGeometry) if self.current_timer is not None: self.current_timer.timeout.disconnect(self.clear_results) self.current_timer.stop() self.current_timer.deleteLater() self.current_timer = None def fetchResults(self, search, context, feedback): self.dbg_info("start GMF locator search...") url = self.service.url if not url: self.emit_bad_configuration() return if len(search) < 2: return params = { 'query': search, 'limit': str(self.service.total_limit), 'partitionlimit': str(self.service.category_limit) } url = self.url_with_param(url, params) self.dbg_info(url.url()) _request = QNetworkRequest(url) _request.setRawHeader(b'User-Agent', self.USER_AGENT) request = QgsBlockingNetworkRequest() if self.service.authid: request.setAuthCfg(self.service.authid) response = request.get(_request, False, feedback) if response == QgsBlockingNetworkRequest.NoError: self.handle_response(request.reply().content()) else: error_msg = request.reply().errorString() self.emit_bad_configuration(error_msg) self.info(error_msg, Qgis.Critical) self.finished.emit() def handle_response(self, content: QByteArray): try: data = json.loads(str(content.data(), encoding='utf-8')) # self.dbg_info(data) features = data['features'] for f in features: json_geom = json.dumps(f['geometry']) ogr_geom = ogr.CreateGeometryFromJson(json_geom) wkt = ogr_geom.ExportToWkt() geometry = QgsGeometry.fromWkt(wkt) self.dbg_info('---------') self.dbg_info( QgsWkbTypes.geometryDisplayString(geometry.type())) self.dbg_info(f.keys()) self.dbg_info('{} {}'.format(f['properties']['layer_name'], f['properties']['label'])) self.dbg_info(f['bbox']) self.dbg_info(f['geometry']) if geometry is None: continue result = QgsLocatorResult() result.filter = self result.displayString = f['properties']['label'] result.group = self.beautify_group( f['properties']['layer_name']) result.userData = geometry self.resultFetched.emit(result) except Exception as e: self.info(str(e), Qgis.Critical) # exc_type, exc_obj, exc_traceback = sys.exc_info() # #filename = os.path.split(exc_traceback.tb_frame.f_code.co_filename)[1] # #self.info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) # self.info(traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) def triggerResult(self, result): self.clear_results() if result.userData == FilterNotConfigured: self.openConfigWidget() self.iface.invalidateLocatorResults() return # this should be run in the main thread, i.e. mapCanvas should not be None geometry = result.userData # geometry.transform(self.transform) dbg_info(str(geometry.asWkt())) dbg_info(geometry.type()) try: rect = QgsReferencedRectangle(geometry.boundingBox(), self.crs) rect.scale(4) self.map_canvas.setReferencedExtent(rect) except AttributeError: # QGIS < 3.10 handling from qgis.core import QgsCoordinateTransform, QgsProject transform = QgsCoordinateTransform( self.crs, self.map_canvas.mapSettings().destinationCrs(), QgsProject.instance()) geometry.transform(transform) rect = geometry.boundingBox() rect.scale(4) self.map_canvas.setExtent(rect) self.map_canvas.refresh() if geometry.type() == QgsWkbTypes.PolygonGeometry: nflash = 16 color1: QColor = self.settings.value('polygon_color') color2 = color1 color1.setAlpha(200) color2.setAlpha(100) self.map_canvas.flashGeometries( [geometry], self.crs, color1, color2, nflash, self.settings.value('highlight_duration') / nflash * 1000) else: self.rubberband.reset(geometry.type()) self.rubberband.addGeometry(geometry, self.crs) self.current_timer = QTimer() self.current_timer.timeout.connect(self.clear_results) self.current_timer.setSingleShot(True) self.current_timer.start( self.settings.value('highlight_duration') * 1000) def beautify_group(self, group) -> str: if self.service.remove_leading_digits: group = re.sub('^[0-9]+', '', group) if self.service.replace_underscore: group = group.replace("_", " ") if self.service.break_camelcase: group = self.break_camelcase(group) return group def info(self, msg="", level=Qgis.Info): QgsMessageLog.logMessage('{} {}'.format(self.__class__.__name__, msg), 'QgsLocatorFilter', level) def dbg_info(self, msg=""): if DEBUG: self.info(msg) @staticmethod def break_camelcase(identifier) -> str: matches = re.finditer( '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', identifier) return ' '.join([m.group(0) for m in matches])
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) if hasattr(self.canvas, 'setParallelRenderingEnabled'): self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(0, 0, 212, 76)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.featureformloaded.connect(self.feature_form_loaded) self.connectButtons() def init_qgisproject(self, doc): parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() return self.canvas.mapRenderer().destinationCrs() def showEvent(self, *args, **kwargs): if self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, project, editmode): self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def highlight_active_selection(self, layer, feature, features): self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def clear_selection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position) def gps_first_fix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): self.marker.hide() def select_data_entry(self): def showformerror(form): pass def actions(): for form in self.project.forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format( form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form") formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): self.project = project self.actionPan.trigger() try: firstform = project.forms[0] self.load_form(firstform) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.freeze(False) self.canvas.refresh() def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def load_form(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [ QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon ]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name( ) == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value RoamEvents.open_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class SelectFeaturesTool(QgsMapTool): selection_clicked = pyqtSignal(list) def __init__(self, mission_track, canvas): QgsMapTool.__init__(self, canvas) self.setCursor(Qt.ArrowCursor) self.mission_track = mission_track self.layer = self.mission_track.get_mission_layer() self.rubber_band = None self.rubber_band_points = None self.selection_polygon = [] self.indexes_within_list = [] self.band_finished = True self.mCtrl = False self.p0, self.p1, self.p2, self.p3 = None, None, None, None self.mission_track.mission_changed.connect(self.update_rubber_band) self.mission_track.step_removed.connect(self.remove_rubber_band) self.wp = self.mission_track.find_waypoints_in_mission() self.layer.startEditing() self.rubber_band_vs_track_indexes = {} self.rubber_band_points = QgsRubberBand(self.canvas(), QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor("green")) self.rubber_band_vertex_counter = 0 def keyPressEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = True def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = False if event.key() == Qt.Key_Escape: self.p0, self.p1, self.p2, self.p3 = None, None, None, None if self.rubber_band: self.rubber_band.reset(True) self.close_polygon_band() self.band_finished = True self.canvas().refresh() return def canvasPressEvent(self, event): if event.button() == Qt.LeftButton: point = self.toMapCoordinates(event.pos()) # check if we have clicked on a vertex tolerance = self.calc_tolerance() vertex = self.find_vertex_at(event.pos(), tolerance) if self.mCtrl and vertex is not None: # if we have clicked on a vertex, identify which one # check if was already in the selection list if vertex not in self.indexes_within_list: # add it self.indexes_within_list.append(vertex) self.update_rubber_band() else: # remove it self.indexes_within_list.remove(vertex) self.update_rubber_band() self.band_finished = True elif vertex is None: # if we have not clicked on a vertex and there's no polygon band, start it if not len(self.selection_polygon ) and self.band_finished and not self.mCtrl: self.selection_clicked.emit(list()) self.band_finished = False self.rubber_band = QgsRubberBand( self.canvas(), QgsWkbTypes.PolygonGeometry) self.rubber_band.setWidth(2) select_green = QColor("green") select_green.setAlpha(128) self.rubber_band.setColor(select_green) if event.button() == Qt.LeftButton: # Left click -> add vertex self.p0 = QgsPointXY(point.x(), point.y()) self.selection_polygon.append(self.p0) elif len(self.selection_polygon) == 1 and not self.mCtrl: if event.button() == Qt.LeftButton: # Left click -> add vertex self.p2 = QgsPointXY(point.x(), point.y()) self.p1 = QgsPointXY(self.p2.x(), self.p0.y()) self.p3 = QgsPointXY(self.p0.x(), self.p2.y()) self.selection_polygon.append(self.p1) self.selection_polygon.append(self.p2) self.selection_polygon.append(self.p3) self.band_finished = True self.set_selection() self.close_polygon_band() self.selection_clicked.emit(self.indexes_within_list) def find_vertex_at(self, pos, tolerance): """ get the vertex that is closer to the clicked point :param pos: The point that we've clicked :param tolerance: The tolerance of pos :return: vertex or None """ if len(self.wp) > 0: dist_to_vertex = [] for v in range(0, len(self.wp)): a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v])) dist_to_vertex.append( math.sqrt((pos.x() - a1.x())**2 + (pos.y() - a1.y())**2)) vertex = dist_to_vertex.index(min(dist_to_vertex)) if min(dist_to_vertex) > tolerance: return None else: return vertex else: return None def calc_tolerance(self): """ Compute the tolerance on canvas :return: tolerance """ # 2% of tolerance width_tolerance = 0.02 * self.canvas().width() height_tolerance = 0.02 * self.canvas().height() if width_tolerance < height_tolerance: tolerance = width_tolerance else: tolerance = height_tolerance return tolerance def canvasMoveEvent(self, event): if not self.band_finished and not self.mCtrl: self.p2 = self.toMapCoordinates(event.pos()) self.p1 = QgsPointXY(self.p2.x(), self.p0.y()) self.p3 = QgsPointXY(self.p0.x(), self.p2.y()) self.selection_polygon.append(self.p1) self.selection_polygon.append(self.p2) self.selection_polygon.append(self.p3) self.rubber_band.setToGeometry( QgsGeometry.fromPolygonXY([self.selection_polygon]), None) self.selection_polygon.pop() self.selection_polygon.pop() self.selection_polygon.pop() def set_selection(self): """ Set vertices highlight according to polygon """ # Check which features are within the polygon mission_track = self.layer.getFeatures() # get mission track feature for f in mission_track: # loop although mission layer only has one feature vertices_it = f.geometry().vertices() polygon_geom = QgsGeometry.fromPolygonXY([self.selection_polygon]) vertices_within_list = [] # self.indexes_within_list = [] vertex_index = 0 # Highlight them using a point rubber band self.rubber_band_vertex_counter = 0 for v in vertices_it: point_geom = QgsGeometry.fromPointXY(QgsPointXY(v.x(), v.y())) if point_geom.within(polygon_geom): vertices_within_list.append(v) if not (vertex_index in self.indexes_within_list ): # only add if not already present self.indexes_within_list.append(vertex_index) self.rubber_band_points.addPoint(QgsPointXY(v.x(), v.y())) self.rubber_band_vertex_counter = self.rubber_band_vertex_counter + 1 self.rubber_band_vs_track_indexes[ vertex_index] = self.rubber_band_vertex_counter - 1 vertex_index = vertex_index + 1 def update_rubber_band(self): if self.rubber_band_points: self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.rubber_band_vs_track_indexes = {} self.rubber_band_vertex_counter = 0 self.wp = self.mission_track.find_waypoints_in_mission() if len(self.indexes_within_list) > 0: selected_vertices = self.mission_track.find_waypoints_in_mission( self.indexes_within_list) for v in selected_vertices: vertex_index = 0 for point in self.wp: if v == point: pc = self.toLayerCoordinates(self.layer, QgsPointXY(v)) self.rubber_band_points.addPoint(pc) self.rubber_band_vertex_counter = self.rubber_band_vertex_counter + 1 self.rubber_band_vs_track_indexes[ vertex_index] = self.rubber_band_vertex_counter - 1 vertex_index = vertex_index + 1 self.set_geometry() def remove_rubber_band(self, wp): # if self.rubber_band_points: # self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.indexes_within_list.remove(wp) self.update_rubber_band() def set_geometry(self): """ Save rubber band to geometry of the layer """ if self.layer.featureCount() == 0: # no feature yet created f = QgsFeature() if len(self.wp) == 1: f.setGeometry( QgsGeometry.fromPointXY( QgsPointXY(self.wp[0].x(), self.wp[0].y()))) else: f.setGeometry(QgsGeometry.fromPolyline(self.wp)) # self.layer.dataProvider().addFeatures([f]) self.layer.addFeatures([f]) else: # mission feature present, edit geometry feats = self.layer.getFeatures() for f in feats: if len(self.wp) == 1: self.layer.changeGeometry( f.id(), QgsGeometry.fromPointXY( QgsPointXY(self.wp[0].x(), self.wp[0].y()))) else: self.layer.changeGeometry( f.id(), QgsGeometry.fromPolyline(self.wp)) self.layer.commitChanges() self.layer.startEditing() def close_polygon_band(self): self.selection_polygon = [] if self.rubber_band is not None: self.rubber_band.reset() self.canvas().scene().removeItem(self.rubber_band) self.rubber_band = None def close_highlight_band(self): self.rubber_band_points.reset() self.canvas().scene().removeItem(self.rubber_band_points) self.rubber_band_points = None def deactivate(self): if self.rubber_band: self.close_polygon_band() if self.rubber_band_points: self.close_highlight_band() try: self.mission_track.mission_changed.disconnect( self.update_rubber_band) self.mission_track.step_removed.disconnect(self.remove_rubber_band) except: logger.info("no connected to signal") self.layer.commitChanges()
class MoveTool(QgsMapTool): def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.__canvas = iface.mapCanvas() self.__icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.__text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = 0 self.__findVertex = 0 self.__onMove = 0 self.__counter = 0 self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None self.__layerConfig = None def icon_path(self): """ To get the icon path :return: icon path """ return self.__icon_path def text(self): """ To get the menu text :return: menu text """ return self.__text def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__updateList() self.__canvas.layersChanged.connect(self.__updateList) QgsProject.instance().snapSettingsChanged.connect(self.__updateList) def deactivate(self): """ When the action is deselected """ self.__canvas.layersChanged.disconnect(self.__updateList) QgsProject.instance().snapSettingsChanged.disconnect(self.__updateList) QgsMapTool.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) self.__layer.editingStarted.disconnect(self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) self.__layer.editingStopped.disconnect(self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.__canvas.mapTool == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.__canvas.setMapTool(self) def removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): self.__layer.editingStopped.disconnect(self.stopEditing) else: self.__layer.editingStarted.disconnect(self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None\ and isinstance(layer, QgsVectorLayer): if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): self.__layer.editingStopped.disconnect(self.stopEditing) else: self.__layer.editingStarted.disconnect(self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__updateList() self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.__canvas.mapTool == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) self.removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry()) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry()) print(self.__selectedVertex) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in xrange(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry( QgsGeometry(self.__newFeature.curveToLine()), None) def __newCurve(self, curved, line_v2, dx, dy): if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in xrange(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2( self.__selectedFeature.geometry()) vertex = polygon_v2.vertexAt(self.__polygonVertexId(polygon_v2)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Line) line_v2 = self.__newLine(polygon_v2.exteriorRing(), dx, dy, curved[0]) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in xrange(polygon_v2.numInteriorRings()): line_v2 = self.__newLine(polygon_v2.interiorRing(num), dx, dy, curved[num + 1]) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __polygonVertexId(self, polygon_v2): """ To get the id of the selected vertex from a polygon :param polygon_v2: the polygon as polygonV2 :return: id as QgsVertexId """ eR = polygon_v2.exteriorRing() if self.__selectedVertex < eR.numPoints(): return QgsVertexId(0, 0, self.__selectedVertex, 1) else: sel = self.__selectedVertex - eR.numPoints() for num in xrange(polygon_v2.numInteriorRings()): iR = polygon_v2.interiorRing(num) if sel < iR.numPoints(): return QgsVertexId(0, num + 1, sel, 1) sel -= iR.numPoints() def __newLine(self, curve_v2, dx, dy, curved): """ To create a new moved line for a part of a polygon :param curve_v2: the original line :param dx: x translation :param dy: y translation :return: the line as lineV2 """ if curved: new_line_v2 = QgsCircularStringV2() else: new_line_v2 = QgsLineStringV2() points = [] for pos in xrange(curve_v2.numPoints()): x = curve_v2.pointN(pos).x() - dx y = curve_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(curve_v2.pointN(pos).z()) points.append(pt) new_line_v2.setPoints(points) return new_line_v2 def __onConfirmClose(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.close() self.__rubberBand.reset() self.__rubberSnap.reset() self.__isEditing = 0 self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__layer.updateExtents() self.__onConfirmClose() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute( field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and \ self.__layer.editFormConfig().suppress() != QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__layer.updateExtents() self.__onConfirmClose() def __updateList(self): """ To update the snapping options of the layer """ noUse, enabled, snappingType, unitType, tolerance, avoidIntersection = \ QgsProject.instance().snapSettingsForLayer(self.__layer.id()) self.__layerConfig = QgsSnappingUtils.LayerConfig( self.__layer, QgsPointLocator.Vertex, tolerance, unitType) if not enabled or tolerance == 0: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate( "VDLTools", "This layer has no snapping options"), level=QgsMessageBar.CRITICAL) def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: f = Finder.findClosestFeatureAt(event.mapPoint(), self.__layerConfig, self) if f is not None and self.__lastFeatureId != f.id(): self.__lastFeatureId = f.id() self.__layer.setSelectedFeatures([f.id()]) if f is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex( event.mapPoint()) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry( QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(event.mapPoint()) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(event.mapPoint()) else: self.__pointPreview(event.mapPoint()) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) if self.__rubberSnap: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.__canvas, QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(event.mapPoint(), self.__canvas, True) if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) self.__rubberSnap.setToGeometry( QgsGeometry().fromPoint(match.point()), None) else: self.__rubberSnap.setIcon(1) self.__rubberSnap.setToGeometry( QgsGeometry().fromPoint(match.point()), None) if match.hasEdge(): self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry( QgsGeometry().fromPoint(match.point()), None) # if self.__counter > 2: # if self.__rubberSnap: # self.__rubberSnap.reset() # else: # self.__rubberSnap = QgsRubberBand(self.__canvas, QGis.Point) # self.__rubberSnap.setColor(color) # self.__rubberSnap.setWidth(2) # self.__rubberSnap.setIconSize(20) # snappedIntersection = Finder.snapToIntersection(event.mapPoint(), self, self.__layerList) # if snappedIntersection is None: # snappedPoint = Finder.snapToLayers(event.mapPoint(), self.__snapperList) # if snappedPoint is not None: # self.__rubberSnap.setIcon(4) # self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(snappedPoint), None) # else: # self.__rubberSnap.setIcon(1) # self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(snappedIntersection), None) # self.__counter = 0 # else: # self.__counter += 1 def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) < 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__findVertex = 1 self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Point) else: self.__onMove = 1 # self.__snapperList, self.__layerList = Finder.updateSnapperList(self.__iface) elif self.__findVertex: self.__findVertex = 0 closest = self.__selectedFeature.geometry().closestVertex( event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = 1 # self.__snapperList, self.__layerList = Finder.updateSnapperList(self.__iface) elif self.__onMove: self.__onMove = 0 mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.__canvas, True) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() # snappedIntersection = Finder.snapToIntersection(event.mapPoint(), self, self.__layerList) # if snappedIntersection is None: # snappedPoint = Finder.snapToLayers(event.mapPoint(), self.__snapperList) # if snappedPoint is not None: # mapPoint = snappedPoint # else: # mapPoint = snappedIntersection self.__isEditing = 1 if self.__rubberBand: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect( self.__onConfirmClose) self.__confDlg.show()
class ExtrapolateTool(QgsMapTool): def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.__canvas = iface.mapCanvas() self.__icon_path = ':/plugins/VDLTools/icons/extrapolate_icon.png' self.__text = QCoreApplication.translate( "VDLTools", "Extrapolate the elevation of a vertex and a " "point at the extremity of a line") # self.__oldTool = None self.__layer = None self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__lastFeatureId = None self.__rubber = None self.__counter = 0 self.__confDlg = None self.__selectedVertex = None self.__elevation = None self.__selectedFeature = None self.__layerConfig = None def icon_path(self): """ To get the icon path :return: icon path """ return self.__icon_path def text(self): """ To get the menu text :return: menu text """ return self.__text def setTool(self): """ To set the current tool as this one """ # self.__oldTool = self.__canvas.mapTool() self.__canvas.setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__rubber = QgsRubberBand(self.__canvas, QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setIconSize(20) self.__updateList() self.__canvas.layersChanged.connect(self.__updateList) QgsProject.instance().snapSettingsChanged.connect(self.__updateList) def deactivate(self): """ When the action is deselected """ self.__rubber.reset() self.__canvas.layersChanged.disconnect(self.__updateList) QgsProject.instance().snapSettingsChanged.disconnect(self.__updateList) QgsMapTool.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) self.__layer.editingStarted.disconnect(self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) self.__layer.editingStopped.disconnect(self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.__canvas.mapTool == self: self.__iface.actionPan().trigger() # self.__canvas.setMapTool(self.__oldTool) def removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): self.__layer.editingStopped.disconnect(self.stopEditing) else: self.__layer.editingStarted.disconnect(self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None\ and isinstance(layer, QgsVectorLayer)\ and QGis.fromOldWkbType(layer.wkbType()) == QgsWKBTypes.LineStringZ: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): self.__layer.editingStopped.disconnect(self.stopEditing) else: self.__layer.editingStarted.disconnect(self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__updateList() self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.__canvas.mapTool == self: self.__iface.actionPan().trigger() # self.__canvas.setMapTool(self.__oldTool) return self.action().setEnabled(False) self.removeLayer() def __updateList(self): """ To update the snapping options of the layer """ noUse, enabled, snappingType, unitType, tolerance, avoidIntersection = \ QgsProject.instance().snapSettingsForLayer(self.__layer.id()) self.__layerConfig = QgsSnappingUtils.LayerConfig( self.__layer, QgsPointLocator.Vertex, tolerance, unitType) if not enabled or tolerance == 0: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate( "VDLTools", "This layer has no snapping options"), level=QgsMessageBar.CRITICAL) def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if not self.__isEditing: f = Finder.findClosestFeatureAt(event.mapPoint(), self.__layerConfig, self) if f is not None and self.__lastFeatureId != f.id(): self.__lastFeatureId = f.id() self.__layer.setSelectedFeatures([f.id()]) if self.__counter > 2: self.__rubber.reset() geom = f.geometry() index = geom.closestVertex(event.mapPoint())[1] line_v2, curved = GeometryV2.asLineV2(geom) num_p = line_v2.numPoints() if num_p > 2 and (index == 0 or index == (num_p - 1)): self.__rubber.setIcon(4) self.__rubber.setToGeometry( QgsGeometry(line_v2.pointN(index)), None) self.__counter = 0 else: self.__counter += 1 if f is None: self.__layer.removeSelection() self.__rubber.reset() self.__lastFeatureId = None def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) < 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return geom = found_features[0].geometry() self.__selectedVertex = geom.closestVertex(event.mapPoint())[1] line_v2, curved = GeometryV2.asLineV2(geom) num_p = line_v2.numPoints() if num_p > 2 and (self.__selectedVertex == 0 or self.__selectedVertex == (num_p - 1)): pt = line_v2.pointN(self.__selectedVertex) if self.__selectedVertex == 0: pt0 = line_v2.pointN(2) pt1 = line_v2.pointN(1) else: pt0 = line_v2.pointN(num_p - 3) pt1 = line_v2.pointN(num_p - 2) big_d = Finder.sqrDistForPoints(pt0, pt1) small_d = Finder.sqrDistForPoints(pt1, pt) if small_d < (big_d / 4): self.__isEditing = True self.__selectedFeature = found_features[0] self.__elevation = pt0.z() + (1 + small_d / big_d) * ( pt1.z() - pt0.z()) if pt.z() is not None and pt.z() != 0: self.__confDlg = ExtrapolateConfirmDialog( pt.z(), self.__elevation) self.__confDlg.okButton().clicked.connect( self.__onEditOk) self.__confDlg.cancelButton().clicked.connect( self.__onEditCancel) self.__confDlg.show() else: self.__edit() else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "The segment is too big"), level=QgsMessageBar.INFO) def __onEditOk(self): """ When the Ok button in Extrapolate Confirm Dialog is pushed """ self.__confDlg.close() self.__edit() def __onEditCancel(self): """ When the Cancel button in Extrapolate Confirm Dialog is pushed """ self.__confDlg.close() self.__rubber.reset() self.__lastFeatureId = None self.__selectedFeature = None self.__selectedVertex = None self.__isEditing = False def __edit(self): """ To add the new extrapolate elevation """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry()) line_v2.setZAt(self.__selectedVertex, self.__elevation) self.__layer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) self.__layer.removeSelection() self.__rubber.reset() self.__lastFeatureId = None self.__selectedFeature = None self.__selectedVertex = None self.__isEditing = False
class InterpolateTool(QgsMapToolAdvancedDigitizing): """ Map tool class to interpolate an elevation in the middle of a segment """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/interpolate_icon.png' self.text = QCoreApplication.translate( "VDLTools", "Interpolate the elevation of a vertex and a point in the middle of a line") self.__layer = None self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__lastFeatureId = None self.__layerList = None self.__lastLayer = None self.__confDlg = None self.__mapPoint = None self.__rubber = None self.__selectedFeature = None self.__findVertex = False def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) self.__updateList() self.__rubber = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setWidth(2) self.__rubber.setIconSize(20) self.canvas().layersChanged.connect(self.__updateList) self.canvas().scaleChanged.connect(self.__updateList) self.setMode(self.CaptureLine) def deactivate(self): """ When the action is deselected """ self.__done() self.__cancel() self.__rubber = None Signal.safelyDisconnect(self.canvas().layersChanged, self.__updateList) Signal.safelyDisconnect(self.canvas().scaleChanged, self.__updateList) QgsMapToolAdvancedDigitizing.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def __done(self): """ When the edition is finished """ self.__isEditing = False self.__confDlg = None self.__mapPoint = None def __cancel(self): """ To cancel used variables """ self.__findVertex = False if self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastLayer = None if self.__rubber is not None: self.__rubber.reset() self.__lastFeatureId = None self.__selectedFeature = None def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.action().setEnabled(False) self.__removeLayer() def __updateList(self): """ To update the line layers list that we can use for interpolation """ self.__layerList = [] for layer in self.canvas().layers(): if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType() \ and layer.geometryType() == QGis.Line: self.__layerList.append(QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.All, 10, QgsTolerance.Pixels)) def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__done() self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and self.__layerList is not None: f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), self.__layerList) if f_l is not None and self.__lastFeatureId != f_l[0].id(): f = f_l[0] self.__lastFeatureId = f.id() if self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastLayer = f_l[1] self.__lastLayer.setSelectedFeatures([f.id()]) if f_l is None and self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: self.__rubber.reset() snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All) match = Finder.snap(map_point, self.canvas(), snap_layers) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): self.__rubber.setIcon(4) self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None) else: intersection = Finder.snapCurvedIntersections(point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: if self.__isVertexUnderPoint(intersection, snap_layers): self.__rubber.setIcon(4) else: self.__rubber.setIcon(1) self.__rubber.setToGeometry(QgsGeometry().fromPoint(intersection), None) if match.hasEdge(): intersection = Finder.snapCurvedIntersections(point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: if self.__isVertexUnderPoint(intersection, snap_layers): self.__rubber.setIcon(4) else: self.__rubber.setIcon(1) self.__rubber.setToGeometry(QgsGeometry().fromPoint(intersection), None) elif self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): self.__rubber.setIcon(3) self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None) def __isVertexUnderPoint(self, point, snap_layers): """ When snapping find a point instead of line/polygon element, we need to check if there is a vertex under it :param point: coordinates :param snap_layers: layers configs :return: True if there is a vertex, False otherwise """ for config in snap_layers: if config.layer.id() == self.__lastLayer.id(): tolerance = config.tolerance if config.unit == QgsTolerance.Pixels: tolerance = Finder.calcCanvasTolerance(self.toCanvasCoordinates(point), config.layer, self, tolerance) elif config.unit == QgsTolerance.ProjectUnits: tolerance = Finder.calcMapTolerance(point, config.layer, self, tolerance) layPoint = self.toLayerCoordinates(config.layer, point) geom = self.__selectedFeature.geometry() dist = geom.closestVertex(layPoint)[4] if dist < (tolerance*tolerance): return True break return False def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if self.__lastLayer is not None and not self.__findVertex: found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Select the position for interpolation (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.setMode(self.CaptureNone) self.__findVertex = True elif self.__findVertex: self.__rubber.reset() snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All) match = Finder.snap(event.mapPoint(), self.canvas(), snap_layers) if match.hasVertex() or match.hasEdge(): point = match.point() ok = False noVertex = False if match.hasVertex(): if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): ok = True noVertex = True else: intersection = Finder.snapCurvedIntersections(point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: point = intersection ok = True if self.__isVertexUnderPoint(intersection, snap_layers): noVertex = True if match.hasEdge(): intersection = Finder.snapCurvedIntersections(point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: point = intersection ok = True if self.__isVertexUnderPoint(intersection, snap_layers): noVertex = True elif self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): ok = True if ok: self.__isEditing = True self.__findVertex = False self.__mapPoint = point if noVertex: self.__ok(False, True) else: self.__confDlg = InterpolateConfirmDialog() if self.__lastLayer.isEditable(): self.__confDlg.setMainLabel(QCoreApplication.translate("VDLTools", "What do you want to do ?")) self.__confDlg.setAllLabel(QCoreApplication.translate("VDLTools", "Create point and new vertex")) self.__confDlg.setVtLabel(QCoreApplication.translate("VDLTools", "Create only the vertex")) self.__confDlg.rejected.connect(self.__done) self.__confDlg.okButton().clicked.connect(self.__onConfirmOk) self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel) self.__confDlg.show() else: self.__done() self.__cancel() def __onConfirmCancel(self): """ When the Cancel button in Interpolate Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmOk(self): """ When the Ok button in Interpolate Confirm Dialog is pushed """ checkedId = self.__confDlg.getCheckedId() self.__confDlg.accept() withVertex = True withPoint = True if checkedId == 1: withVertex = False else: if not self.__lastLayer.isEditable(): self.__lastLayer.startEditing() if checkedId == 2: withPoint = False self.__ok(withVertex, withPoint) def __ok(self, withVertex, withPoint): """ To apply the interpolation :param withVertex: if we want a new interpolated vertex :param withPoint: if we want a new interpolated point """ line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface) vertex_v2 = QgsPointV2() vertex_id = QgsVertexId() line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2, vertex_id, 0) x0 = line_v2.xAt(vertex_id.vertex-1) y0 = line_v2.yAt(vertex_id.vertex-1) d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y()) x1 = line_v2.xAt(vertex_id.vertex) y1 = line_v2.yAt(vertex_id.vertex) d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y()) z0 = line_v2.zAt(vertex_id.vertex-1) z1 = line_v2.zAt(vertex_id.vertex) z = old_div((d0*z1 + d1*z0), (d0 + d1)) vertex_v2.addZValue(round(z, 2)) if withPoint: pt_feat = QgsFeature(self.__layer.pendingFields()) pt_feat.setGeometry(QgsGeometry(vertex_v2)) for i in range(len(self.__layer.pendingFields())): # default = self.__layer.defaultValue(i, pt_feat) # if default is not None: # print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default) # print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i)) e = QgsExpression(self.__layer.defaultValueExpression(i)) default = e.evaluate(pt_feat) pt_feat.setAttribute(i, default) if self.__layer.editFormConfig().suppress() == QgsEditFormConfig.SuppressOn: self.__layer.addFeature(pt_feat) else: self.__iface.openFeatureForm(self.__layer, pt_feat) if withVertex: line_v2.insertVertex(vertex_id, vertex_v2) self.__lastLayer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) else: self.__selectedFeature = found_features[0] else: self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "No more feature selected"), level=QgsMessageBar.INFO) self.__iface.mapCanvas().refresh() self.__done() self.__findVertex = True
class GeomEditorDialog(QDialog, Ui_GeomEditor, SettingDialog): def __init__(self, layer, feature, mapCanvas, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.settings = MySettings() SettingDialog.__init__(self, self.settings, False, True) self.mapCanvas = mapCanvas self.setAttribute(Qt.WA_DeleteOnClose) self.feature = feature self.initialGeometry = QgsGeometry(feature.geometry()) self.layer = layer # close if no geom, hide "sketch current point" if not needed geomType = layer.geometryType() if not geomType in (QGis.Point, QGis.Line, QGis.Polygon): self.close() return if geomType == QGis.Point: self.pointRubberGroup.hide() # editors management self.editorLayout = QGridLayout(self.editorContainer) self.editor = GeomEditor(layer, feature.geometry()) self.displayCombo.setCurrentIndex(0) self.displayCombo.currentIndexChanged.connect(self.setEditor) self.setEditor() # rubber bands self.featureRubber = QgsRubberBand(mapCanvas) self.currentPointRubber = QgsRubberBand(mapCanvas) self.settings.setting("featureRubberColor").valueChanged.connect(self.updateFeatureRubber) self.settings.setting("featureRubberSize").valueChanged.connect(self.updateFeatureRubber) self.settings.setting("currentPointRubberSize").valueChanged.connect(self.updateCurrentPointRubber) self.settings.setting("currentPointRubberColor").valueChanged.connect(self.updateCurrentPointRubber) self.settings.setting("currentPointRubberIcon").valueChanged.connect(self.updateCurrentPointRubber) self.updateFeatureRubber() self.updateCurrentPointRubber() self.geometryChanged() # GUI signals connection self.applyButton.clicked.connect(self.applyGeometry) self.resetButton.clicked.connect(self.resetGeometry) self.sketchGeometry.clicked.connect(self.geometryChanged) self.displayPointRubber.clicked.connect(self.currentPointRubber.reset) self.layerEditable() layer.editingStopped.connect(self.layerEditable) layer.editingStarted.connect(self.layerEditable) # set texts in UI self.layerLabel.setText(layer.name()) try: featureTitle = unicode(feature[layer.displayField()]) except KeyError: featureTitle = "" if featureTitle == "": featureTitle = str(feature.id()) self.featureEdit.setText(featureTitle) def setEditor(self): self.editorLayout.removeWidget(self.editor) geom = self.editor.getGeom() idx = self.displayCombo.currentIndex() if idx == -99999: editor = CellEditor elif idx == 0: editor = WktEditor elif idx == 1: editor = WkbEditor else: self.editor = GeomEditor return self.editor = editor(self.layer, geom, self) self.editorLayout.addWidget(self.editor, 0, 0, 1, 1) self.editor.currentPointChanged.connect(self.drawCurrentPoint) self.editor.geometryChanged.connect(self.geometryChanged) def resetGeometry(self): self.editor.setGeom(self.initialGeometry) def closeEvent(self, e): self.featureRubber.reset() self.currentPointRubber.reset() self.layer.editingStarted.disconnect(self.layerEditable) self.layer.editingStopped.disconnect(self.layerEditable) self.editor.closeEditor() QDialog.closeEvent(self, e) def layerEditable(self): layerIsEditable = self.layer.isEditable() self.resetButton.setEnabled(layerIsEditable) self.applyButton.setEnabled(layerIsEditable) def geometryChanged(self): self.featureRubber.reset() self.currentPointRubber.reset() if self.editor.isGeomValid(): self.displayCombo.setEnabled(True) self.applyButton.setEnabled(self.layer.isEditable()) geomStatus = "Geometry is valid" if self.sketchGeometry.isChecked(): self.featureRubber.setToGeometry(self.editor.getGeom(), self.layer) else: self.applyButton.setEnabled(False) self.displayCombo.setEnabled(False) geomStatus = "Geometry is not valid" self.geomStatusLabel.setText(geomStatus) @pyqtSlot(QgsGeometry) def drawCurrentPoint(self, point): if self.displayPointRubber.isChecked(): self.currentPointRubber.setToGeometry(point, None) self.mapCanvas.refresh() def applyGeometry(self): geometry = self.editor.getGeom() if geometry is not None: self.layer.changeGeometry(self.feature.id(), geometry) self.layer.updateExtents() self.layer.setCacheImage(None) self.layer.triggerRepaint() def updateFeatureRubber(self): self.featureRubber.setColor(self.settings.value("featureRubberColor")) self.featureRubber.setWidth(self.settings.value("featureRubberSize")) self.layer.triggerRepaint() def updateCurrentPointRubber(self): self.currentPointRubber.setIconSize(self.settings.value("currentPointRubberSize")) self.currentPointRubber.setColor(self.settings.value("currentPointRubberColor")) self.currentPointRubber.setIcon(self.settings.value("currentPointRubberIcon")) self.mapCanvas.refresh()
class GeorefRasterBy2PointsMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.firstPoint = None self.rubberBandOrigin = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubberBandOrigin.setColor(Qt.red) self.rubberBandOrigin.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubberBandOrigin.setIconSize(7) self.rubberBandOrigin.setWidth(2) self.rubberBandDisplacement = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.rubberBandExtent = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(2) self.isLayerVisible = True self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = self.firstPoint = None self.isEmittingPoint = False self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def deactivate(self): QgsMapToolEmitPoint.deactivate(self) self.reset() def canvasPressEvent(self, e): if self.firstPoint is None: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.originalCenter = self.layer.center # this tool do the displacement itself TODO update so it is done by # transformed coordinates + new center) self.originalCornerPoints = \ self.layer.transformedCornerCoordinates( *self.layer.transformParameters()) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) self.showDisplacement(self.startPoint, self.endPoint) self.layer.history.append({ "action": "2pointsA", "center": self.layer.center }) else: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.startY = e.pos().y() self.endY = self.startY self.isEmittingPoint = True self.height = self.canvas.height() self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) self.layer.history.append({ "action": "2pointsB", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale, "rotation": None }) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() if self.firstPoint is None: x = (self.originalCenter.x() + self.endPoint.x() - self.startPoint.x()) y = (self.originalCenter.y() + self.endPoint.y() - self.startPoint.y()) self.layer.setCenter(QgsPointXY(x, y)) self.firstPoint = self.endPoint setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() else: rotation = self.computeRotation() xScale = yScale = self.computeScale() self.layer.moveCenterFromPointRotate(self.firstPoint, rotation, xScale, yScale) self.layer.setScale(self.layer.xScale * xScale, self.layer.yScale * yScale) val = self.layer.rotation + rotation if val > 180: val = val - 360 self.iface.mainWindow().findChild( QDoubleSpinBox, 'FreehandRasterGeoreferencer_spinbox').setValue(val) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rasterShadow.reset() self.firstPoint = None self.startPoint = self.endPoint = None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) if self.firstPoint is None: self.showDisplacement(self.startPoint, self.endPoint) else: self.endY = e.pos().y() rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) def computeRotation(self): # The angle is the difference between angle # horizontal/endPoint-firstPoint and horizontal/startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.degrees(math.atan2(-dY, dX) - math.atan2(-dY0, dX0)) def computeScale(self): # The scale is the ratio between endPoint-firstPoint and # startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.sqrt((dX * dX + dY * dY) / (dX0 * dX0 + dY0 * dY0)) def showRotationScale(self, rotation, xScale, yScale): center, _, _, _ = self.layer.transformParameters() # newRotation = rotation + originalRotation cornerPoints = self.layer.transformedCornerCoordinatesFromPoint( self.firstPoint, rotation, xScale, yScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() # Calculate the displacement of the center due to the rotation from # another point. newCenterDX = (cornerPoints[0].x() + cornerPoints[2].x()) / 2 - center.x() newCenterDY = (cornerPoints[0].y() + cornerPoints[2].y()) / 2 - center.y() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(newCenterDX, newCenterDY, False) self.rasterShadow.setDeltaScale(xScale, yScale, False) self.rasterShadow.setDeltaRotation(rotation, True) self.rasterShadow.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point0 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point1 = QgsPointXY(self.firstPoint.x(), self.firstPoint.y()) point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) self.rubberBandDisplacement.addPoint(point0, False) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint(point2, True) # true to update canvas self.rubberBandDisplacement.show() def showDisplacement(self, startPoint, endPoint): self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandOrigin.addPoint(endPoint, True) self.rubberBandOrigin.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(endPoint.x(), endPoint.y()) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint(point2, True) # true to update canvas self.rubberBandDisplacement.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in self.originalCornerPoints: self._addDisplacementToPoint(self.rubberBandExtent, point, False) # for closing self._addDisplacementToPoint(self.rubberBandExtent, self.originalCornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement( self.endPoint.x() - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True) self.rasterShadow.show() def _addDisplacementToPoint(self, rubberBand, point, doUpdate): x = point.x() + self.endPoint.x() - self.startPoint.x() y = point.y() + self.endPoint.y() - self.startPoint.y() self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
class FinderBox(QComboBox): running = False toFinish = 0 searchStarted = pyqtSignal() searchFinished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.resultView = QTreeView() self.resultView.setHeaderHidden(True) self.resultView.setMinimumHeight(300) self.resultView.activated.connect(self.itemActivated) self.resultView.pressed.connect(self.itemPressed) self.setView(self.resultView) self.resultModel = ResultModel(self) self.setModel(self.resultModel) self.finders = finders for finder in self.finders.values(): finder.resultFound.connect(self.resultFound) finder.limitReached.connect(self.limitReached) finder.finished.connect(self.finished) self.clearButton = QPushButton(self) self.clearButton.setIcon( QIcon(":/plugins/quickfinder/icons/draft.svg")) self.clearButton.setText('') self.clearButton.setFlat(True) self.clearButton.setCursor(QCursor(Qt.ArrowCursor)) self.clearButton.setStyleSheet('border: 0px; padding: 0px;') self.clearButton.clicked.connect(self.clear) layout = QHBoxLayout(self) self.setLayout(layout) layout.addStretch() layout.addWidget(self.clearButton) layout.addSpacing(20) buttonSize = self.clearButton.sizeHint() # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) padding = buttonSize.width() # + frameWidth + 1 self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def clearSelection(self): self.resultModel.setSelected(None, self.resultView.palette()) self.rubber.reset() def clear(self): self.clearSelection() self.resultModel.clearResults() self.lineEdit().setText('') def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clearSelection() QComboBox.keyPressEvent(self, event) def search(self): if self.running: return toFind = self.lineEdit().text() if not toFind or toFind == '': return self.running = True self.searchStarted.emit() self.clearSelection() self.resultModel.clearResults() self.resultModel.truncateHistory(MySettings().value("historyLength")) self.resultModel.setLoading(True) self.showPopup() QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.findersToStart = [] for finder in self.finders.values(): if finder.activated(): self.findersToStart.append(finder) bbox = self.mapCanvas.fullExtent() while len(self.findersToStart) > 0: finder = self.findersToStart[0] self.findersToStart.remove(finder) self.resultModel.addResult(finder.name) finder.start(toFind, bbox=bbox) # For case there is no finder activated self.finished(None) def stop(self): self.findersToStart = [] for finder in self.finders.values(): if finder.isRunning(): finder.stop() self.finished(None) def resultFound(self, finder, layername, value, geometry, srid): self.resultModel.addResult(finder.name, layername, value, geometry, srid) self.resultView.expandAll() def limitReached(self, finder, layername): self.resultModel.addEllipsys(finder.name, layername) def finished(self, finder): if len(self.findersToStart) > 0: return for finder in self.finders.values(): if finder.isRunning(): return self.running = False self.searchFinished.emit() self.resultModel.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def itemActivated(self, index): item = self.resultModel.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.resultModel.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.resultModel.setSelected(item, self.resultView.palette()) geometry = self.transformGeom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoomToRubberBand() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.resultModel.setSelected(item, self.resultView.palette()) self.rubber.reset(child.geometry.type()) for i in xrange(0, item.rowCount()): geometry = self.transformGeom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoomToRubberBand() return if item.__class__.__name__ == 'QStandardItem': self.clearSelection() def transformGeom(self, item): src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapRenderer().destinationCrs() geom = QgsGeometry(item.geometry) geom.transform(QgsCoordinateTransform(src_crs, dest_crs)) return geom def zoomToRubberBand(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow): """ Main application window """ def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.canvaslayers = [] self.layerbuttons = [] self.project = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.bar = roam.messagebaritems.MessageBar(self.centralwidget) self.actionMap.setVisible(False) self.actionLegend.setVisible(False) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.menuGroup = QActionGroup(self) self.menuGroup.setExclusive(True) self.menuGroup.addAction(self.actionMap) self.menuGroup.addAction(self.actionDataEntry) self.menuGroup.addAction(self.actionLegend) self.menuGroup.addAction(self.actionProject) self.menuGroup.addAction(self.actionSync) self.menuGroup.addAction(self.actionSettings) self.menuGroup.addAction(self.actionGPS) self.menuGroup.triggered.connect(self.updatePage) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionLegend.triggered.connect(self.updatelegend) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) self.projectwidget.requestOpenProject.connect(self.loadProject) QgsProject.instance().readProject.connect(self._readProject) self.gpswidget.setgps(GPS) self.actionSettings.toggled.connect(self.settingswidget.populateControls) self.actionSettings.toggled.connect(self.settingswidget.readSettings) self.settingswidget.settingsupdated.connect(self.settingsupdated) self.dataentrywidget = DataEntryWidget(self.canvas, self.bar) self.widgetpage.layout().addWidget(self.dataentrywidget) self.dataentrywidget.rejected.connect(self.formrejected) self.dataentrywidget.featuresaved.connect(self.featureSaved) self.dataentrywidget.featuredeleted.connect(self.featuredeleted) self.dataentrywidget.failedsave.connect(self.failSave) self.dataentrywidget.helprequest.connect(self.showhelp) def createSpacer(width=0, height=0): widget = QWidget() widget.setMinimumWidth(width) widget.setMinimumHeight(height) return widget gpsspacewidget = createSpacer(30) sidespacewidget = createSpacer(30) sidespacewidget2 = createSpacer(height=20) sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) def createlabel(text): style = """ QLabel { color: #706565; font: 14px "Calibri" ; }""" label = QLabel(text) label.setStyleSheet(style) return label self.projectlabel = createlabel("Project: {project}") self.userlabel = createlabel("User: {user}".format(user=getpass.getuser())) self.positionlabel = createlabel('') self.gpslabel = createlabel("GPS: Not active") self.statusbar.addWidget(self.projectlabel) self.statusbar.addWidget(self.userlabel) spacer = createSpacer() spacer2 = createSpacer() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.statusbar.addWidget(spacer) self.statusbar.addWidget(self.positionlabel) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2) self.menutoolbar.insertWidget(self.actionProject, sidespacewidget) self.panels = [] self.connectButtons() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.canvas_page.layout().insertWidget(0, self.projecttoolbar) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.selectdataentry) self.centralwidget.layout().addWidget(self.statusbar) self.actionGPSFeature.setProperty('dataentry', True) self.infodock = InfoDock(self.canvas) self.infodock.featureupdated.connect(self.highlightfeature) self.infodock.hide() self.hidedataentry() self.canvas.extentsChanged.connect(self.updatestatuslabel) RoamEvents.openimage.connect(self.openimage) RoamEvents.openurl.connect(self.viewurl) RoamEvents.openfeatureform.connect(self.openForm) RoamEvents.openkeyboard.connect(self.openkeyboard) RoamEvents.selectioncleared.connect(self.clearselection) RoamEvents.editgeometry.connect(self.addforedit) RoamEvents.editgeometry_complete.connect(self.on_geometryedit) RoamEvents.onShowMessage.connect(self.showUIMessage) RoamEvents.selectionchanged.connect(self.highlightselection) RoamEvents.selectionchanged.connect(self.showInfoResults) GPS.gpspostion.connect(self.updatecanvasfromgps) GPS.firstfix.connect(self.gpsfirstfix) GPS.gpsdisconnected.connect(self.gpsdisconnected) self.lastgpsposition = None self.marker = GPSMarker(self.canvas) self.marker.hide() self.legendpage.showmap.connect(self.showmap) self.editfeaturestack = [] self.currentselection = {} def showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''): self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra) def addforedit(self, form, feature): self.editfeaturestack.append((form, feature)) self.loadform(form) actions = self.getcaptureactions() for action in actions: if action.isdefault: action.trigger() break def updatelegend(self): self.legendpage.updatecanvas(self.canvas) def gpsfirstfix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) def updatecanvasfromgps(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.canvas.setExtent(rect) self.canvas.refresh() self.marker.show() self.marker.setCenter(position) self.gpslabel.setText("GPS: PDOP {} HDOP {} VDOP {}".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) def gpsdisconnected(self): self.marker.hide() self.gpslabel.setText("GPS Not Active") def openkeyboard(self): if not roam.config.settings.get('keyboard', True): return if sys.platform == 'win32': try: programfiles = os.environ['ProgramW6432'] except KeyError: programfiles = os.environ['ProgramFiles'] cmd = r'{path}\Common Files\Microsoft Shared\ink\TabTip.exe'.format(path=programfiles) try: os.startfile(cmd) except WindowsError: roam.config.settings['keyboard'] = False roam.config.save() else: cmd = 'onboard' Popen(cmd) def selectdataentry(self): forms = self.project.forms formpicker = PickActionDialog(msg="Select data entry form") for form in forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(self.showformerror, form)) else: action.triggered.connect(partial(self.loadform, form)) formpicker.addAction(action) formpicker.exec_() def showformerror(self, form): pass def viewurl(self, url): """ Open a URL in Roam :param url: :return: """ key = url.toString().lstrip('file://') try: # Hack. Eww fix me. data, imagetype = roam.htmlviewer.images[os.path.basename(key)] pix = QPixmap() if imagetype == 'base64': pix.loadFromData(data) else: pix.load(data) self.openimage(pix) except KeyError: pix = QPixmap() pix.load(key) if pix.isNull(): QDesktopServices.openUrl(url) return self.openimage(pix) def openimage(self, pixmap): viewer = ImageViewer(self.stackedWidget) viewer.resize(self.stackedWidget.size()) viewer.openimage(pixmap) def settingsupdated(self, settings): self.show() self.actionGPS.updateGPSPort() def updatestatuslabel(self): extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format(extent.center().toString())) def on_geometryedit(self, form, feature): layer = form.QGISLayer self.reloadselection(layer, updated=[feature]) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def reloadselection(self, layer, deleted=[], updated=[]): """ Reload the selection after features have been updated or deleted. :param layer: :param deleted: :param updated: :return: """ selectedfeatures = self.currentselection[layer] # Update any features that have changed. for updatedfeature in updated: oldfeatures = [f for f in selectedfeatures if f.id() == updatedfeature.id()] for feature in oldfeatures: self.currentselection[layer].remove(feature) self.currentselection[layer].append(updatedfeature) # Delete any old ones for deletedid in deleted: oldfeatures = [f for f in selectedfeatures if f.id() == deletedid] for feature in oldfeatures: self.currentselection[layer].remove(feature) RoamEvents.selectionchanged.emit(self.currentselection) def highlightselection(self, results): self.clearselection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def clearselection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def highlightfeature(self, layer, feature, features): self.clearselection() self.highlightselection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def showmap(self): self.actionMap.setVisible(True) self.actionLegend.setVisible(True) self.actionMap.trigger() def hidedataentry(self): self.actionDataEntry.setVisible(False) def showdataentry(self): self.actionDataEntry.setVisible(True) self.actionDataEntry.trigger() def dataentrychanged(self, index): self.clearCapatureTools() if not index.isValid(): return modelindex = index # modelindex = self.dataentrymodel.index(index, 0) form = modelindex.data(Qt.UserRole + 1) self.dataentryselection.setCurrentIndex(index.row()) self.createCaptureButtons(form) def raiseerror(self, *exinfo): info = traceback.format_exception(*exinfo) item = self.bar.pushError(QApplication.translate('MainWindowPy','Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8), info) def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) self.actionQuit.triggered.connect(self.exit) def getcaptureactions(self): for action in self.projecttoolbar.actions(): if action.property('dataentry'): yield action def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def createCaptureButtons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.addNewFeature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def loadform(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.createCaptureButtons(form) def clearToolRubberBand(self): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def showhelp(self, url): help = HelpPage(self.stackedWidget) help.setHelpPage(url) help.show() def dataentryfinished(self): self.hidedataentry() self.showmap() self.cleartempobjects() self.infodock.refreshcurrent() def featuredeleted(self, layer, featureid): self.dataentryfinished() self.reloadselection(layer, deleted=[featureid]) self.canvas.refresh() def featureSaved(self): self.dataentryfinished() self.canvas.refresh() def failSave(self, messages): self.bar.pushError("Error when saving changes.", messages) def cleartempobjects(self): self.currentfeatureband.reset() self.clearToolRubberBand() def formrejected(self, message, level): self.dataentryfinished() if message: RoamEvents.raisemessage("Form Message", message, level, duration=2) self.cleartempobjects() def openForm(self, form, feature, editmode): """ Open the form that is assigned to the layer """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) self.showdataentry() self.dataentrywidget.openform(feature=feature, form=form, project=self.project, editmode=editmode) def editfeaturegeometry(self, form, feature, newgeometry): layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() RoamEvents.editgeometry_complete.emit(form, feature) def addNewFeature(self, form, geometry): """ Add a new new feature to the given layer """ layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value self.openForm(form, feature, editmode=False) def exit(self): """ Exit the application. """ QApplication.exit(0) def showInfoResults(self, results): forms = {} for layer in results.keys(): layername = layer.name() if not layername in forms: forms[layername] = list(self.project.formsforlayer(layername)) self.currentselection = results self.infodock.setResults(results, forms) self.infodock.show() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def missingLayers(self, layers): """ Called when layers have failed to load from the current project """ roam.utils.warning("Missing layers") map(roam.utils.warning, layers) missinglayers = roam.messagebaritems.MissingLayerItem(layers, parent=self.bar) self.bar.pushItem(missinglayers) def loadprojects(self, projects): """ Load the given projects into the project list """ projects = list(projects) self.projectwidget.loadProjectList(projects) self.syncwidget.loadprojects(projects) def updatePage(self, action): """ Update the current stack page based on the current selected action """ page = action.property("page") self.stackedWidget.setCurrentIndex(page) def show(self): """ Override show method. Handles showing the app in fullscreen mode or just maximized """ fullscreen = roam.config.settings.get("fullscreen", False) if fullscreen: self.showFullScreen() else: self.showMaximized() def viewprojects(self): self.stackedWidget.setCurrentIndex(1) @roam.utils.timeit def _readProject(self, doc): """ readProject is called by QgsProject once the map layer has been populated with all the layers """ parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() self.projectOpened() self.canvas.freeze(False) self.canvas.refresh() GPS.crs = self.canvas.mapRenderer().destinationCrs() self.showmap() @roam.utils.timeit def projectOpened(self): """ Called when a new project is opened in QGIS. """ projectpath = QgsProject.instance().fileName() self.project = Project.from_folder(os.path.dirname(projectpath)) self.projectlabel.setText("Project: {}".format(self.project.name)) try: firstform = self.project.forms[0] self.loadform(self.project.forms[0]) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) # Show panels for panel in self.project.getPanels(): self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel) self.panels.append(panel) self.infoTool.selectionlayers = self.project.selectlayersmapping() layers = self.project.legendlayersmapping().values() self.legendpage.updateitems(layers) self.actionPan.trigger() #noinspection PyArgumentList @roam.utils.timeit def loadProject(self, project): """ Load a project into the application . """ roam.utils.log(project) roam.utils.log(project.name) roam.utils.log(project.projectfile) roam.utils.log(project.valid) (passed, message) = project.onProjectLoad() if not passed: self.bar.pushMessage("Project load rejected", "Sorry this project couldn't" "be loaded. Click for me details.", QgsMessageBar.WARNING, extrainfo=message) return self.actionMap.trigger() self.closeProject() self.canvas.refresh() self.canvas.repaint() RoamEvents.selectioncleared.emit() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler(self.badLayerHandler) self.stackedWidget.setCurrentIndex(3) self.projectloading_label.setText("Project {} Loading".format(project.name)) pixmap = QPixmap(project.splash) w = self.projectimage.width() h = self.projectimage.height() self.projectimage.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio)) QApplication.processEvents() QDir.setCurrent(os.path.dirname(project.projectfile)) fileinfo = QFileInfo(project.projectfile) QgsProject.instance().read(fileinfo) def closeProject(self): """ Close the current open project """ self.clearCapatureTools() self.canvas.freeze() QgsMapLayerRegistry.instance().removeAllMapLayers() self.canvas.clear() self.canvas.freeze(False) for panel in self.panels: self.removeDockWidget(panel) del panel # Remove all the old buttons for action in self.layerbuttons: self.editgroup.removeAction(action) self.panels = [] self.project = None self.dataentrywidget.clear() self.hidedataentry() self.infodock.close()
class IntersectionDialog(QDialog, Ui_Intersection, SettingDialog): def __init__(self, iface, observations, initPoint): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() SettingDialog.__init__(self, self.settings, UpdateMode.NoUpdate) self.processButton.clicked.connect(self.doIntersection) self.okButton.clicked.connect(self.accept) self.finished.connect(self.resetRubber) self.initPoint = initPoint self.observations = [] self.solution = None self.report = "" self.rubber = QgsRubberBand(iface.mapCanvas(), QGis.Point) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIcon(self.settings.value("rubberIcon")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.observationTableWidget.displayRows(observations) self.observationTableWidget.itemChanged.connect(self.disbaleOKbutton) self.doIntersection() def resetRubber(self, dummy=0): self.rubber.reset() def disbaleOKbutton(self): self.okButton.setDisabled(True) def doIntersection(self): self.observations = [] self.solution = None self.report = "" self.rubber.reset() observations = self.observationTableWidget.getObservations() nObs = len(observations) if nObs < 2: self.reportBrowser.setText(QCoreApplication.translate("IntersectIt", "No intersection can be done " "with less than 2 observations.")) return if nObs == 2: if observations[0]["type"] == "distance" and observations[1]["type"] == "distance": intersection = TwoCirclesIntersection(observations, self.initPoint) elif observations[0]["type"] == "orientation" and observations[1]["type"] == "orientation": intersection = TwoOrientationIntersection(observations) else: intersection = DistanceOrientationIntersection(observations, self.initPoint) else: maxIter = self.advancedIntersecLSmaxIteration.value() threshold = self.advancedIntersecLSconvergeThreshold.value() intersection = LeastSquares(observations, self.initPoint, maxIter, threshold) self.reportBrowser.setText(intersection.report) if intersection.solution is not None: self.solution = intersection.solution self.observations = observations self.report = intersection.report self.okButton.setEnabled(True) self.rubber.setToGeometry(QgsGeometry().fromPoint(self.solution), None)
class MainWindow(mainwindow_widget, mainwindow_base): """ Main application window """ def __init__(self, settings): super(MainWindow, self).__init__() self.setupUi(self) self.settings = settings roam.featureform.settings = settings.settings self.canvaslayers = [] self.layerbuttons = [] self.project = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.bar = roam.messagebaritems.MessageBar(self) self.actionMap.setVisible(False) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.menuGroup = QActionGroup(self) self.menuGroup.setExclusive(True) self.menuGroup.addAction(self.actionMap) self.menuGroup.addAction(self.actionDataEntry) self.menuGroup.addAction(self.actionProject) self.menuGroup.addAction(self.actionSync) self.menuGroup.addAction(self.actionSettings) self.menuGroup.triggered.connect(self.updatePage) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) #TODO Extract GPS out into a service and remove UI stuff self.actionGPS = GPSAction(":/icons/gps", self.canvas, self.settings, self) self.projecttoolbar.addAction(self.actionGPS) self.projectwidget = ProjectsWidget(self) self.projectwidget.requestOpenProject.connect(self.loadProject) QgsProject.instance().readProject.connect(self._readProject) self.project_page.layout().addWidget(self.projectwidget) self.syncwidget = SyncWidget() self.syncpage.layout().addWidget(self.syncwidget) self.settingswidget = SettingsWidget(settings, self) self.settings_page.layout().addWidget(self.settingswidget) self.actionSettings.toggled.connect(self.settingswidget.populateControls) self.actionSettings.toggled.connect(self.settingswidget.readSettings) self.settingswidget.settingsupdated.connect(self.settingsupdated) self.dataentrywidget = DataEntryWidget(self.canvas, self.bar) self.widgetpage.layout().addWidget(self.dataentrywidget) self.dataentrywidget.rejected.connect(self.formrejected) self.dataentrywidget.featuresaved.connect(self.featureSaved) self.dataentrywidget.featuredeleted.connect(self.featuredeleted) self.dataentrywidget.failedsave.connect(self.failSave) self.dataentrywidget.helprequest.connect(self.showhelp) self.dataentrywidget.openimage.connect(self.openimage) def createSpacer(width=0, height=0): widget = QWidget() widget.setMinimumWidth(width) widget.setMinimumHeight(height) return widget gpsspacewidget = createSpacer(30) sidespacewidget = createSpacer(30) sidespacewidget2 = createSpacer(height=20) sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) def createlabel(text): style = """ QLabel { color: #706565; font: 14px "Calibri" ; }""" label = QLabel(text) label.setStyleSheet(style) return label self.projectlabel = createlabel("Project: {project}") self.userlabel = createlabel("User: {user}".format(user=getpass.getuser())) self.positionlabel = createlabel('') self.statusbar.addWidget(self.projectlabel) self.statusbar.addWidget(self.userlabel) spacer = createSpacer() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.statusbar.addWidget(spacer) self.statusbar.addWidget(self.positionlabel) self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2) self.menutoolbar.insertWidget(self.actionProject, sidespacewidget) self.stackedWidget.currentChanged.connect(self.updateUIState) self.panels = [] self.connectButtons() self.band = QgsRubberBand(self.canvas) self.band.setIconSize(20) self.band.setWidth(10) self.band.setColor(QColor(186, 93, 212, 76)) self.canvas_page.layout().insertWidget(0, self.projecttoolbar) self.dataentrymodel = QStandardItemModel(self) self.dataentrycombo = QComboBox(self.projecttoolbar) self.dataentrycombo.setIconSize(QSize(48,48)) self.dataentrycombo.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.dataentrycombo.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.dataentrycombo.setModel(self.dataentrymodel) self.dataentrycomboaction = self.projecttoolbar.insertWidget(self.topspaceraction, self.dataentrycombo) self.dataentrycombo.showPopup = self.selectdataentry self.biglist = BigList(self.canvas) self.biglist.setlabel("Select data entry form") self.biglist.setmodel(self.dataentrymodel) self.biglist.itemselected.connect(self.dataentrychanged) self.biglist.hide() self.centralwidget.layout().addWidget(self.statusbar) self.actionGPSFeature.setProperty('dataentry', True) self.infodock = InfoDock(self.canvas) self.infodock.requestopenform.connect(self.openForm) self.infodock.featureupdated.connect(self.highlightfeature) self.infodock.resultscleared.connect(self.clearselection) self.infodock.openurl.connect(self.viewurl) self.infodock.hide() self.hidedataentry() self.canvas.extentsChanged.connect(self.updatestatuslabel) self.projecttoolbar.toolButtonStyleChanged.connect(self.updatecombo) def selectdataentry(self, ): if self.dataentrycombo.count() == 0: return self.biglist.show() def viewurl(self, url): """ Open a URL in Roam :param url: :return: """ key = url.toString().lstrip('file://') try: # Hack. Eww fix me. data, imagetype = roam.htmlviewer.images[os.path.basename(key)] except KeyError: # It's not a image so lets just pass it of as a normal # URL QDesktopServices.openUrl(url) return pix = QPixmap() if imagetype == 'base64': pix.loadFromData(data) else: pix.load(data) self.openimage(pix) def openimage(self, pixmap): viewer = ImageViewer(self.stackedWidget) viewer.resize(self.stackedWidget.size()) viewer.openimage(pixmap) def updatecombo(self, *args): self.dataentrycombo.setMinimumHeight(0) def settingsupdated(self, settings): settings.save() self.show() self.actionGPS.updateGPSPort() # eww! roam.featureform.settings = settings.settings def updatestatuslabel(self): extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format(extent.center().toString())) def highlightselection(self, results): for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 150)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def clearselection(self): # Clear the main selection rubber band self.band.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() def highlightfeature(self, layer, feature, features): self.clearselection() self.highlightselection({layer: features}) self.band.setToGeometry(feature.geometry(), layer) def showmap(self): self.actionMap.setVisible(True) self.actionMap.trigger() def hidedataentry(self): self.actionDataEntry.setVisible(False) def showdataentry(self): self.actionDataEntry.setVisible(True) self.actionDataEntry.trigger() def dataentrychanged(self, index): wasactive = self.clearCapatureTools() if not index.isValid(): return modelindex = index # modelindex = self.dataentrymodel.index(index, 0) form = modelindex.data(Qt.UserRole + 1) self.dataentrycombo.setCurrentIndex(index.row()) self.createCaptureButtons(form, wasactive) def raiseerror(self, *exinfo): info = traceback.format_exception(*exinfo) item = self.bar.pushError('Seems something has gone wrong. Press for more details', info) def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = TouchMapTool(self.canvas) self.moveTool = MoveTool(self.canvas, []) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionMove, self.moveTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(self.showInfoResults) # The edit toolbutton is currently not being used but leaving it for feature. self.moveTool.layersupdated.connect(self.actionMove.setEnabled) self.moveTool.layersupdated.connect(self.actionEdit_Tools.setEnabled) self.actionGPSFeature.triggered.connect(self.addFeatureAtGPS) self.actionGPSFeature.setEnabled(self.actionGPS.isConnected) self.actionGPS.gpsfixed.connect(self.actionGPSFeature.setEnabled) self.actionHome.triggered.connect(self.homeview) self.actionQuit.triggered.connect(self.exit) def showToolError(self, label, message): self.bar.pushMessage(label, message, QgsMessageBar.WARNING) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def createCaptureButtons(self, form, wasselected): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) if action.isdefault: action.setChecked(wasselected) if hasattr(tool, 'geometryComplete'): add = partial(self.addNewFeature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showToolError, form.label)) self.projecttoolbar.insertAction(self.topspaceraction, self.actionGPSFeature) self.actionGPSFeature.setVisible(not tool.isEditTool()) def createFormButtons(self, forms): """ Create buttons for each form that is defined """ self.dataentrymodel.clear() self.clearCapatureTools() def captureFeature(form): item = QStandardItem(QIcon(form.icon), form.icontext) item.setData(form, Qt.UserRole + 1) item.setSizeHint(QSize(item.sizeHint().width(), self.projecttoolbar.height())) self.dataentrymodel.appendRow(item) capabilitityhandlers = {"capture": captureFeature} failedforms = [] for form in forms: valid, reasons = form.valid if not valid: roam.utils.log("Form is invalid for data entry because {}".format(reasons)) failedforms.append((form, reasons)) continue for capability in form.capabilities: try: capabilitityhandlers[capability](form) except KeyError: # Just ignore capabilities we don't support yet. continue if failedforms: for form, reasons in failedforms: html = "<h3>{}</h3><br>{}".format(form.label, "<br>".join(reasons)) self.bar.pushMessage("Form errors", "Looks like some forms couldn't be loaded", level=QgsMessageBar.WARNING, extrainfo=html) visible = self.dataentrymodel.rowCount() > 0 self.dataentrycomboaction.setVisible(visible) self.dataentrycombo.setMinimumHeight(self.projecttoolbar.height()) index = self.dataentrymodel.index(0, 0) self.dataentrychanged(index) def addFeatureAtGPS(self): """ Add a record at the current GPS location. """ index = self.dataentrycombo.currentIndex() modelindex = self.dataentrymodel.index(index, 0) form = modelindex.data(Qt.UserRole + 1) point = self.actionGPS.position point = QgsGeometry.fromPoint(point) self.addNewFeature(form=form, geometry=point) def clearToolRubberBand(self): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def showhelp(self, url): help = HelpPage(self.stackedWidget) help.setHelpPage(url) help.show() def dataentryfinished(self): self.hidedataentry() self.showmap() self.cleartempobjects() self.infodock.refreshcurrent() def featuredeleted(self): self.dataentryfinished() self.bar.pushMessage("Deleted", "Feature Deleted", QgsMessageBar.INFO, 1) self.canvas.refresh() def featureSaved(self): self.dataentryfinished() self.canvas.refresh() def failSave(self, messages): self.bar.pushError("Error when saving changes.", messages) def cleartempobjects(self): self.band.reset() self.clearToolRubberBand() def formrejected(self, message, level): self.dataentryfinished() if message: self.bar.pushMessage("Form Message", message, level, duration=2) self.cleartempobjects() def openForm(self, form, feature): """ Open the form that is assigned to the layer """ self.band.setToGeometry(feature.geometry(), form.QGISLayer) self.showdataentry() self.dataentrywidget.openform(feature=feature, form=form, project=self.project) def addNewFeature(self, form, geometry): """ Add a new new feature to the given layer """ layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value self.openForm(form, feature) def exit(self): """ Exit the application. """ QApplication.exit(0) def showInfoResults(self, results): self.infodock.clearResults() forms = {} for layer in results.keys(): layername = layer.name() if not layername in forms: forms[layername] = list(self.project.formsforlayer(layername)) self.infodock.setResults(results, forms) self.infodock.show() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def missingLayers(self, layers): """ Called when layers have failed to load from the current project """ roam.utils.warning("Missing layers") map(roam.utils.warning, layers) missinglayers = roam.messagebaritems.MissingLayerItem(layers, parent=self.bar) self.bar.pushItem(missinglayers) def loadprojects(self, projects): """ Load the given projects into the project list """ projects = list(projects) self.projectwidget.loadProjectList(projects) self.syncwidget.loadprojects(projects) def updatePage(self, action): """ Update the current stack page based on the current selected action """ page = action.property("page") self.stackedWidget.setCurrentIndex(page) def show(self): """ Override show method. Handles showing the app in fullscreen mode or just maximized """ fullscreen = self.settings.settings.get("fullscreen", False) if fullscreen: self.showFullScreen() else: self.showMaximized() def viewprojects(self): self.stackedWidget.setCurrentIndex(1) def updateUIState(self, page): """ Update the UI state to reflect the currently selected page in the stacked widget """ pass @roam.utils.timeit def _readProject(self, doc): """ readProject is called by QgsProject once the map layer has been populated with all the layers """ parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) self.canvas.updateScale() self.projectOpened() self.canvas.freeze(False) self.canvas.refresh() self.showmap() @roam.utils.timeit def projectOpened(self): """ Called when a new project is opened in QGIS. """ projectpath = QgsProject.instance().fileName() self.project = Project.from_folder(os.path.dirname(projectpath)) self.projectlabel.setText("Project: {}".format(self.project.name)) self.createFormButtons(forms=self.project.forms) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) # Show panels for panel in self.project.getPanels(): self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel) self.panels.append(panel) # TODO Abstract this out if not self.project.selectlayers: selectionlayers = QgsMapLayerRegistry.instance().mapLayers().values() else: selectionlayers = [] for layername in self.project.selectlayers: try: layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0] except IndexError: roam.utils.warning("Can't find QGIS layer for select layer {}".format(layername)) continue selectionlayers.append(layer) self.infoTool.selectionlayers = selectionlayers self.actionPan.trigger() #noinspection PyArgumentList @roam.utils.timeit def loadProject(self, project): """ Load a project into the application . """ roam.utils.log(project) roam.utils.log(project.name) roam.utils.log(project.projectfile) roam.utils.log(project.valid) (passed, message) = project.onProjectLoad() if not passed: self.bar.pushMessage("Project load rejected", "Sorry this project couldn't" "be loaded. Click for me details.", QgsMessageBar.WARNING, extrainfo=message) return self.actionMap.trigger() self.closeProject() self.canvas.refresh() self.canvas.repaint() self.infodock.clearResults() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler(self.badLayerHandler) self.stackedWidget.setCurrentIndex(3) self.projectloading_label.setText("Project {} Loading".format(project.name)) pixmap = QPixmap(project.splash) w = self.projectimage.width() h = self.projectimage.height() self.projectimage.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio)) QApplication.processEvents() QDir.setCurrent(os.path.dirname(project.projectfile)) fileinfo = QFileInfo(project.projectfile) QgsProject.instance().read(fileinfo) def closeProject(self): """ Close the current open project """ self.canvas.freeze() QgsMapLayerRegistry.instance().removeAllMapLayers() self.canvas.clear() self.canvas.freeze(False) for panel in self.panels: self.removeDockWidget(panel) del panel # Remove all the old buttons for action in self.layerbuttons: self.editgroup.removeAction(action) self.dataentrymodel.clear() self.panels = [] self.project = None self.dataentrywidget.clear() self.hidedataentry() self.infodock.close()
class ExtrapolateTool(QgsMapTool): """ Map tool class to extrapolate the elevation of a vertex at the end of a line """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/extrapolate_icon.png' self.text = QCoreApplication.translate("VDLTools", "Extrapolate the elevation of a vertex and a " "point at the extremity of a line") self.__layer = None self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__lastFeatureId = None self.__rubber = None self.__confDlg = None self.__selectedVertex = None self.__elevation = None self.__selectedFeature = None def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__rubber = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setIconSize(20) def deactivate(self): """ When the action is deselected """ self.__cancel() self.__rubber = None QgsMapTool.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def __cancel(self): """ To cancel used variables """ self.__layer.removeSelection() if self.__rubber is not None: self.__rubber.reset() self.__lastFeatureId = None self.__confDlg = None self.__selectedFeature = None self.__selectedVertex = None self.__elevation = None self.__isEditing = False def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer\ and QGis.fromOldWkbType(layer.wkbType()) == QgsWKBTypes.LineStringZ: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) self.__removeLayer() def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if not self.__isEditing: laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(event.mapPoint(), self.canvas(), [laySettings]) if f_l is not None: self.__lastFeatureId = f_l[0].id() self.__layer.setSelectedFeatures([f_l[0].id()]) self.__rubber.reset() geom = f_l[0].geometry() index = geom.closestVertex(event.mapPoint())[1] line_v2, curved = GeometryV2.asLineV2(geom, self.__iface) num_p = line_v2.numPoints() if num_p > 2 and (index == 0 or index == (num_p-1)): self.__rubber.setIcon(4) self.__rubber.setToGeometry(QgsGeometry(line_v2.pointN(index)), None) else: self.__layer.removeSelection() self.__rubber.reset() self.__lastFeatureId = None def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return geom = found_features[0].geometry() self.__selectedVertex = geom.closestVertex(event.mapPoint())[1] line_v2, curved = GeometryV2.asLineV2(geom, self.__iface) num_p = line_v2.numPoints() if num_p > 2 and (self.__selectedVertex == 0 or self.__selectedVertex == (num_p-1)): pt = line_v2.pointN(self.__selectedVertex) if self.__selectedVertex == 0: pt0 = line_v2.pointN(2) pt1 = line_v2.pointN(1) else: pt0 = line_v2.pointN(num_p-3) pt1 = line_v2.pointN(num_p-2) big_d = Finder.sqrDistForPoints(pt0, pt1) small_d = Finder.sqrDistForPoints(pt1, pt) self.__isEditing = True self.__selectedFeature = found_features[0] self.__elevation = round(pt0.z() + (1 + old_div(small_d, big_d)) * (pt1.z() - pt0.z()), 2) if small_d < (old_div(big_d, 4)): if pt.z() is not None and pt.z() != 0: message = QCoreApplication.translate("VDLTools", "This vertex has already an elevation ") + \ "(" + str(pt.z()) + ")" + \ QCoreApplication.translate("VDLTools", " do you really want to change it (new elevation : ") + \ str(self.__elevation) + ") ?" self.__confDlg = ExtrapolateConfirmDialog(message) self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.okButton().clicked.connect(self.__onEditOk) self.__confDlg.cancelButton().clicked.connect(self.__onEditCancel) self.__confDlg.show() else: self.__edit() else: message = QCoreApplication.translate("VDLTools", "The segment is too big, do you really want " "to extrapolate anyway ? (elevation : ") + \ str(self.__elevation) + ") ?" self.__confDlg = ExtrapolateConfirmDialog(message) self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.okButton().clicked.connect(self.__onEditOk) self.__confDlg.cancelButton().clicked.connect(self.__onEditCancel) self.__confDlg.show() def __onEditOk(self): """ When the Ok button in Extrapolate Confirm Dialog is pushed """ self.__confDlg.accept() self.__edit() def __onEditCancel(self): """ When the Cancel button in Extrapolate Confirm Dialog is pushed """ self.__confDlg.reject() def __edit(self): """ To add the new extrapolate elevation """ line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface) line_v2.setZAt(self.__selectedVertex, self.__elevation) self.__layer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) self.__cancel()
class ParentAction(object): def __init__(self, iface, settings, controller, plugin_dir): """ Class constructor """ # Initialize instance attributes self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.controller = controller self.plugin_dir = plugin_dir self.dao = self.controller.dao self.schema_name = self.controller.schema_name self.project_type = None self.plugin_version = self.get_plugin_version() self.add_layer = AddLayer(iface, settings, controller, plugin_dir) self.user_current_layer = None self.rubber_point = None self.rubber_polygon = None def init_rubber(self): try: self.rubber_point = QgsRubberBand(self.canvas, 0) self.rubber_point.setColor(Qt.yellow) self.rubber_point.setIconSize(10) self.rubber_polygon = QgsRubberBand(self.canvas, 2) self.rubber_polygon.setColor(Qt.darkRed) self.rubber_polygon.setIconSize(20) except: pass def set_controller(self, controller): """ Set controller class """ self.controller = controller self.schema_name = self.controller.schema_name def open_web_browser(self, dialog, widget=None): """ Display url using the default browser """ if widget is not None: url = utils_giswater.getWidgetText(dialog, widget) if url == 'null': url = 'http://www.giswater.org' else: url = 'http://www.giswater.org' webbrowser.open(url) def get_plugin_version(self): """ Get plugin version from metadata.txt file """ # Check if metadata file exists metadata_file = os.path.join(self.plugin_dir, 'metadata.txt') if not os.path.exists(metadata_file): message = "Metadata file not found" self.controller.show_warning(message, parameter=metadata_file) return None metadata = configparser.ConfigParser() metadata.read(metadata_file) plugin_version = metadata.get('general', 'version') if plugin_version is None: message = "Plugin version not found" self.controller.show_warning(message) return plugin_version def get_file_dialog(self, dialog, widget): """ Get file dialog """ # Check if selected file exists. Set default value if necessary file_path = utils_giswater.getWidgetText(dialog, widget) if file_path is None or file_path == 'null' or not os.path.exists(str(file_path)): folder_path = self.plugin_dir else: folder_path = os.path.dirname(file_path) # Open dialog to select file os.chdir(folder_path) file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.AnyFile) message = "Select file" files_path, filter_ = file_dialog.getOpenFileNames(parent=None, caption=self.controller.tr(message)) file_text = "" for file in files_path: file_text += f"{file}\n\n" if files_path: utils_giswater.setWidgetText(dialog, widget, str(file_text)) return files_path def get_folder_dialog(self, dialog, widget): """ Get folder dialog """ # Check if selected folder exists. Set default value if necessary folder_path = utils_giswater.getWidgetText(dialog, widget) if folder_path is None or folder_path == 'null' or not os.path.exists(folder_path): folder_path = os.path.expanduser("~") # Open dialog to select folder os.chdir(folder_path) file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.Directory) message = "Select folder" folder_path = file_dialog.getExistingDirectory( parent=None, caption=self.controller.tr(message), directory=folder_path) if folder_path: utils_giswater.setWidgetText(dialog, widget, str(folder_path)) 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')) v_screens = ctypes.windll.user32 screen_x = v_screens.GetSystemMetrics(78) # Width of virtual screen screen_y = v_screens.GetSystemMetrics(79) # Height of virtual screen monitors = v_screens.GetSystemMetrics(80) # Will return an integer of the num of display monitors present. if (int(x) < 0 and monitors == 1) or (int(y) < 0 and monitors == 1): dialog.resize(int(width), int(height)) else: 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 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() + 8) self.controller.plugin_settings_set_value(dialog.objectName() + "_y", dialog.pos().y() + 31) def get_last_tab(self, dialog, selector_name): """ Get the name of the last tab used by the user from QSettings() :param dialog: QDialog :param selector_name: Name of the selector (String) :return: Name of the last tab used by the user (string) """ tab_name = self.controller.plugin_settings_value(f"{dialog.objectName()}_{selector_name}") return tab_name def save_current_tab(self, dialog, tab_widget, selector_name): """ Save the name of current tab used by the user into QSettings() :param dialog: QDialog :param tab_widget: QTabWidget :param selector_name: Name of the selector (String) """ index = tab_widget.currentIndex() tab = tab_widget.widget(index) if tab: tab_name = tab.objectName() dlg_name = dialog.objectName() self.controller.plugin_settings_set_value(f"{dlg_name}_{selector_name}", tab_name) def open_dialog(self, dlg=None, dlg_name=None, info=True, maximize_button=True, stay_on_top=True, title=None): """ Open dialog """ # Check database connection before opening dialog if not self.controller.check_db_connection(): return if dlg is None or type(dlg) is bool: dlg = self.dlg # Set window title if title is not None: dlg.setWindowTitle(title) else: if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top, maximize/minimize button and information button # if info is True maximize flag will be ignored # To enable maximize button you must set info to False flags = Qt.WindowCloseButtonHint if info: flags |= Qt.WindowSystemMenuHint | Qt.WindowContextHelpButtonHint else: if maximize_button: flags |= Qt.WindowMinMaxButtonsHint if stay_on_top: flags |= Qt.WindowStaysOnTopHint dlg.setWindowFlags(flags) # Open dialog if issubclass(type(dlg), GwDialog): dlg.open() elif issubclass(type(dlg), GwMainWindow): dlg.show() else: dlg.show() def close_dialog(self, dlg=None): """ Close dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg try: self.save_settings(dlg) dlg.close() map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.iface.actionPan().trigger() except AttributeError: pass except Exception as e: self.controller.log_info(type(e).__name__) def multi_row_selector(self, dialog, tableleft, tableright, field_id_left, field_id_right, name='name', hide_left=[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], hide_right=[1, 2, 3], aql=""): """ :param dialog: :param tableleft: Table to consult and load on the left side :param tableright: Table to consult and load on the right side :param field_id_left: ID field of the left table :param field_id_right: ID field of the right table :param name: field name (used in add_lot.py) :param hide_left: Columns to hide from the left table :param hide_right: Columns to hide from the right table :param aql: (add query left) Query added to the left side (used in basic.py def basic_exploitation_selector()) :return: """ # fill QTableView all_rows tbl_all_rows = dialog.findChild(QTableView, "all_rows") tbl_all_rows.setSelectionBehavior(QAbstractItemView.SelectRows) schema_name = self.schema_name.replace('"', '') query_left = f"SELECT * FROM {schema_name}.{tableleft} WHERE {name} NOT IN " query_left += f"(SELECT {schema_name}.{tableleft}.{name} FROM {schema_name}.{tableleft}" query_left += f" RIGHT JOIN {schema_name}.{tableright} ON {tableleft}.{field_id_left} = {tableright}.{field_id_right}" query_left += f" WHERE cur_user = current_user)" query_left += f" AND {field_id_left} > -1" query_left += aql self.fill_table_by_query(tbl_all_rows, query_left + f" ORDER BY {name};") self.hide_colums(tbl_all_rows, hide_left) tbl_all_rows.setColumnWidth(1, 200) # fill QTableView selected_rows tbl_selected_rows = dialog.findChild(QTableView, "selected_rows") tbl_selected_rows.setSelectionBehavior(QAbstractItemView.SelectRows) query_right = f"SELECT {tableleft}.{name}, cur_user, {tableleft}.{field_id_left}, {tableright}.{field_id_right}" query_right += f" FROM {schema_name}.{tableleft}" query_right += f" JOIN {schema_name}.{tableright} ON {tableleft}.{field_id_left} = {tableright}.{field_id_right}" query_right += " WHERE cur_user = current_user" self.fill_table_by_query(tbl_selected_rows, query_right + f" ORDER BY {name};") self.hide_colums(tbl_selected_rows, hide_right) tbl_selected_rows.setColumnWidth(0, 200) # Button select dialog.btn_select.clicked.connect(partial(self.multi_rows_selector, tbl_all_rows, tbl_selected_rows, field_id_left, tableright, field_id_right, query_left, query_right, field_id_right)) # Button unselect query_delete = f"DELETE FROM {schema_name}.{tableright}" query_delete += f" WHERE current_user = cur_user AND {tableright}.{field_id_right}=" dialog.btn_unselect.clicked.connect(partial(self.unselector, tbl_all_rows, tbl_selected_rows, query_delete, query_left, query_right, field_id_right)) # QLineEdit dialog.txt_name.textChanged.connect(partial(self.query_like_widget_text, dialog, dialog.txt_name, tbl_all_rows, tableleft, tableright, field_id_right, field_id_left, name, aql)) # Order control tbl_all_rows.horizontalHeader().sectionClicked.connect(partial(self.order_by_column, tbl_all_rows, query_left)) tbl_selected_rows.horizontalHeader().sectionClicked.connect( partial(self.order_by_column, tbl_selected_rows, query_right)) def order_by_column(self, qtable, query, idx): """ :param qtable: QTableView widget :param query: Query for populate QsqlQueryModel :param idx: The index of the clicked column :return: """ oder_by = {0: "ASC", 1: "DESC"} sort_order = qtable.horizontalHeader().sortIndicatorOrder() col_to_sort = qtable.model().headerData(idx, Qt.Horizontal) query += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable, query) self.refresh_map_canvas() def hide_colums(self, widget, comuns_to_hide): for i in range(0, len(comuns_to_hide)): widget.hideColumn(comuns_to_hide[i]) def unselector(self, qtable_left, qtable_right, query_delete, query_left, query_right, field_id_right): selected_list = qtable_right.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return expl_id = [] for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = str(qtable_right.model().record(row).value(field_id_right)) expl_id.append(id_) for i in range(0, len(expl_id)): self.controller.execute_sql(query_delete + str(expl_id[i])) # Refresh oder_by = {0: "ASC", 1: "DESC"} sort_order = qtable_left.horizontalHeader().sortIndicatorOrder() idx = qtable_left.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_left.model().headerData(idx, Qt.Horizontal) query_left += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_left, query_left) sort_order = qtable_right.horizontalHeader().sortIndicatorOrder() idx = qtable_right.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_right.model().headerData(idx, Qt.Horizontal) query_right += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_right, query_right) self.refresh_map_canvas() def multi_rows_selector(self, qtable_left, qtable_right, id_ori, tablename_des, id_des, query_left, query_right, field_id): """ :param qtable_left: QTableView origin :param qtable_right: QTableView destini :param id_ori: Refers to the id of the source table :param tablename_des: table destini :param id_des: Refers to the id of the target table, on which the query will be made :param query_right: :param query_left: :param field_id: """ selected_list = qtable_left.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return expl_id = [] curuser_list = [] for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = qtable_left.model().record(row).value(id_ori) expl_id.append(id_) curuser = qtable_left.model().record(row).value("cur_user") curuser_list.append(curuser) for i in range(0, len(expl_id)): # Check if expl_id already exists in expl_selector sql = (f"SELECT DISTINCT({id_des}, cur_user)" f" FROM {tablename_des}" f" WHERE {id_des} = '{expl_id[i]}' AND cur_user = current_user") row = self.controller.get_row(sql) if row: # if exist - show warning message = "Id already selected" self.controller.show_info_box(message, "Info", parameter=str(expl_id[i])) else: sql = (f"INSERT INTO {tablename_des} ({field_id}, cur_user) " f" VALUES ({expl_id[i]}, current_user)") self.controller.execute_sql(sql) # Refresh oder_by = {0: "ASC", 1: "DESC"} sort_order = qtable_left.horizontalHeader().sortIndicatorOrder() idx = qtable_left.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_left.model().headerData(idx, Qt.Horizontal) query_left += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_right, query_right) sort_order = qtable_right.horizontalHeader().sortIndicatorOrder() idx = qtable_right.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_right.model().headerData(idx, Qt.Horizontal) query_right += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_left, query_left) self.refresh_map_canvas() def fill_table(self, widget, table_name, set_edit_strategy=QSqlTableModel.OnManualSubmit, 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 self.model = QSqlTableModel() self.model.setTable(table_name) self.model.setEditStrategy(set_edit_strategy) self.model.setSort(0, 0) self.model.select() # Check for errors if self.model.lastError().isValid(): self.controller.show_warning(self.model.lastError().text()) # Attach model to table view widget.setModel(self.model) if expr_filter: widget.model().setFilter(expr_filter) def fill_table_by_query(self, qtable, query): """ :param qtable: QTableView to show :param query: query to set model """ model = QSqlQueryModel() model.setQuery(query) qtable.setModel(model) qtable.show() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) def query_like_widget_text(self, dialog, text_line, qtable, tableleft, tableright, field_id_r, field_id_l, name='name', aql=''): """ Fill the QTableView by filtering through the QLineEdit""" schema_name = self.schema_name.replace('"', '') query = utils_giswater.getWidgetText(dialog, text_line, return_string_null=False).lower() sql = (f"SELECT * FROM {schema_name}.{tableleft} WHERE {name} NOT IN " f"(SELECT {tableleft}.{name} FROM {schema_name}.{tableleft}" f" RIGHT JOIN {schema_name}.{tableright}" f" ON {tableleft}.{field_id_l} = {tableright}.{field_id_r}" f" WHERE cur_user = current_user) AND LOWER({name}::text) LIKE '%{query}%'" f" AND {field_id_l} > -1") sql += aql self.fill_table_by_query(qtable, sql) 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 check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr_filter 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 refresh_map_canvas(self, restore_cursor=False): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() if restore_cursor: self.set_cursor_restore() def set_cursor_wait(self): """ Change cursor to 'WaitCursor' """ QApplication.setOverrideCursor(Qt.WaitCursor) def set_cursor_restore(self): """ Restore to previous cursors """ QApplication.restoreOverrideCursor() 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_table_columns(self, dialog, widget, table_name, sort_order=0, isQStandardItemModel=False): """ Configuration of tables. Set visibility and width of columns """ widget = utils_giswater.getWidget(dialog, widget) if not widget: return # Set width and alias of visible columns columns_to_delete = [] sql = (f"SELECT columnindex, width, alias, status" f" FROM config_form_tableview" f" WHERE tablename = '{table_name}'" f" ORDER BY columnindex") rows = self.controller.get_rows(sql, log_info=False) if not rows: return for row in rows: if not row['status']: columns_to_delete.append(row['columnindex'] - 1) else: width = row['width'] if width is None: width = 100 widget.setColumnWidth(row['columnindex'] - 1, width) widget.model().setHeaderData(row['columnindex'] - 1, Qt.Horizontal, row['alias']) # Set order if isQStandardItemModel: widget.model().sort(sort_order, Qt.AscendingOrder) else: widget.model().setSort(sort_order, Qt.AscendingOrder) widget.model().select() # Delete columns for column in columns_to_delete: widget.hideColumn(column) return widget def connect_signal_selection_changed(self, option): """ Connect signal selectionChanged """ try: if option == "mincut_connec": self.canvas.selectionChanged.connect(partial(self.snapping_selection_connec)) elif option == "mincut_hydro": self.canvas.selectionChanged.connect(partial(self.snapping_selection_hydro)) except Exception: pass def disconnect_signal_selection_changed(self): """ Disconnect signal selectionChanged """ try: self.canvas.selectionChanged.disconnect() except Exception: pass finally: self.iface.actionPan().trigger() def set_label_current_psector(self, dialog): sql = ("SELECT t1.name FROM plan_psector AS t1 " " INNER JOIN config_param_user AS t2 ON t1.psector_id::text = t2.value " " WHERE t2.parameter='plan_psector_vdefault' AND cur_user = current_user") row = self.controller.get_row(sql) if not row: return utils_giswater.setWidgetText(dialog, 'lbl_vdefault_psector', row[0]) def multi_rows_delete(self, widget, table_name, column_id): """ Delete selected elements of the table :param QTableView widget: origin :param table_name: table origin :param column_id: Refers to the id of the source table """ # 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 = "" for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = widget.model().record(row).value(str(column_id)) inf_text += f"{id_}, " list_id += f"'{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 = f"DELETE FROM {table_name}" sql += f" WHERE {column_id} IN ({list_id})" self.controller.execute_sql(sql) widget.model().select() def select_features_by_expr(self, layer, expr): """ Select features of @layer applying @expr """ if not layer: return if expr is None: layer.removeSelection() else: it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result and select them id_list = [i.id() for i in it] if len(id_list) > 0: layer.selectByIds(id_list) else: layer.removeSelection() def hide_void_groupbox(self, dialog): """ Hide empty groupbox """ grb_list = {} grbox_list = dialog.findChildren(QGroupBox) for grbox in grbox_list: widget_list = grbox.findChildren(QWidget) if len(widget_list) == 0: grb_list[grbox.objectName()] = 0 grbox.setVisible(False) return grb_list def zoom_to_selected_features(self, layer, geom_type=None, zoom=None): """ Zoom to selected features of the @layer with @geom_type """ if not layer: return self.iface.setActiveLayer(layer) self.iface.actionZoomToSelected().trigger() if geom_type: # Set scale = scale_zoom if geom_type in ('node', 'connec', 'gully'): scale = self.scale_zoom # Set scale = max(current_scale, scale_zoom) elif geom_type == 'arc': scale = self.iface.mapCanvas().scale() if int(scale) < int(self.scale_zoom): scale = self.scale_zoom else: scale = 5000 if zoom is not None: scale = zoom self.iface.mapCanvas().zoomScale(float(scale)) def make_list_for_completer(self, sql): """ Prepare a list with the necessary items for the completer :param sql: Query to be executed, where will we get the list of items (string) :return list_items: List with the result of the query executed (List) ["item1","item2","..."] """ rows = self.controller.get_rows(sql) list_items = [] if rows: for row in rows: list_items.append(str(row[0])) return list_items def set_completer_lineedit(self, qlineedit, list_items): """ Set a completer into a QLineEdit :param qlineedit: Object where to set the completer (QLineEdit) :param list_items: List of items to set into the completer (List)["item1","item2","..."] """ completer = QCompleter() completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setMaxVisibleItems(10) completer.setCompletionMode(0) completer.setFilterMode(Qt.MatchContains) completer.popup().setStyleSheet("color: black;") qlineedit.setCompleter(completer) model = QStringListModel() model.setStringList(list_items) completer.setModel(model) def get_max_rectangle_from_coords(self, list_coord): """ Returns the minimum rectangle(x1, y1, x2, y2) of a series of coordinates :type list_coord: list of coors in format ['x1 y1', 'x2 y2',....,'x99 y99'] """ coords = list_coord.group(1) polygon = coords.split(',') x, y = polygon[0].split(' ') min_x = x # start with something much higher than expected min min_y = y max_x = x # start with something much lower than expected max max_y = y for i in range(0, len(polygon)): x, y = polygon[i].split(' ') if x < min_x: min_x = x if x > max_x: max_x = x if y < min_y: min_y = y if y > max_y: max_y = y return max_x, max_y, min_x, min_y def zoom_to_rectangle(self, x1, y1, x2, y2, margin=5): rect = QgsRectangle(float(x1) - margin, float(y1) - margin, float(x2) + margin, float(y2) + margin) self.canvas.setExtent(rect) self.canvas.refresh() def create_action(self, action_name, action_group, icon_num=None, text=None): """ Creates a new action with selected parameters """ icon = None icon_folder = self.plugin_dir + '/icons/' icon_path = icon_folder + icon_num + '.png' if os.path.exists(icon_path): icon = QIcon(icon_path) if icon is None: action = QAction(text, action_group) else: action = QAction(icon, text, action_group) action.setObjectName(action_name) return action def set_wait_cursor(self): QApplication.instance().setOverrideCursor(Qt.WaitCursor) def set_arrow_cursor(self): QApplication.instance().setOverrideCursor(Qt.ArrowCursor) def delete_layer_from_toc(self, layer_name): """ Delete layer from toc if exist """ layer = None for lyr in list(QgsProject.instance().mapLayers().values()): if lyr.name() == layer_name: layer = lyr break if layer is not None: # Remove layer QgsProject.instance().removeMapLayer(layer) # Remove group if is void root = QgsProject.instance().layerTreeRoot() group = root.findGroup('GW Temporal Layers') if group: layers = group.findLayers() if not layers: root.removeChildNode(group) self.delete_layer_from_toc(layer_name) def create_body(self, form='', feature='', filter_fields='', extras=None): """ Create and return parameters as body to functions""" client = f'$${{"client":{{"device":4, "infoType":1, "lang":"ES"}}, ' form = f'"form":{{{form}}}, ' feature = f'"feature":{{{feature}}}, ' filter_fields = f'"filterFields":{{{filter_fields}}}' page_info = f'"pageInfo":{{}}' data = f'"data":{{{filter_fields}, {page_info}' if extras is not None: data += ', ' + extras data += f'}}}}$$' body = "" + client + form + feature + data return body def get_composers_list(self): layour_manager = QgsProject.instance().layoutManager().layouts() active_composers = [layout for layout in layour_manager] return active_composers def get_composer_index(self, name): index = 0 composers = self.get_composers_list() for comp_view in composers: composer_name = comp_view.name() if composer_name == name: break index += 1 return index def set_restriction(self, dialog, widget_to_ignore, restriction): """ Set all widget enabled(False) or readOnly(True) except those on the tuple :param dialog: :param widget_to_ignore: tuple = ('widgetname1', 'widgetname2', 'widgetname3', ...) :param restriction: roles that do not have access. tuple = ('role1', 'role1', 'role1', ...) :return: """ role = self.controller.get_restriction() if role in restriction: widget_list = dialog.findChildren(QWidget) for widget in widget_list: if widget.objectName() in widget_to_ignore: continue # Set editable/readonly if type(widget) in (QLineEdit, QDoubleSpinBox, QTextEdit): widget.setReadOnly(True) widget.setStyleSheet("QWidget {background: rgb(242, 242, 242);color: rgb(100, 100, 100)}") elif type(widget) in (QComboBox, QCheckBox, QTableView, QPushButton): widget.setEnabled(False) def set_dates_from_to(self, widget_from, widget_to, table_name, field_from, field_to): sql = (f"SELECT MIN(LEAST({field_from}, {field_to}))," f" MAX(GREATEST({field_from}, {field_to}))" f" FROM {table_name}") row = self.controller.get_row(sql, log_sql=False) current_date = QDate.currentDate() if row: if row[0]: widget_from.setDate(row[0]) else: widget_from.setDate(current_date) if row[1]: widget_to.setDate(row[1]) else: widget_to.setDate(current_date) def get_values_from_catalog(self, table_name, typevalue, order_by='id'): sql = (f"SELECT id, idval" f" FROM {table_name}" f" WHERE typevalue = '{typevalue}'" f" ORDER BY {order_by}") rows = self.controller.get_rows(sql) return rows def integer_validator(self, value, widget, btn_accept): """ Check if the value is an integer or not. This function is called in def set_datatype_validator(self, value, widget, btn) widget = getattr(self, f"{widget.property('datatype')}_validator")( value, widget, btn) """ if value is None or bool(re.search("^\d*$", value)): widget.setStyleSheet(None) btn_accept.setEnabled(True) else: widget.setStyleSheet("border: 1px solid red") btn_accept.setEnabled(False) def double_validator(self, value, widget, btn_accept): """ Check if the value is double or not. This function is called in def set_datatype_validator(self, value, widget, btn) widget = getattr(self, f"{widget.property('datatype')}_validator")( value, widget, btn) """ if value is None or bool(re.search("^\d*$", value)) or bool(re.search("^\d+\.\d+$", value)): widget.setStyleSheet(None) btn_accept.setEnabled(True) else: widget.setStyleSheet("border: 1px solid red") btn_accept.setEnabled(False) def load_qml(self, layer, qml_path): """ Apply QML style located in @qml_path in @layer """ if layer is None: return False if not os.path.exists(qml_path): self.controller.log_warning("File not found", parameter=qml_path) return False if not qml_path.endswith(".qml"): self.controller.log_warning("File extension not valid", parameter=qml_path) return False layer.loadNamedStyle(qml_path) layer.triggerRepaint() return True def open_file_path(self, filter_="All (*.*)"): """ Open QFileDialog """ msg = self.controller.tr("Select DXF file") path, filter_ = QFileDialog.getOpenFileName(None, msg, "", filter_) return path, filter_ def show_exceptions_msg(self, title, msg=""): cat_exception = {'KeyError': 'Key on returned json from ddbb is missed.'} self.dlg_info = DialogTextUi() self.dlg_info.btn_accept.setVisible(False) self.dlg_info.btn_close.clicked.connect(partial(self.close_dialog, self.dlg_info)) self.dlg_info.setWindowTitle(title) utils_giswater.setWidgetText(self.dlg_info, self.dlg_info.txt_infolog, msg) self.open_dialog(self.dlg_info, dlg_name='dialog_text', title=title) def put_combobox(self, qtable, rows, field, widget_pos, combo_values): """ Set one column of a QtableView as QComboBox with values from database. :param qtable: QTableView to fill :param rows: List of items to set QComboBox (["..", "..."]) :param field: Field to set QComboBox (String) :param widget_pos: Position of the column where we want to put the QComboBox (integer) :param combo_values: List of items to populate QComboBox (["..", "..."]) :return: """ for x in range(0, len(rows)): combo = QComboBox() row = rows[x] # Populate QComboBox utils_giswater.set_item_data(combo, combo_values, 1) # Set QCombobox to wanted item utils_giswater.set_combo_itemData(combo, str(row[field]), 1) # Get index and put QComboBox into QTableView at index position idx = qtable.model().index(x, widget_pos) qtable.setIndexWidget(idx, combo) combo.currentIndexChanged.connect(partial(self.update_status, combo, qtable, x, widget_pos)) def update_status(self, combo, qtable, pos_x, widget_pos): """ Update values from QComboBox to QTableView :param combo: QComboBox from which we will take the value :param qtable: QTableView Where update values :param pos_x: Position of the row where we want to update value (integer) :param widget_pos:Position of the widget where we want to update value (integer) :return: """ elem = combo.itemData(combo.currentIndex()) i = qtable.model().index(pos_x, widget_pos) qtable.model().setData(i, elem[0]) i = qtable.model().index(pos_x, widget_pos + 1) qtable.model().setData(i, elem[1]) def get_feature_by_id(self, layer, id_, field_id): expr = "" + str(field_id) + "= '" + str(id_) + "'" features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(expr)) for feature in features: if str(feature[field_id]) == str(id_): return feature return False def document_insert(self, dialog, tablename, field, field_value): """ Insert a document related to the current visit :param dialog: (QDialog ) :param tablename: Name of the table to make the queries (string) :param field: Field of the table to make the where clause (string) :param field_value: Value to compare in the clause where (string) """ doc_id = dialog.doc_id.text() if not doc_id: message = "You need to insert doc_id" self.controller.show_warning(message) return # Check if document already exist sql = (f"SELECT doc_id" f" FROM {tablename}" f" WHERE doc_id = '{doc_id}' AND {field} = '{field_value}'") row = self.controller.get_row(sql) if row: msg = "Document already exist" self.controller.show_warning(msg) return # Insert into new table sql = (f"INSERT INTO {tablename} (doc_id, {field})" f" VALUES ('{doc_id}', '{field_value}')") status = self.controller.execute_sql(sql) if status: message = "Document inserted successfully" self.controller.show_info(message) dialog.tbl_document.model().select() def document_open(self, qtable): """ Open selected document """ # Get selected rows field_index = qtable.model().fieldIndex('path') selected_list = qtable.selectionModel().selectedRows(field_index) if not selected_list: message = "Any record selected" self.controller.show_info_box(message) return elif len(selected_list) > 1: message = "More then one document selected. Select just one document." self.controller.show_warning(message) return path = selected_list[0].data() # Check if file exist if os.path.exists(path): # Open the document if sys.platform == "win32": os.startfile(path) else: opener = "open" if sys.platform == "darwin" else "xdg-open" subprocess.call([opener, path]) else: webbrowser.open(path) def document_delete(self, qtable, tablename): """ Delete record from selected rows in tbl_document """ # Get selected rows. 0 is the column of the pk 0 'id' selected_list = qtable.selectionModel().selectedRows(0) if len(selected_list) == 0: message = "Any record selected" self.controller.show_info_box(message) return selected_id = [] for index in selected_list: doc_id = index.data() selected_id.append(str(doc_id)) message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, ','.join(selected_id)) if answer: sql = (f"DELETE FROM {tablename}" f" WHERE id IN ({','.join(selected_id)})") status = self.controller.execute_sql(sql) if not status: message = "Error deleting data" self.controller.show_warning(message) return else: message = "Document deleted" self.controller.show_info(message) qtable.model().select() def get_all_actions(self): actions_list = self.iface.mainWindow().findChildren(QAction) for action in actions_list: self.controller.log_info(str(action.objectName())) action.triggered.connect(partial(self.show_action_name, action)) def show_action_name(self, action): self.controller.log_info(str(action.objectName())) def get_points(self, list_coord=None): """ Return list of QgsPoints taken from geometry :type list_coord: list of coors in format ['x1 y1', 'x2 y2',....,'x99 y99'] """ coords = list_coord.group(1) polygon = coords.split(',') points = [] for i in range(0, len(polygon)): x, y = polygon[i].split(' ') point = QgsPointXY(float(x), float(y)) points.append(point) return points def hilight_feature_by_id(self, qtable, layer_name, field_id, width, index): """ Based on the received index and field_id, the id of the received field_id is searched within the table and is painted in red on the canvas """ self.resetRubberbands() layer = self.controller.get_layer_by_tablename(layer_name) if not layer: return row = index.row() column_index = utils_giswater.get_col_index_by_col_name(qtable, field_id) _id = index.sibling(row, column_index).data() feature = self.get_feature_by_id(layer, _id, field_id) try: geometry = feature.geometry() self.rubber_polygon.setToGeometry(geometry, None) self.rubber_polygon.setColor(QColor(255, 0, 0, 100)) self.rubber_polygon.setWidth(width) self.rubber_polygon.show() except AttributeError: pass def draw_polyline(self, points, color=QColor(255, 0, 0, 100), width=5, duration_time=None): """ Draw 'line' over canvas following list of points :param duration_time: integer milliseconds ex: 3000 for 3 seconds """ if self.rubber_polygon is None: self.init_rubber() rb = self.rubber_polygon polyline = QgsGeometry.fromPolylineXY(points) rb.setToGeometry(polyline, None) rb.setColor(color) rb.setWidth(width) rb.show() # wait to simulate a flashing effect if duration_time is not None: QTimer.singleShot(duration_time, self.resetRubberbands) return rb def resetRubberbands(self): if self.rubber_polygon is None: self.init_rubber() self.rubber_point.reset(0) self.rubber_polygon.reset(2) def restore_user_layer(self): if self.user_current_layer: self.iface.setActiveLayer(self.user_current_layer) else: layer = self.controller.get_layer_by_tablename('v_edit_node') if layer: self.iface.setActiveLayer(layer) def set_style_mapzones(self): extras = f'"mapzones":""' body = self.create_body(extras=extras) json_return = self.controller.get_json('gw_fct_getstylemapzones', body) if not json_return: return False for mapzone in json_return['body']['data']['mapzones']: # Loop for each mapzone returned on json lyr = self.controller.get_layer_by_tablename(mapzone['layer']) categories = [] status = mapzone['status'] if status == 'Disable': pass if lyr: # Loop for each id returned on json for id in mapzone['values']: # initialize the default symbol for this geometry type symbol = QgsSymbol.defaultSymbol(lyr.geometryType()) symbol.setOpacity(float(mapzone['opacity'])) # Setting simp R = random.randint(0, 255) G = random.randint(0, 255) B = random.randint(0, 255) if status == 'Stylesheet': try: R = id['stylesheet']['color'][0] G = id['stylesheet']['color'][1] B = id['stylesheet']['color'][2] except TypeError: R = random.randint(0, 255) G = random.randint(0, 255) B = random.randint(0, 255) elif status == 'Random': R = random.randint(0, 255) G = random.randint(0, 255) B = random.randint(0, 255) # Setting sytle layer_style = {'color': '{}, {}, {}'.format(int(R), int(G), int(B))} symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style) if symbol_layer is not None: symbol.changeSymbolLayer(0, symbol_layer) category = QgsRendererCategory(id['id'], symbol, str(id['id'])) categories.append(category) # apply symbol to layer renderer if 'idname' in mapzone: lyr.setRenderer(QgsCategorizedSymbolRenderer(mapzone['idname'], categories)) # repaint layer lyr.triggerRepaint()
class nominatim_dlg(QDockWidget, Ui_search): def getHttp(self, uri, params): QgsApplication.setOverrideCursor(Qt.WaitCursor) try: rq = QUrl(uri) q = QUrlQuery() for (k, v) in params.items(): q.addQueryItem(k, v) rq.setQuery(q) req = QNetworkRequest(rq) try: reply = self.nominatim_networkAccessManager.blockingGet(req) resource = reply.content().data().decode('utf8') r = json.loads(resource) if (isinstance(r, list)): self.populateTable(r) else: self.populateTable([r]) except: self.tableResult.clearContents() finally: QgsApplication.restoreOverrideCursor() def searchJson(self, params, user, options, options2): contents = str(options).strip() items = contents.split(' ') for (k, v) in options2.items(): if k in ['viewbox']: params["bounded"] = "1" params[k] = v pairs = [] for item in items: pair = item.split('=', 1) if (pair != [''] and pair != [] and len(pair) > 1): pairs.append(pair) for (k, v) in pairs: if k in ['viewbox', 'countrycodes', 'limit', 'exclude_place_ids', 'addressdetails', 'exclude_place_ids', 'bounded', 'routewidth', 'osm_type', 'osm_id'] and not(k in options2.keys()): params[k] = v if k in ['viewbox']: params["bounded"] = "1" params["polygon_text"] = "1" params["format"] = "json" uri = 'https://nominatim.openstreetmap.org/search' self.getHttp(uri, params) def findNearbyJSON(self, params, user, options): uri = "https://nominatim.openstreetmap.org/reverse" params["format"] = "json" self.getHttp(uri, params) """ Gestion de l'évènement "leave", afin d'effacer l'objet sélectionné en sortie du dock """ def eventFilter(self, obj, event): typ = event.type() if typ == event.Leave: try: self.plugin.canvas.scene().removeItem(self.rubber) except: pass return False def __init__(self, parent, plugin): self.plugin = plugin QDockWidget.__init__(self, parent) self.setupUi(self) self.btnApply.setIcon(QIcon(":plugins/nominatim/arrow_green.png")) self.btnMask.setIcon(QIcon(":plugins/nominatim/add_mask.png")) self.btnLayer.setIcon(QIcon(":plugins/nominatim/add_layer.png")) self.tableResult.installEventFilter(self) # cf. eventFilter method self.tableResult.cellDoubleClicked.connect(self.onChoose) self.tableResult.cellEntered.connect(self.cellEntered) self.editSearch.returnPressed.connect(self.onReturnPressed) self.btnSearch.clicked.connect(self.onReturnPressed) self.btnApply.clicked.connect(self.onApply) self.btnHelp.clicked.connect(self.plugin.do_help) self.btnLocalize.clicked.connect(self.doLocalize) self.btnMask.clicked.connect(self.onMask) self.btnLayer.clicked.connect(self.onLayer) self.MultiPolygonLayerId = None self.LineLayerId = None self.PointLayerId = None try: self.cbExtent.setChecked(self.plugin.limitSearchToExtent) except: self.cbExtent.setChecked(self.plugin.limitSearchToExtent) self.currentExtent = self.plugin.canvas.extent() self.tableResult.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) try: self.editSearch.setText(self.plugin.lastSearch) except: pass try: if self.plugin.localiseOnStartup: self.doLocalize() except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, 'Extensions') pass self.nominatim_networkAccessManager = QgsNetworkAccessManager.instance() def cellEntered(self, row, col): item = self.tableResult.item(row, 0) try: self.plugin.canvas.scene().removeItem(self.rubber) self.showItem(item) except: pass def onLayer(self): for r in self.tableResult.selectedRanges(): item = self.tableResult.item(r.topRow(), 0) self.doLayer(item) def onMask(self): for r in self.tableResult.selectedRanges(): item = self.tableResult.item(r.topRow(), 0) self.doMask(item) def populateRow(self, item, idx): id = item['place_id'] name = item['display_name'] try: className = QApplication.translate("nominatim", item['class'], None) except: className = "" try: typeName = QApplication.translate("nominatim", item['type'], None) except: typeName = "" try: wkt = item['geotext'] except: wkt = None try: osm_type = item['osm_type'] except: osm_type = None bbox = {} if osm_type == "node": lat = item['lat'] lng = item['lon'] poFD = ogr.FeatureDefn("Point") poFD.SetGeomType(ogr.wkbPoint) oFLD = ogr.FieldDefn('id', ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn('name', ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) wkt = "POINT("+str(lng)+" "+str(lat)+")" ogrGeom = ogr.CreateGeometryFromWkt(wkt) else: try: bbox = item['boundingbox'] poFD = ogr.FeatureDefn("Rectangle") poFD.SetGeomType(ogr.wkbPolygon) oFLD = ogr.FieldDefn('id', ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn('name', ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) if wkt is None: wkt = "POLYGON(("+str(bbox[2])+" "+str(bbox[0])+", "+str(bbox[2])+" " +\ str(bbox[1])+", "+str(bbox[3])+" "+str(bbox[1])+", "+str(bbox[3])+" " +\ str(bbox[0])+", "+str(bbox[2])+" "+str(bbox[0])+"))" ogrGeom = ogr.CreateGeometryFromWkt(wkt) except: lat = item['lat'] lng = item['lon'] poFD = ogr.FeatureDefn("Point") poFD.SetGeomType(ogr.wkbPoint) oFLD = ogr.FieldDefn('id', ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn('name', ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) wkt = "POINT("+str(lng)+" "+str(lat)+")" ogrGeom = ogr.CreateGeometryFromWkt(wkt) mapCrsWKT = self.plugin.canvas.mapSettings().destinationCrs().toWkt() sourceSRS = osr.SpatialReference() sourceSRS.ImportFromEPSG(4326) targetSRS = osr.SpatialReference() targetSRS.ImportFromWkt(str(mapCrsWKT)) trsf = osr.CoordinateTransformation(sourceSRS, targetSRS) try: ogrGeom.Transform(trsf) except TypeError as e: QgsMessageLog.logMessage("Nominatim - transformation error. Check map projection.", "Extensions") ogrFeature.SetGeometry(ogrGeom) ogrFeature.SetFID(int(idx+1)) ogrFeature.SetField(str('id'), str(id)) ogrFeature.SetField(str('name'), name) item = QTableWidgetItem(name) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) item.setData(Qt.UserRole, ogrFeature) self.tableResult.setItem(idx, 0, item) itemLibelle = QTableWidgetItem(className) itemLibelle.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableResult.setItem(idx, 1, itemLibelle) itemType = QTableWidgetItem(typeName) itemType.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableResult.setItem(idx, 2, itemType) def populateTable(self, r): idx = 0 self.tableResult.clearContents() self.tableResult.setRowCount(len(r)) for item in r: self.populateRow(item, idx) idx = idx+1 def doLocalize(self): try: # center bbox = self.plugin.canvas.extent() sourceCrs = self.plugin.canvas.mapSettings().destinationCrs() targetCrs = QgsCoordinateReferenceSystem() targetCrs.createFromSrid(4326) xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance()) bbox = xform.transform(bbox) params = {"lon": str(bbox.center().x()), "lat": str(bbox.center().y()), "zoom": "10"} self.findNearbyJSON(params, self.plugin.gnUsername, self.plugin.gnOptions) except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, 'Extensions') pass def onReturnPressed(self): try: txt = self.editSearch.text().strip() self.plugin.lastSearch = self.editSearch.text() self.plugin.limitSearchToExtent = (self.cbExtent.isChecked()) options = self.plugin.gnOptions options2 = {} if self.plugin.limitSearchToExtent: sourceCrs = self.plugin.canvas.mapSettings().destinationCrs() targetCrs = QgsCoordinateReferenceSystem() targetCrs.createFromSrid(4326) xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance()) geom = xform.transform(self.plugin.canvas.extent()) options2 = {'viewbox': str(geom.xMinimum()) + ',' + str(geom.yMaximum()) + ',' + str(geom.xMaximum()) + ',' + str(geom.yMinimum())} params = {'q': txt, 'addressdetails': '0'} self.searchJson(params, self.plugin.gnUsername, options, options2) except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, 'Extensions') pass def onChoose(self, row, col): item = self.tableResult.item(row, 0) self.go(item) def onApply(self): for item in self.tableResult.selectedItems(): self.go(item) break def getBBox(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint): mapextent = self.plugin.canvas.extent() ww = mapextent.width()/100 mapcrs = self.plugin.canvas.mapSettings().destinationCrs() x = geom.boundingBox().center().x() y = geom.boundingBox().center().y() ww = 50.0 if mapcrs.mapUnits() == QgsUnitTypes.DistanceFeet: ww = 150 if mapcrs.mapUnits() == QgsUnitTypes.DistanceDegrees: ww = 0.0005 bbox = QgsRectangle(x-10*ww, y-10*ww, x+10*ww, y+10*ww) return bbox else: bbox = geom.boundingBox() rubberRect = QgsRectangle(bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()) return rubberRect def showItem(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint): self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PointGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(2) self.rubber.setToGeometry(geom, None) else: # dont show if it is larger than the canvas if self.plugin.canvas.extent().contains(geom.boundingBox()): pass else: geom = geom.intersection(QgsGeometry.fromRect(self.plugin.canvas.extent())) self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PolygonGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setWidth(4) self.rubber.setToGeometry(geom, None) def go(self, item, zoom=True): try: self.plugin.canvas.scene().removeItem(self.rubber) except: pass if zoom: bbox = self.getBBox(item) self.plugin.canvas.setExtent(bbox) self.plugin.canvas.refresh() self.showItem(item) def doMask(self, item): mapcrs = self.plugin.canvas.mapSettings().destinationCrs() ogrFeature = item.data(Qt.UserRole) layerName = "OSM "+ogrFeature.GetFieldAsString('id') geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) if (geom.type() == QgsWkbTypes.PolygonGeometry): try: try: from mask import aeag_mask except: from mask_plugin import aeag_mask aeag_mask.do(mapcrs, {geom}, "Mask "+layerName) except: geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) toCrs = self.plugin.canvas.mapSettings().destinationCrs() l = max(geom.boundingBox().width(), geom.boundingBox().height()) x = geom.boundingBox().center().x() y = geom.boundingBox().center().y() rect = QgsRectangle(x-l, y-l, x+l, y+l) # geom.boundingBox() rect.scale(4) mask = QgsGeometry.fromRect(rect) mask = mask.difference(geom) maskLayer = QgsVectorLayer("MultiPolygon", "Mask "+layerName, "memory") maskLayer.setCrs(toCrs) QgsProject.instance().addMapLayer(maskLayer) pr = maskLayer.dataProvider() fields = QgsFields() fields.append(QgsField("id", QVariant.String)) fields.append(QgsField("name", QVariant.String)) fet = QgsFeature() fet.initAttributes(2) fet.setGeometry(mask) fet.setFields(fields) fet.setAttribute("id", (ogrFeature.GetFieldAsString('id'))) fet.setAttribute("name", (ogrFeature.GetFieldAsString('name'))) pr.addAttributes(fields.toList()) maskLayer.startEditing() pr.addFeatures([fet]) maskLayer.commitChanges() maskLayer.updateExtents() # transparence, epaisseur renderer = maskLayer.renderer() s = renderer.symbol() s.setOpacity(0.90) s.setColor(QColor(255, 255, 255)) if isinstance(s, QgsLineSymbol): s.setWidth(0) layerTree = QgsProject.instance().layerTreeRoot().findLayer(maskLayer) if layerTree: self.plugin.iface.layerTreeView().layerTreeModel()\ .refreshLayerLegend(layerTree) # Refresh legend self.go(item) def doLayer(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) fields = QgsFields() fields.append(QgsField("id", QVariant.String)) fields.append(QgsField("name", QVariant.String)) fet = QgsFeature() fet.initAttributes(2) fet.setFields(fields) fet.setGeometry(geom) fet.setAttribute("id", (ogrFeature.GetFieldAsString('id'))) fet.setAttribute("name", (ogrFeature.GetFieldAsString('name'))) vl = None if not self.plugin.singleLayer: if geom.type() == QgsWkbTypes.PolygonGeometry: layerName = "OSMPlaceSearch Polygon" layerId = self.MultiPolygonLayerId if geom.type() == QgsWkbTypes.LineGeometry: layerName = "OSMPlaceSearch Line" layerId = self.LineLayerId if geom.type() == QgsWkbTypes.PointGeometry: layerName = "OSMPlaceSearch Point" layerId = self.PointLayerId vl = QgsProject.instance().mapLayer(layerId) if vl is not None: pr = vl.dataProvider() else: if geom.type() == QgsWkbTypes.PolygonGeometry: vl = QgsVectorLayer("MultiPolygon", layerName, "memory") self.MultiPolygonLayerId = vl.id() if geom.type() == QgsWkbTypes.LineGeometry: vl = QgsVectorLayer("MultiLineString", layerName, "memory") self.LineLayerId = vl.id() if geom.type() == QgsWkbTypes.PointGeometry: vl = QgsVectorLayer("Point", layerName, "memory") self.PointLayerId = vl.id() if vl is not None: pr = vl.dataProvider() # ajout de champs pr.addAttributes(fields.toList()) QgsProject.instance().addMapLayer(vl) else: layerName = "OSM "+ogrFeature.GetFieldAsString('id') # creer une nouvelle couche si n'existe pas encore if geom.type() == QgsWkbTypes.PolygonGeometry: vl = QgsVectorLayer("MultiPolygon", layerName, "memory") if geom.type() == QgsWkbTypes.LineGeometry: vl = QgsVectorLayer("MultiLineString", layerName, "memory") if geom.type() == QgsWkbTypes.PointGeometry: vl = QgsVectorLayer("Point", layerName, "memory") if vl is not None: pr = vl.dataProvider() # ajout de champs pr.addAttributes(fields.toList()) QgsProject.instance().addMapLayer(vl) if vl is not None: vl.setProviderEncoding('UTF-8') vl.startEditing() pr.addFeatures([fet]) vl.commitChanges() # mise a jour etendue de la couche vl.updateExtents() layerTree = QgsProject.instance().layerTreeRoot().findLayer(vl) if layerTree: self.plugin.iface.layerTreeView()\ .layerTreeModel().refreshLayerLegend(layerTree) # Refresh legend self.go(item, False)
class TrackWidget(QWidget, Ui_Track): def __init__(self, parent=None): super(TrackWidget, self).__init__(parent) self.setupUi(self) self.title = "Display track" self.canvas = None self.band = None self.color_btn = None self.center = False self.position = None self.geom_type = None self.marker = None self.hidden = False self.centerButton.setEnabled(False) icon = QIcon(":/resources/mActionSave.svg") self.save_track_pushButton.setIcon(icon) self.save_track_pushButton.setToolTip("Save Track") self.save_track_pushButton.clicked.connect(self.save_track) def init(self, title, canvas, default_color, geom_type, marker): self.canvas = canvas self.geom_type = geom_type self.track_groupBox.setTitle(title) if marker: # Add marker self.marker = marker self.with_marker = True else: self.with_marker = False # Add rubber band self.band = QgsRubberBand(self.canvas, self.geom_type) if self.geom_type == QgsWkbTypes.PointGeometry: self.band.setIcon(QgsRubberBand.ICON_CIRCLE) self.band.setIconSize(12) else: self.band.setWidth(3) # Add color button widget for picking color self.color_btn = QgsColorButton() self.horizontal_layout_color.insertWidget(1, self.color_btn, 0, Qt.AlignLeft) self.color_btn.colorChanged.connect(self.color_changed) self.color_btn.setDefaultColor(default_color) self.color_btn.setColor(default_color) # Set signals self.centerButton.clicked.connect(self.center_to_location) self.clearTrackButton.clicked.connect(self.clear_track) def color_changed(self): transparent_color = QColor(self.color_btn.color()) transparent_color.setAlpha(80) self.band.setColor(transparent_color) if self.with_marker: self.marker.set_color(QColor(self.color_btn.color())) def add_position(self, position): self.band.addPoint(position) def track_update_canvas(self, position, heading): self.centerButton.setEnabled(True) self.position = position self.add_position(position) if self.with_marker: self.marker.set_center(position, heading) if self.isHidden(): if self.with_marker: self.marker.hide() self.band.hide() else: if self.with_marker: self.marker.show() self.band.show() def center_to_location(self): """ Center to last received position on the map. """ rect = QgsRectangle(self.position, self.position) self.canvas.setExtent(rect) self.canvas.zoomScale(400) self.canvas.refresh() def hide_band(self): self.band.hide() def hide_marker(self): if self.with_marker: self.marker.hide() def clear_track(self): self.band.reset(self.geom_type) def save_track(self): """ Save the track to disk """ layer_name, selected_filter = QFileDialog.getSaveFileName(None, 'Save Track', "", 'Shapefile (*.shp);;KML (*.kml);;GPX (*.gpx)') if layer_name != '': if self.geom_type == QgsWkbTypes.PointGeometry: geometric_object = "MultiPoint?crs=epsg:4326" else: geometric_object = "LineString?crs=epsg:4326" layer = QgsVectorLayer( geometric_object, layer_name, "memory") feature = QgsFeature() feature.setGeometry(self.band.asGeometry()) layer.dataProvider().addFeatures([feature]) if selected_filter == "Shapefile (*.shp)": if not layer_name.endswith('.shp'): layer_name = layer_name + '.shp' ret = QgsVectorFileWriter.writeAsVectorFormat(layer, layer_name, "utf-8", QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId), "ESRI Shapefile") if ret == QgsVectorFileWriter.NoError: logger.info(layer.name() + " saved to " + layer_name) elif selected_filter == "KML (*.kml)": if not layer_name.endswith('.kml'): layer_name = layer_name + '.kml' QgsVectorFileWriter.writeAsVectorFormat(layer, layer_name, "utf-8", QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId), "KML") elif selected_filter == "GPX (*.gpx)": if not layer_name.endswith('.gpx'): layer_name = layer_name + '.gpx' ds_options = list() ds_options.append("GPX_USE_EXTENSIONS=TRUE") QgsVectorFileWriter.writeAsVectorFormat(layer, layer_name, "utf-8", QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId), "GPX", datasourceOptions=ds_options) def close(self): self.hide_band() self.hide_marker()
class InterpolateTool(QgsMapToolAdvancedDigitizing): """ Map tool class to interpolate an elevation in the middle of a segment """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/interpolate_icon.png' self.text = QCoreApplication.translate( "VDLTools", "Interpolate the elevation of a vertex and a point in the middle of a line" ) self.__layer = None self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__lastFeatureId = None self.__layerList = None self.__lastLayer = None self.__confDlg = None self.__mapPoint = None self.__rubber = None self.__selectedFeature = None self.__findVertex = False def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) self.__updateList() self.__rubber = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setWidth(2) self.__rubber.setIconSize(20) self.canvas().layersChanged.connect(self.__updateList) self.canvas().scaleChanged.connect(self.__updateList) self.setMode(self.CaptureLine) def deactivate(self): """ When the action is deselected """ self.__done() self.__cancel() self.__rubber = None Signal.safelyDisconnect(self.canvas().layersChanged, self.__updateList) Signal.safelyDisconnect(self.canvas().scaleChanged, self.__updateList) QgsMapToolAdvancedDigitizing.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def __done(self): """ When the edition is finished """ self.__isEditing = False self.__confDlg = None self.__mapPoint = None def __cancel(self): """ To cancel used variables """ self.__findVertex = False if self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastLayer = None if self.__rubber is not None: self.__rubber.reset() self.__lastFeatureId = None self.__selectedFeature = None def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type( ) == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.action().setEnabled(False) self.__removeLayer() def __updateList(self): """ To update the line layers list that we can use for interpolation """ self.__layerList = [] for layer in self.canvas().layers(): if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType() \ and layer.geometryType() == QGis.Line: self.__layerList.append( QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.All, 10, QgsTolerance.Pixels)) def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__done() self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and self.__layerList is not None: f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), self.__layerList) if f_l is not None and self.__lastFeatureId != f_l[0].id(): f = f_l[0] self.__lastFeatureId = f.id() if self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastLayer = f_l[1] self.__lastLayer.setSelectedFeatures([f.id()]) if f_l is None and self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: self.__rubber.reset() snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All) match = Finder.snap(map_point, self.canvas(), snap_layers) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): self.__rubber.setIcon(4) self.__rubber.setToGeometry( QgsGeometry().fromPoint(point), None) else: intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: self.__rubber.setIcon(1) self.__rubber.setToGeometry( QgsGeometry().fromPoint(intersection), None) if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: self.__rubber.setIcon(1) self.__rubber.setToGeometry( QgsGeometry().fromPoint(intersection), None) elif self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): self.__rubber.setIcon(3) self.__rubber.setToGeometry( QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if self.__lastLayer is not None and not self.__findVertex: found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] self.__iface.messageBar().pushMessage( QCoreApplication.translate( "VDLTools", "Select the position for interpolation (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.setMode(self.CaptureNone) self.__findVertex = True elif self.__findVertex: self.__rubber.reset() snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All) match = Finder.snap(event.mapPoint(), self.canvas(), snap_layers) if match.hasVertex() or match.hasEdge(): point = match.point() ok = False noVertex = False if match.hasVertex(): if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): ok = True noVertex = True else: intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: point = intersection ok = True if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: point = intersection ok = True elif self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): ok = True if ok: self.__isEditing = True self.__findVertex = False self.__mapPoint = point if noVertex: self.__ok(False, True) else: self.__confDlg = InterpolateConfirmDialog() if self.__lastLayer.isEditable(): self.__confDlg.setMainLabel( QCoreApplication.translate( "VDLTools", "What do you want to do ?")) self.__confDlg.setAllLabel( QCoreApplication.translate( "VDLTools", "Create point and new vertex")) self.__confDlg.setVtLabel( QCoreApplication.translate( "VDLTools", "Create only the vertex")) self.__confDlg.rejected.connect(self.__done) self.__confDlg.okButton().clicked.connect( self.__onConfirmOk) self.__confDlg.cancelButton().clicked.connect( self.__onConfirmCancel) self.__confDlg.show() else: self.__done() self.__cancel() def __onConfirmCancel(self): """ When the Cancel button in Interpolate Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmOk(self): """ When the Ok button in Interpolate Confirm Dialog is pushed """ checkedId = self.__confDlg.getCheckedId() self.__confDlg.accept() withVertex = True withPoint = True if checkedId == 1: withVertex = False else: if not self.__lastLayer.isEditable(): self.__lastLayer.startEditing() if checkedId == 2: withPoint = False self.__ok(withVertex, withPoint) def __ok(self, withVertex, withPoint): """ To realize the interpolation :param withVertex: if we want a new interpolated vertex :param withPoint: if we want a new interpolated point """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry(), self.__iface) vertex_v2 = QgsPointV2() vertex_id = QgsVertexId() line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2, vertex_id, 0) x0 = line_v2.xAt(vertex_id.vertex - 1) y0 = line_v2.yAt(vertex_id.vertex - 1) d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y()) x1 = line_v2.xAt(vertex_id.vertex) y1 = line_v2.yAt(vertex_id.vertex) d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y()) z0 = line_v2.zAt(vertex_id.vertex - 1) z1 = line_v2.zAt(vertex_id.vertex) vertex_v2.addZValue(old_div((d0 * z1 + d1 * z0), (d0 + d1))) if withPoint: pt_feat = QgsFeature(self.__layer.pendingFields()) pt_feat.setGeometry(QgsGeometry(vertex_v2)) for i in range(len(self.__layer.pendingFields())): # default = self.__layer.defaultValue(i, pt_feat) # if default is not None: # print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default) # print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i)) e = QgsExpression(self.__layer.defaultValueExpression(i)) default = e.evaluate(pt_feat) pt_feat.setAttribute(i, default) if self.__layer.editFormConfig().suppress( ) == QgsEditFormConfig.SuppressOn: self.__layer.addFeature(pt_feat) else: self.__iface.openFeatureForm(self.__layer, pt_feat) if withVertex: line_v2.insertVertex(vertex_id, vertex_v2) self.__lastLayer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) else: self.__selectedFeature = found_features[0] else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No more feature selected"), level=QgsMessageBar.INFO) self.__iface.mapCanvas().refresh() self.__done() self.__findVertex = True
class wincan2qgep(QObject): name = u"&Wincan 2 QGEP" actions = None def __init__(self, iface): QObject.__init__(self) self.iface = iface self.actions = {} self.settings = MySettings() self.dlg = None # translation environment self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(self.plugin_dir, 'i18n', 'wincan2qgep_{0}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) def initGui(self): self.actions['openInspection'] = QAction( QIcon(":/plugins/wincan2qgep/icons/wincan_logo.png"), self.tr(u"Ouvrir une inspection"), self.iface.mainWindow()) self.actions['openInspection'].triggered.connect(self.openInspection) self.iface.addPluginToMenu(self.name, self.actions['openInspection']) self.iface.addToolBarIcon(self.actions['openInspection']) self.actions['showSettings'] = QAction( QIcon(":/plugins/wincan2qgep/icons/settings.svg"), self.tr(u"&Settings"), self.iface.mainWindow()) self.actions['showSettings'].triggered.connect(self.showSettings) self.iface.addPluginToMenu(self.name, self.actions['showSettings']) self.actions['help'] = QAction( QIcon(":/plugins/wincan2qgep/icons/help.svg"), self.tr("Help"), self.iface.mainWindow()) self.actions['help'].triggered.connect(lambda: QDesktopServices().openUrl(QUrl("http://3nids.github.io/wincan2qgep"))) self.iface.addPluginToMenu(self.name, self.actions['help']) self.rubber = QgsRubberBand(self.iface.mapCanvas()) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) def unload(self): """ Unload plugin """ for action in self.actions.itervalues(): self.iface.removePluginMenu(self.name, action) self.iface.removeToolBarIcon(action) if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber if self.dlg: self.dlg.close() @pyqtSlot(str, QgsMessageBar.MessageLevel) def displayMessage(self, message, level): self.iface.messageBar().pushMessage("Wincan 2 QGEP", message, level) def showSettings(self): if ConfigurationDialog().exec_(): self._reloadFinders() def openInspection(self): xmlPath = self.settings.value('xmlPath') if xmlPath == '': xmlPath = QgsProject.instance().homePath() filepath = QFileDialog.getOpenFileName(None, "Open WIncan inspection data", xmlPath, "Wincan file (*.xml)") #filepath = '/var/run/user/1000/gvfs/smb-share:server=s4laveyre.sige.ch,share=inspection_tv/SIGE_2014/Rapport 2014/SIGE 5004B 14/XML/Project.xml' if filepath: self.settings.set_value('xmlPath', os.path.dirname(os.path.realpath(filepath))) data = ImportData(filepath).data self.dlg = DataBrowserDialog(self.iface, data) self.dlg.show()
class quickFinder(QObject): name = u"&Quick Finder" actions = None toolbar = None finders = {} loadingIcon = None def __init__(self, iface): QObject.__init__(self) self.iface = iface self.actions = {} self.finders = {} self.settings = MySettings() self._initFinders() self.iface.projectRead.connect(self._reloadFinders) self.iface.newProjectCreated.connect(self._reloadFinders) # translation environment self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(self.plugin_dir, 'i18n', 'quickfinder_{0}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) def initGui(self): self.actions['showSettings'] = QAction( QIcon(":/plugins/quickfinder/icons/settings.svg"), self.tr(u"&Settings"), self.iface.mainWindow()) self.actions['showSettings'].triggered.connect(self.showSettings) self.iface.addPluginToMenu(self.name, self.actions['showSettings']) self.actions['help'] = QAction( QIcon(":/plugins/quickfinder/icons/help.svg"), self.tr("Help"), self.iface.mainWindow()) self.actions['help'].triggered.connect( lambda: QDesktopServices().openUrl( QUrl("http://3nids.github.io/quickfinder"))) self.iface.addPluginToMenu(self.name, self.actions['help']) self._initToolbar() self.rubber = QgsRubberBand(self.iface.mapCanvas()) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) def unload(self): """ Unload plugin """ for key in self.finders.keys(): self.finders[key].close() for action in self.actions.itervalues(): self.iface.removePluginMenu(self.name, action) if self.toolbar: del self.toolbar if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def _initToolbar(self): """setup the plugin toolbar.""" self.toolbar = self.iface.addToolBar(self.name) self.toolbar.setObjectName('mQuickFinderToolBar') self.searchAction = QAction(QIcon(":/plugins/quickfinder/icons/magnifier13.svg"), self.tr("Search"), self.toolbar) self.stopAction = QAction( QIcon(":/plugins/quickfinder/icons/wrong2.svg"), self.tr("Cancel"), self.toolbar) self.finderBox = FinderBox(self.finders, self.iface, self.toolbar) self.finderBox.searchStarted.connect(self.searchStarted) self.finderBox.searchFinished.connect(self.searchFinished) self.finderBoxAction = self.toolbar.addWidget(self.finderBox) self.finderBoxAction.setVisible(True) self.searchAction.triggered.connect(self.finderBox.search) self.toolbar.addAction(self.searchAction) self.stopAction.setVisible(False) self.stopAction.triggered.connect(self.finderBox.stop) self.toolbar.addAction(self.stopAction) self.toolbar.setVisible(True) def _initFinders(self): self.finders['geomapfish'] = GeomapfishFinder(self) self.finders['osm'] = OsmFinder(self) self.finders['project'] = ProjectFinder(self) for key in self.finders.keys(): self.finders[key].message.connect(self.displayMessage) self.refreshProject() def _reloadFinders(self): for key in self.finders.keys(): self.finders[key].close() self.finders[key].reload() self.refreshProject() @pyqtSlot(str, QgsMessageBar.MessageLevel) def displayMessage(self, message, level): self.iface.messageBar().pushMessage("QuickFinder", message, level) def showSettings(self): if ConfigurationDialog().exec_(): self._reloadFinders() def searchStarted(self): self.searchAction.setVisible(False) self.stopAction.setVisible(True) def searchFinished(self): self.searchAction.setVisible(True) self.stopAction.setVisible(False) def refreshProject(self): if not self.finders['project'].activated: return if not self.settings.value("refreshAuto"): return nDays = self.settings.value("refreshDelay") # do not ask more ofen than 3 days askLimit = min(3, nDays) recentlyAsked = self.settings.value("refreshLastAsked") >= nDaysAgoIsoDate(askLimit) if recentlyAsked: return threshDate = nDaysAgoIsoDate(nDays) uptodate = True for search in self.finders['project'].searches.values(): if search.dateEvaluated <= threshDate: uptodate = False break if uptodate: return self.settings.setValue("refreshLastAsked", nDaysAgoIsoDate(0)) ret = QMessageBox(QMessageBox.Warning, "Quick Finder", QCoreApplication.translate("Auto Refresh", "Some searches are outdated. Do you want to refresh them ?"), QMessageBox.Cancel | QMessageBox.Yes).exec_() if ret == QMessageBox.Yes: RefreshDialog(self.finders['project']).exec_()
class ShLocatorFilter(QgsLocatorFilter): HEADERS = {b'User-Agent': b'Mozilla/5.0 QGIS ShLocator Filter'} message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget) def __init__(self, iface: QgisInterface = None): """" :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise """ super().__init__() self.iface = iface self.settings = Settings() # following properties will only be used in main thread self.rubber_band = None self.map_canvas: QgsMapCanvas = None self.transform_ch = None self.current_timer = None self.result_found = False self.nam_fetch_feature = None if iface is not None: # happens only in main thread self.map_canvas = iface.mapCanvas() self.map_canvas.destinationCrsChanged.connect( self.create_transforms) self.rubber_band = QgsRubberBand( self.map_canvas, QgsWkbTypes.PolygonGeometry) self.rubber_band.setColor(QColor(255, 50, 50, 200)) self.rubber_band.setFillColor(QColor(255, 255, 50, 160)) self.rubber_band.setBrushStyle(Qt.SolidPattern) self.rubber_band.setLineStyle(Qt.SolidLine) self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE) self.rubber_band.setIconSize(15) self.rubber_band.setWidth(4) self.rubber_band.setBrushStyle(Qt.NoBrush) self.create_transforms() def name(self): return 'ShLocator' def clone(self): return ShLocatorFilter() def priority(self): return QgsLocatorFilter.Highest def displayName(self): return 'ShLocator' def prefix(self): return 'shl' def clearPreviousResults(self): if self.rubber_band: self.rubber_band.reset(QgsWkbTypes.PointGeometry) if self.current_timer is not None: self.current_timer.stop() self.current_timer.deleteLater() self.current_timer = None def hasConfigWidget(self): return True def openConfigWidget(self, parent=None): dlg = ConfigDialog(parent) dlg.exec_() def create_transforms(self): # this should happen in the main thread src_crs_ch = QgsCoordinateReferenceSystem('EPSG:2056') assert src_crs_ch.isValid() dst_crs = self.map_canvas.mapSettings().destinationCrs() self.transform_ch = QgsCoordinateTransform( src_crs_ch, dst_crs, QgsProject.instance()) @staticmethod def url_with_param(url, params) -> str: url = QUrl(url) q = QUrlQuery(url) for key, value in params.items(): q.addQueryItem(key, value) url.setQuery(q) return url.url() def fetchResults(self, search: str, context: QgsLocatorContext, feedback: QgsFeedback): try: dbg_info("start shlocator search...") if len(search) < 3: return self.result_found = False params = { 'query': str(search), 'maxfeatures': str(self.settings.value('max_features')) } nam = NetworkAccessManager() feedback.canceled.connect(nam.abort) url = self.url_with_param( self.settings.value('service_url'), params) dbg_info(url) try: (response, content) = nam.request( url, headers=self.HEADERS, blocking=True) self.handle_response(response, search) except RequestsExceptionUserAbort: pass except RequestsException as err: info(err, Qgis.Info) if not self.result_found: result = QgsLocatorResult() result.filter = self result.displayString = self.tr('No result found.') result.userData = NoResult self.resultFetched.emit(result) except Exception as e: info(e, Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) info(traceback.print_exception( exc_type, exc_obj, exc_traceback), Qgis.Critical) def data_product_qgsresult(self, data: dict) -> QgsLocatorResult: result = QgsLocatorResult() result.filter = self result.displayString = data['display'] result.group = 'Karten' result.userData = DataProductResult( type=data['type'], dataproduct_id=data['dataproduct_id'], display=data['display'], dset_info=data['dset_info'], sublayers=data.get('sublayers', None) ) data_product = 'dataproduct' data_type = data['type'] result.icon, result.description = dataproduct2icon_description( data_product, data_type) return result def handle_response(self, response, search_text: str): try: if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): info("Error in main response with status code: " "{} from {}".format(response.status_code, response.url)) return display_name_field = QgsField('display_name', QVariant.String) fields = QgsFields() fields.append(display_name_field) features = QgsJsonUtils.stringToFeatureList(response.content.decode('utf-8'), fields, QTextCodec.codecForName('UTF-8')) dbg_info('Found {} features'.format(len(features))) dbg_info('Data {}'.format(response.content.decode('utf-8'))) for feature in features: dbg_info('Adding feature {}'.format(feature['display_name'])) result = QgsLocatorResult() result.filter = self result.group = 'Objekte' result.displayString = feature['display_name'] result.userData = FeatureResult(feature) self.resultFetched.emit(result) self.result_found = True except Exception as e: info(str(e), Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) info(traceback.print_exception( exc_type, exc_obj, exc_traceback), Qgis.Critical) def triggerResult(self, result: QgsLocatorResult): # this is run in the main thread, i.e. map_canvas is not None self.clearPreviousResults() if type(result.userData) == NoResult: pass elif type(result.userData) == FeatureResult: self.highlight(result.userData.feature.geometry()) else: info('Incorrect result. Please contact support', Qgis.Critical) def highlight(self, geometry: QgsGeometry): self.rubber_band.reset(geometry.type()) self.rubber_band.addGeometry(geometry, None) rect = geometry.boundingBox() if not self.settings.value('keep_scale'): if rect.isEmpty(): current_extent = self.map_canvas.extent() rect = current_extent.scaled(self.settings.value( 'point_scale')/self.map_canvas.scale(), rect.center()) else: rect.scale(4) self.map_canvas.setExtent(rect) self.map_canvas.refresh() # self.current_timer = QTimer() # self.current_timer.timeout.connect(self.clearPreviousResults) # self.current_timer.setSingleShot(True) # self.current_timer.start(5000) def parse_feature_response(self, response): if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): info("Error in feature response with status code: " "{} from {}".format(response.status_code, response.url)) return data = json.loads(response.content.decode('utf-8')) geometry_type = data['geometry']['type'] geometry = QgsGeometry() if geometry_type == 'Point': geometry = QgsGeometry.fromPointXY(QgsPointXY(data['geometry']['coordinates'][0], data['geometry']['coordinates'][1])) elif geometry_type == 'Polygon': rings = data['geometry']['coordinates'] for r in range(0, len(rings)): for p in range(0, len(rings[r])): rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1]) geometry = QgsGeometry.fromPolygonXY(rings) elif geometry_type == 'MultiPolygon': islands = data['geometry']['coordinates'] for i in range(0, len(islands)): for r in range(0, len(islands[i])): for p in range(0, len(islands[i][r])): islands[i][r][p] = QgsPointXY( islands[i][r][p][0], islands[i][r][p][1]) geometry = QgsGeometry.fromMultiPolygonXY(islands) else: # ShLocator does not handle {geometry_type} yet. Please contact support info('ShLocator unterstützt den Geometrietyp {geometry_type} nicht.' ' Bitte kontaktieren Sie den Support.'.format(geometry_type=geometry_type), Qgis.Warning) geometry.transform(self.transform_ch) self.highlight(geometry)
class QgepMapToolConnectNetworkElements(QgsMapTool): """ This map tool connects wastewater networkelements. It works on two lists of layers: source layers with fields with a foreign key to a networkelement target layers which depict networkelements (reaches and network nodes) The tool will snap to source layers first and once one is chosen to a target layer. It will then ask which field(s) should be connected and perform the update on the database """ def __init__(self, iface, action): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.action = action self.rbline = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rbline.setColor(QColor('#f4530e')) self.rbline.setWidth(3) self.rbmarkers = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rbmarkers.setColor(QColor('#f4530e')) self.rbmarkers.setIconSize(6) self.source_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.target_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.source_feature = QgsFeature() self.rb_source_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_source_feature.setColor(QColor('#f49e79')) self.rb_source_feature.setWidth(3) self.target_feature = QgsFeature() self.rb_target_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_target_feature.setColor(QColor('#f49e79')) self.rb_target_feature.setWidth(3) def activate(self): """ Called by QGIS whenever the tool is activated. """ # A dict of layers # and for each layer the fields to use as foreign key # as well as the possible target layers # Reaches can be connected to reaches and nodes # Catchment areas only to nodes self.network_element_sources = { QgepLayerManager.layer('vw_qgep_reach'): { 'fields': [ ('rp_to_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point To')), ('rp_from_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point From')) ], 'target_layers': [ QgepLayerManager.layer('vw_wastewater_node'), QgepLayerManager.layer('vw_qgep_reach') ]}, QgepLayerManager.layer('od_catchment_area'): {'fields': [ ('fk_wastewater_networkelement_rw_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater current')), ('fk_wastewater_networkelement_rw_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater planned')), ('fk_wastewater_networkelement_ww_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater current')), ('fk_wastewater_networkelement_ww_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater planned')) ], 'target_layers': [ QgepLayerManager.layer('vw_wastewater_node') ]} } self.setSnapLayers(self.source_snapper, self.network_element_sources.keys()) self.reset() self.action.setChecked(True) self.iface.mapCanvas().setCursor(QCursor(Qt.CrossCursor)) def setSnapLayers(self, snapper, layers): snap_layers = list() for layer in layers: if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) snap_layers.append(snap_layer) snapper.setLayers(snap_layers) snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) def canvasMoveEvent(self, event): """ When the mouse moves, update the rubberbands. """ pt = event.originalMapPoint() snap_match = self.snapper.snapToMap(pt) if snap_match.isValid(): if snap_match.type() != QgsPointLocator.Area: pt = snap_match.point() self.matchpoint = pt if self.source_match: # There is already a source feature : snap to target feature # candidates if self.target_feature.id() != snap_match.featureId(): self.target_feature = self.get_feature_for_match( snap_match) self.rb_target_feature.setToGeometry( self.target_feature.geometry(), snap_match.layer()) self.rb_target_feature.show() self.rbmarkers.movePoint(pt) else: # Snapped to source feature, update source feature rubber band # and target layer snapper if self.source_feature.id() != snap_match.featureId(): self.source_feature = self.get_feature_for_match( snap_match) self.rb_source_feature.setToGeometry( self.source_feature.geometry(), snap_match.layer()) self.setSnapLayers(self.target_snapper, self.network_element_sources[ snap_match.layer()]['target_layers']) self.rb_source_feature.show() self.rbmarkers.movePoint(pt, 0) self.rbmarkers.show() else: self.rbmarkers.hide() if self.source_match: self.rb_target_feature.hide() else: self.rb_source_feature.hide() self.rbline.movePoint(pt) self.snapresult = snap_match def canvasReleaseEvent(self, event): """ On a click update the rubberbands and the snapping results if it's a left click. Reset if it's a right click. """ if event.button() == Qt.LeftButton: if self.snapresult.isValid(): if self.source_match: self.connect_features(self.source_match, self.snapresult) else: self.rbline.show() self.rbline.addPoint(self.matchpoint) self.source_match = self.snapresult self.snapper = self.target_snapper else: self.reset() def deactivate(self): """ Called by QGIS whenever this tool is deactivated. """ self.reset() self.action.setChecked(False) def reset(self): """ Resets the tool to a pristine state """ self.source_match = None self.rbline.hide() self.rbline.reset() self.rbmarkers.hide() self.rbmarkers.reset(QGis.Point) self.rbmarkers.addPoint(QgsPoint()) self.snapresult = None self.source_match = None self.snapper = self.source_snapper self.source_feature = QgsFeature() self.target_feature = QgsFeature() self.rb_source_feature.reset() self.rb_target_feature.reset() def get_feature_for_match(self, match): """ Get the feature for a snapping result @param match: The QgsPointLocator.SnapMatch object @return: A feature """ return next(match.layer().getFeatures(QgsFeatureRequest().setFilterFid(match.featureId()))) def connect_features(self, source, target): """ Connects the source feature with the target feature. @param source: A QgsPointLocator.Match object. Its foreign key will be updated. A dialog will be opened which asks the user for which foreign key(s) he wants to update. @param target: A QgsPointLocator.Match object. This feature will be used as link target. Its obj_id attribute will be used as primary key. """ dlg = QDialog(self.iface.mainWindow()) dlg.setWindowTitle(self.tr('Select properties to connect')) dlg.setLayout(QFormLayout()) properties = list() for prop in self.network_element_sources[source.layer()]['fields']: cbx = QCheckBox(prop[1]) cbx.setObjectName(prop[0]) properties.append(cbx) dlg.layout().addWidget(cbx) btn_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(btn_box) btn_box.accepted.connect(dlg.accept) btn_box.rejected.connect(dlg.reject) source_feature = self.get_feature_for_match(source) target_feature = self.get_feature_for_match(target) if dlg.exec_(): for cbx in properties: if cbx.isChecked(): source_feature[cbx.objectName()] = target_feature['obj_id'] if source.layer().updateFeature(source_feature): self.iface.messageBar().pushMessage('QGEP', self.tr('Connected {} to {}').format( source_feature[ 'identifier'], target_feature['identifier']), QgsMessageBar.INFO, 5) else: self.iface.messageBar().pushMessage('QGEP', self.tr( 'Error connecting features'), QgsMessageBar.WARNING, 5) self.reset()
class QgepMapToolConnectNetworkElements(QgsMapTool): """ This map tool connects wastewater networkelements. It works on two lists of layers: source layers with fields with a foreign key to a networkelement target layers which depict networkelements (reaches and network nodes) The tool will snap to source layers first and once one is chosen to a target layer. It will then ask which field(s) should be connected and perform the update on the database """ def __init__(self, iface, action): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.action = action self.rbline = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rbline.setColor(QColor('#f4530e')) self.rbline.setWidth(3) self.rbmarkers = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rbmarkers.setColor(QColor('#f4530e')) self.rbmarkers.setIconSize(6) self.source_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.target_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.source_feature = QgsFeature() self.rb_source_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_source_feature.setColor(QColor('#f49e79')) self.rb_source_feature.setWidth(3) self.target_feature = QgsFeature() self.rb_target_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_target_feature.setColor(QColor('#f49e79')) self.rb_target_feature.setWidth(3) def activate(self): """ Called by QGIS whenever the tool is activated. """ source_snap_layers = list() target_snap_layers = list() # A dict of layers and the fields that are foreign keys # pointing to wastewater networkelements self.network_element_sources = { QgepLayerManager.layer('vw_qgep_reach'): [ ('rp_to_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point To')), ('rp_from_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point From')) ], QgepLayerManager.layer('od_catchment_area'): [ ('fk_wastewater_networkelement_rw_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater current')), ('fk_wastewater_networkelement_rw_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater planned')), ('fk_wastewater_networkelement_ww_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater current')), ('fk_wastewater_networkelement_ww_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater planned')) ] } # A list of layers that can be used as wastewater networkelement # targets self.network_element_targets = [ QgepLayerManager.layer('vw_wastewater_node'), QgepLayerManager.layer('vw_qgep_reach') ] for layer in self.network_element_sources.keys(): if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) source_snap_layers.append(snap_layer) for layer in self.network_element_targets: if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) target_snap_layers.append(snap_layer) self.source_snapper.setLayers(source_snap_layers) self.source_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) self.target_snapper.setLayers(target_snap_layers) self.target_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) self.reset() self.action.setChecked(True) self.iface.mapCanvas().setCursor(QCursor(Qt.CrossCursor)) def canvasMoveEvent(self, event): """ When the mouse moves, update the rubberbands. """ pt = event.originalMapPoint() snap_match = self.snapper.snapToMap(pt) if snap_match.isValid(): if snap_match.type() != QgsPointLocator.Area: pt = snap_match.point() self.matchpoint = pt if self.source_match: if self.target_feature.id() != snap_match.featureId(): self.target_feature = self.get_feature_for_match( snap_match) self.rb_target_feature.setToGeometry( self.target_feature.geometry(), snap_match.layer()) self.rb_target_feature.show() self.rbmarkers.movePoint(pt) else: if self.source_feature.id() != snap_match.featureId(): self.source_feature = self.get_feature_for_match( snap_match) self.rb_source_feature.setToGeometry( self.source_feature.geometry(), snap_match.layer()) self.rb_source_feature.show() self.rbmarkers.movePoint(pt, 0) self.rbmarkers.show() else: self.rbmarkers.hide() if self.source_match: self.rb_target_feature.hide() else: self.rb_source_feature.hide() self.rbline.movePoint(pt) self.snapresult = snap_match def canvasReleaseEvent(self, event): """ On a click update the rubberbands and the snapping results if it's a left click. Reset if it's a right click. """ if event.button() == Qt.LeftButton: if self.snapresult.isValid(): if self.source_match: self.connect_features(self.source_match, self.snapresult) else: self.rbline.show() self.rbline.addPoint(self.matchpoint) self.source_match = self.snapresult self.snapper = self.target_snapper else: self.reset() def deactivate(self): """ Called by QGIS whenever this tool is deactivated. """ self.reset() self.action.setChecked(False) def reset(self): """ Resets the tool to a pristine state """ self.source_match = None self.rbline.hide() self.rbline.reset() self.rbmarkers.hide() self.rbmarkers.reset(QGis.Point) self.rbmarkers.addPoint(QgsPoint()) self.snapresult = None self.source_match = None self.snapper = self.source_snapper self.source_feature = QgsFeature() self.target_feature = QgsFeature() self.rb_source_feature.reset() self.rb_target_feature.reset() def get_feature_for_match(self, match): """ Get the feature for a snapping result @param match: The QgsPointLocator.SnapMatch object @return: A feature """ return next(match.layer().getFeatures(QgsFeatureRequest().setFilterFid(match.featureId()))) def connect_features(self, source, target): """ Connects the source feature with the target feature. @param source: A QgsPointLocator.Match object. Its foreign key will be updated. A dialog will be opened which asks the user for which foreign key(s) he wants to update. @param target: A QgsPointLocator.Match object. This feature will be used as link target. Its obj_id attribute will be used as primary key. """ dlg = QDialog(self.iface.mainWindow()) dlg.setWindowTitle(self.tr('Select properties to connect')) dlg.setLayout(QFormLayout()) properties = list() for prop in self.network_element_sources[source.layer()]: cbx = QCheckBox(prop[1]) cbx.setObjectName(prop[0]) properties.append(cbx) dlg.layout().addWidget(cbx) btn_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(btn_box) btn_box.accepted.connect(dlg.accept) btn_box.rejected.connect(dlg.reject) source_feature = self.get_feature_for_match(source) target_feature = self.get_feature_for_match(target) if dlg.exec_(): for cbx in properties: if cbx.isChecked(): source_feature[cbx.objectName()] = target_feature['obj_id'] if source.layer().updateFeature(source_feature): self.iface.messageBar().pushMessage('QGEP', self.tr('Connected {} to {}').format( source_feature[ 'identifier'], target_feature['identifier']), QgsMessageBar.INFO, 5) else: self.iface.messageBar().pushMessage('QGEP', self.tr( 'Error connecting features'), QgsMessageBar.WARNING, 5) self.reset()
class Geo360Dialog(QWidget, Ui_orbitalDialog): """QGIS Plugin Implementation.""" def __init__(self, iface, parent=None, featuresId=None, layer=None): """Constructor.""" QDialog.__init__(self) self.setupUi(self) self.s = QSettings() self.plugin_path = os.path.dirname(os.path.realpath(__file__)) QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8")) Gui.loadLatin1Encoding() self.iface = iface self.canvas = self.iface.mapCanvas() self.parent = parent #Orientation from image self.yaw = math.pi self.bearing = None self.layer = layer self.featuresId = featuresId #Restore Previous size self.RestoreSize() #defaults self.actualPointDx = None self.actualPointSx = None self.actualPointOrientation = None self.selected_features = qgsutils.getToFeature(self.canvas, self.layer, self.featuresId) #Get image path self.current_image = self.GetImage() QtGui.qApp.processEvents() #Creamos el visor self.CreateViewer() QtGui.qApp.processEvents() #Check if image exist if os.path.exists(self.current_image) is False: qgsutils.showUserAndLogMessage(self, u"Information: ", u"There is no associated image.", QgsMessageBar.INFO) self.ChangeUrlViewer(config.DEFAULT_EMPTY) self.setPosition() return #Set RubberBand self.setOrientation() self.setPosition() QtGui.qApp.processEvents() #Copy file to local server self.CopyFile(self.current_image) QtGui.qApp.processEvents() #Set Initial Yaw def SetInitialYaw(self): self.bearing = self.selected_features.attribute(config.column_yaw) self.view.browser.GetMainFrame().ExecuteFunction( "InitialYaw", self.bearing) return #Create Viewer def CreateViewer(self): qgsutils.showUserAndLogMessage(self, u"Information: ", u"Create viewer", QgsMessageBar.INFO, onlyLog=True) self.view = CefWidget(self) self.m_vbox = QVBoxLayout() self.m_vbox.addWidget(self.view) QtGui.qApp.processEvents() self.frame.setLayout(self.m_vbox) QtGui.qApp.processEvents() self.view.embed() QtGui.qApp.processEvents() return #Copy Image File in Local Server def CopyFile(self, src): qgsutils.showUserAndLogMessage(self, u"Information: ", u"Copiar imagem", QgsMessageBar.INFO, onlyLog=True) pattern = "^(?P<photo_id>\d+)[^\d].*jpg$" src_dir = src dst_dir = self.plugin_path + "\\viewer" #Delete images on first time for root, dirs, files in os.walk(dst_dir): for file in filter(lambda x: re.match(pattern, x), files): os.remove(os.path.join(root, file)) #Copy image in local folder dst_dir = dst_dir + "\\image.jpg" shutil.copy(src_dir, dst_dir) QtGui.qApp.processEvents() return #Restore Dialog Size def RestoreSize(self): dw = self.s.value("EquirectangularViewer/width") dh = self.s.value("EquirectangularViewer/height") if dw == None: return size = self.size() anim = QtCore.QPropertyAnimation(self, 'size', self) anim.setStartValue(size) anim.setEndValue(QtCore.QSize(dw, dh)) anim.setDuration(1) anim.start() QtGui.qApp.processEvents() return #Save Dialog Size def SaveSize(self): dw = self.width() dh = self.height() self.s.setValue("EquirectangularViewer/width", dw) self.s.setValue("EquirectangularViewer/height", dh) QtGui.qApp.processEvents() return #Get Selected Image def GetImage(self): try: path = qgsutils.getAttributeFromFeature(self.selected_features, config.column_name) except: qgsutils.showUserAndLogMessage(self, u"Information: ", u"Column not found.", QgsMessageBar.INFO) return qgsutils.showUserAndLogMessage(self, u"Information: ", str(path), QgsMessageBar.INFO, onlyLog=True) return path #Change Url Viewer def ChangeUrlViewer(self, new_url): self.view.browser.GetMainFrame().ExecuteJavascript( "window.location='%s'" % new_url) QtGui.qApp.processEvents() return #Reaload Image viewer def ReloadView(self, newId): self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) # this will activate the window self.activateWindow() QtGui.qApp.processEvents() self.selected_features = qgsutils.getToFeature(self.canvas, self.layer, newId) self.current_image = self.GetImage() #Check if image exist if os.path.exists(self.current_image) is False: qgsutils.showUserAndLogMessage( self, u"Information: ", u"It is not in the associated image.", QgsMessageBar.INFO) self.ChangeUrlViewer(config.DEFAULT_EMPTY) self.setPosition() return #Set RubberBand self.setOrientation() self.setPosition() #Copy file to local server self.CopyFile(self.current_image) self.ChangeUrlViewer(config.DEFAULT_URL) QtGui.qApp.processEvents() return #Expanded/Decreased Dialog def ResizeDialog(self): sender = QObject.sender(self) w = self.width() h = self.height() size = self.size() anim = QtCore.QPropertyAnimation(self, 'size', self) anim.setStartValue(size) if sender.objectName() == "btn_ZoomOut": anim.setEndValue(QtCore.QSize(w - 50, h - 50)) else: anim.setEndValue(QtCore.QSize(w + 50, h + 50)) anim.setDuration(300) anim.start() QtGui.qApp.processEvents() return #Get to Back Image def GetBackNextImage(self): qgsutils.removeAllHighlightFeaturesFromCanvasScene(self.canvas) sender = QObject.sender(self) lys = self.canvas.layers() #Check if mapa foto is loaded if len(lys) == 0: qgsutils.showUserAndLogMessage( self, u"Information: ", u"You need to upload the photo layer.", QgsMessageBar.INFO) return for layer in lys: if layer.name() == config.layer_name: self.encontrado = True self.iface.setActiveLayer(layer) QtGui.qApp.processEvents() f = self.selected_features ac_lordem = f.attribute(config.column_order) if sender.objectName() == "btn_back": new_lordem = int(ac_lordem) - 1 else: new_lordem = int(ac_lordem) + 1 #Filter mapa foto layer ids = [ feat.id() for feat in layer.getFeatures(QgsFeatureRequest( ).setFilterExpression("order ='" + str(new_lordem) + "'")) ] if len(ids) == 0: qgsutils.showUserAndLogMessage( self, u"Information: ", u"There is no superiority that follows.", QgsMessageBar.INFO) #Filter mapa foto layer ids = [ feat.id() for feat in layer.getFeatures(QgsFeatureRequest( ).setFilterExpression("order ='" + str(ac_lordem) + "'")) ] #Update selected feature self.ReloadView(ids[0]) return self.ReloadView(ids[0]) QtGui.qApp.processEvents() if self.encontrado == False: qgsutils.showUserAndLogMessage( self, u"Information: ", u"You need to upload the photo layer.", QgsMessageBar.INFO) return #FullScreen action button def FullScreen(self, bool): qgsutils.showUserAndLogMessage(self, u"Information: ", u"Fullscreen.", QgsMessageBar.INFO, onlyLog=True) if (bool): self.showFullScreen() else: self.showNormal() QtGui.qApp.processEvents() return @staticmethod def ActualOrientation(yaw): geo360Plugin = qgis.utils.plugins["EquirectangularViewer"] if geo360Plugin is not None: geo360Dialog = qgis.utils.plugins["EquirectangularViewer"].dlg if geo360Dialog is not None: geo360Dialog.UpdateOrientation(yaw=float(yaw)) return #Update Orientation def UpdateOrientation(self, yaw=None): self.bearing = self.selected_features.attribute(config.column_yaw) try: self.actualPointOrientation.reset() except: pass self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.actualPointOrientation.setColor(Qt.blue) self.actualPointOrientation.setWidth(5) self.actualPointOrientation.addPoint(self.actualPointDx) #End Point CS = self.canvas.mapUnitsPerPixel() * 25 A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2) A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2) self.actualPointOrientation.addPoint(QgsPoint(A1x, A1y)) #Vision Angle if yaw is not None: angle = float(self.bearing + yaw) * math.pi / -180 else: angle = float(self.bearing) * math.pi / -180 tmpGeom = self.actualPointOrientation.asGeometry() self.actualPointOrientation.setToGeometry( self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle), self.dumLayer) QtGui.qApp.processEvents() #Set Orientation in the firt time def setOrientation(self, yaw=None): self.bearing = self.selected_features.attribute(config.column_yaw) self.actualPointDx = self.selected_features.geometry().asPoint() try: self.actualPointOrientation.reset() except: pass QtGui.qApp.processEvents() self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.actualPointOrientation.setColor(Qt.blue) self.actualPointOrientation.setWidth(5) self.actualPointOrientation.addPoint(self.actualPointDx) #End Point CS = self.canvas.mapUnitsPerPixel() * 25 A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2) A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2) self.actualPointOrientation.addPoint(QgsPoint(A1x, A1y)) #Vision Angle if yaw is not None: angle = float(self.bearing + yaw) * math.pi / -180 else: angle = float(self.bearing) * math.pi / -180 tmpGeom = self.actualPointOrientation.asGeometry() self.rotateTool = transformGeometry() self.dumLayer = QgsVectorLayer("Point?crs=EPSG:4326", "temporary_points", "memory") self.actualPointOrientation.setToGeometry( self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle), self.dumLayer) QtGui.qApp.processEvents() #Set RubberBand Position def setPosition(self): self.actualPointDx = self.selected_features.geometry().asPoint() try: self.positionDx.reset() self.positionSx.reset() self.positionInt.reset() except: pass QtGui.qApp.processEvents() self.positionDx = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.positionDx.setWidth(6) self.positionDx.setIcon(QgsRubberBand.ICON_CIRCLE) self.positionDx.setIconSize(6) self.positionDx.setColor(Qt.black) self.positionSx = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.positionSx.setWidth(5) self.positionSx.setIcon(QgsRubberBand.ICON_CIRCLE) self.positionSx.setIconSize(4) self.positionSx.setColor(Qt.blue) self.positionInt = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.positionInt.setWidth(5) self.positionInt.setIcon(QgsRubberBand.ICON_CIRCLE) self.positionInt.setIconSize(3) self.positionInt.setColor(Qt.white) QtGui.qApp.processEvents() self.positionDx.addPoint(self.actualPointDx) self.positionSx.addPoint(self.actualPointDx) self.positionInt.addPoint(self.actualPointDx) QtGui.qApp.processEvents() #Close dialog def closeEvent(self, evt): qgsutils.showUserAndLogMessage(self, u"Information: ", u"Close dialog", QgsMessageBar.INFO, onlyLog=True) qgsutils.removeAllHighlightFeaturesFromCanvasScene(self.canvas) QtGui.qApp.processEvents() self.canvas.refresh() self.iface.actionPan().trigger() self.SaveSize() self.stopTimer() QtGui.qApp.processEvents() return
class DiviIdentifyTool(QgsMapToolIdentify): on_feature = pyqtSignal(object) on_activities = pyqtSignal(dict) on_raster = pyqtSignal(list) wgs84 = QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId) cursor = QCursor(QPixmap(["16 16 3 1", "# c None", "a c #000000", ". c #ffffff", ".###########..##", "...########.aa.#", ".aa..######.aa.#", "#.aaa..#####..##", "#.aaaaa..##.aa.#", "##.aaaaaa...aa.#", "##.aaaaaa...aa.#", "##.aaaaa.##.aa.#", "###.aaaaa.#.aa.#", "###.aa.aaa..aa.#", "####..#..aa.aa.#", "####.####.aa.a.#", "##########.aa..#", "###########.aa..", "############.a.#", "#############.##"]), 0, 0) def __init__(self, parent): self.parent = parent self.iface = parent.iface self.canvas = parent.iface.mapCanvas() self.indentifying = False self.currentFid = None self.geometry = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.geometry.setColor(QColor('red')) self.geometry.setFillColor(QColor(255, 0, 0, 100)) self.geometry.setIconSize(7) self.geometry.setWidth(3) super(DiviIdentifyTool, self).__init__(self.canvas) def canvasReleaseEvent(self, event ): self.geometry.reset(QgsWkbTypes.PointGeometry) layer = self.iface.activeLayer() if layer is None: return if isinstance(layer, QgsRasterLayer): if layer.customProperty('DiviId') is None: #Selected layer is not from DIVI self.on_raster.emit( [] ) return point = self.iface.mapCanvas().getCoordinateTransform().toMapCoordinates( event.x(), event.y() ) self.identifyRaster( point, layer.customProperty('DiviId') ) self.geometry.addPoint( point ) return self.on_activities.emit( { 'attachments':[], 'comments':[], 'changes':[]} ) if layer.customProperty('DiviId') is None: #Selected layer is not from DIVI self.on_feature.emit( None ) return result = self.identify(event.x(), event.y(), [layer], QgsMapToolIdentify.ActiveLayer) if not result: #Clear activities panel and return if no feaure was found if self.indentifying: self.abortIdentification() self.on_feature.emit( None ) return self.parent.identification_dock.tvIdentificationResult.model().sourceModel().setLoading() feature = result[0].mFeature if feature.id()<0: #Added feature self.iface.messageBar().pushMessage(self.tr("Error"), self.tr("Selected feature is not saved."), level=QgsMessageBar.CRITICAL, duration=3) return self.geometry.setToGeometry(feature.geometry(), layer) if self.indentifying: self.abortIdentification() self.identifyVector( feature ) def activate(self): super(DiviIdentifyTool, self).activate() self.connector = self.parent.dockwidget.getConnector() self.canvas.setCursor(self.cursor) def deactivate(self): super(DiviIdentifyTool, self).deactivate() self.geometry.reset() self.action().setChecked(False) del self.connector def identifyVector(self, feature): self.indentifying = True if not self.parent.identification_dock.isVisible(): self.parent.identification_dock.show() self.parent.identification_dock.raise_() fid = self.parent.ids_map[self.parent.iface.activeLayer().id()][feature.id()] self.on_feature.emit( fid ) QgsMessageLog.logMessage(self.tr('Feature start identification: %d (diviID:%d)') % (feature.id(), fid), 'DIVI') if not self.indentifying: self.on_feature.emit( None ) return #Get activities for itemType in ('attachments', 'comments', 'changes'): data = getattr(self.connector, 'get_%s' % itemType)( fid ) or {} if self.indentifying: self.on_activities.emit( { itemType : data.get('data', []) } ) else: self.on_feature.emit( None ) return QgsMessageLog.logMessage(self.tr('Feature end identification: %d (diviID:%d)') % (feature.id(), fid), 'DIVI') self.indentifying = False def identifyRaster(self, point, layerid): canvas_crs = self.canvas.mapSettings().destinationCrs() if canvas_crs.authid() != "EPSG:4326": transform = QgsCoordinateTransform(canvas_crs, self.wgs84, QgsCoordinateTransformContext ()) point = transform.transform(point) result = self.connector.getRasterIdentification( layerid, point )['data'][0] if not isinstance(result, list): result = [result] self.on_raster.emit( result ) def abortIdentification(self): self.connector.abort() self.indentifying = False self.on_feature.emit( None ) def toggleMapTool(self, state): if state: self.canvas.setMapTool(self) else: self.canvas.unsetMapTool(self)
class IntersectTool(QgsMapTool): """ Map tool class to create temporary circle, with center point """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/intersect_icon.png' self.text = QCoreApplication.translate("VDLTools", "From intersection") self.setCursor(Qt.ArrowCursor) self.__lineLayerID = None self.__pointLayerID = None self.__rubber = None self.ownSettings = None self.__isEditing = False self.__distance = 0 def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__rubber = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setIconSize(20) self.__rubber.setWidth(2) self.__distance = 6.0 def deactivate(self): """ When the action is deselected """ self.__cancel() self.__rubber = None QgsMapTool.deactivate(self) def __cancel(self): """ To cancel used variables """ if self.__rubber is not None: self.__rubber.reset() self.__isEditing = False def __setDistanceDialog(self, mapPoint): """ To create an Intersect Distance Dialog :param mapPoint: radius of the circle """ self.__dstDlg = IntersectDistanceDialog(mapPoint) self.__dstDlg.rejected.connect(self.__cancel) self.__dstDlg.okButton().clicked.connect(self.__onDstOk) self.__dstDlg.cancelButton().clicked.connect(self.__onDstCancel) self.__dstDlg.observation().setValue(self.__distance) self.__dstDlg.observation().selectAll() self.__dstDlg.show() def __onDstOk(self): """ When the Ok button in Intersect Distance Dialog is pushed """ self.__distance = float(self.__dstDlg.observation().text()) circle = QgsCircularStringV2() x = self.__dstDlg.mapPoint().x() y = self.__dstDlg.mapPoint().y() circle.setPoints([ QgsPointV2(x + self.__distance * cos(pi / 180 * a), y + self.__distance * sin(pi / 180 * a)) for a in range(0, 361, 90) ]) lineLayer = self.__lineLayer() lineLayer.startEditing() feature = QgsFeature() feature.setGeometry(QgsGeometry(circle)) fields = lineLayer.pendingFields() feature.setFields(fields) fieldsNames = [fields.at(pos).name() for pos in range(fields.count())] if "distance" in fieldsNames: feature.setAttribute("distance", self.__distance) if "x" in fieldsNames: feature.setAttribute("x", self.__dstDlg.mapPoint().x()) if "y" in fieldsNames: feature.setAttribute("y", self.__dstDlg.mapPoint().y()) lineLayer.addFeature(feature) # lineLayer.updateExtents() lineLayer.commitChanges() # center pointLayer = self.__pointLayer() pointLayer.startEditing() feature = QgsFeature() feature.setGeometry(QgsGeometry().fromPoint(self.__dstDlg.mapPoint())) fields = pointLayer.pendingFields() feature.setFields(fields) pointLayer.addFeature(feature) pointLayer.commitChanges() self.__dstDlg.accept() self.__cancel() def __onDstCancel(self): """ When the Cancel button in Intersect Distance Dialog is pushed """ self.__dstDlg.reject() def canvasMoveEvent(self, mouseEvent): """ When the mouse is moved :param mouseEvent: mouse event """ if not self.__isEditing: self.__rubber.reset() match = Finder.snap(mouseEvent.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubber.setIcon(4) else: self.__rubber.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self) if intersection is not None: self.__rubber.setIcon(1) point = intersection else: self.__rubber.setIcon(3) self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None) def canvasReleaseEvent(self, mouseEvent): """ When the mouse is clicked :param mouseEvent: mouse event """ if mouseEvent.button() != Qt.LeftButton: return match = Finder.snap(mouseEvent.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections( match.point(), self.canvas(), self) if intersection is not None: point = intersection self.__isEditing = True self.__setDistanceDialog(point) def __lineLayer(self): """ To get the line layer to create the circle :return: a line layer """ if self.ownSettings is not None: if self.ownSettings.linesLayer is not None: layer = self.ownSettings.linesLayer self.__lineLayerID = layer.id() return layer layer = QgsMapLayerRegistry.instance().mapLayer(self.__lineLayerID) if layer is None: epsg = self.canvas().mapRenderer().destinationCrs().authid() layer = QgsVectorLayer( "LineString?crs=%s&index=yes&field=distance:double&field=x:double&field=y:double" % epsg, "Memory Lines", "memory") QgsMapLayerRegistry.instance().addMapLayer(layer) layer.layerDeleted.connect(self.__lineLayerDeleted) self.__lineLayerID = layer.id() if self.ownSettings is not None: self.ownSettings.linesLayer = layer else: self.__iface.legendInterface().setLayerVisible(layer, True) return layer def __lineLayerDeleted(self): """ To deselect the line layer when it is deleted """ self.lineLayerID = None def __pointLayer(self): """ To get the point layer to create the center :return: a point layer """ if self.ownSettings is not None: if self.ownSettings.pointsLayer is not None: layer = self.ownSettings.pointsLayer self.__pointLayerID = layer.id() return layer layer = QgsMapLayerRegistry.instance().mapLayer(self.__pointLayerID) if layer is None: epsg = self.canvas().mapRenderer().destinationCrs().authid() layer = QgsVectorLayer("Point?crs=%s&index=yes" % epsg, "Memory Points", "memory") QgsMapLayerRegistry.instance().addMapLayer(layer) layer.layerDeleted.connect(self.__pointLayerDeleted) self.__pointLayerID = layer.id() if self.ownSettings is not None: self.ownSettings.pointsLayer = layer else: self.__iface.legendInterface().setLayerVisible(layer, True) return layer def __pointLayerDeleted(self): """ To deselect the point layer when it is deleted :return: """ self.__pointLayerID = None
class nominatim_dlg(QDockWidget, FORM_CLASS): """ Gestion de l'évènement "leave", afin d'effacer l'objet sélectionné en sortie du dock """ def eventFilter(self, obj, event): typ = event.type() if typ == event.Leave: try: self.plugin.canvas.scene().removeItem(self.rubber) except: pass return False def __init__(self, parent, plugin): self.plugin = plugin QDockWidget.__init__(self, parent) self.setupUi(self) self.btnApply.setIcon( QIcon(str(DIR_PLUGIN_ROOT / "resources/arrow_green.png"))) self.btnMask.setIcon( QIcon(str(DIR_PLUGIN_ROOT / "resources/add_mask.png"))) self.btnLayer.setIcon( QIcon(str(DIR_PLUGIN_ROOT / "resources/add_layer.png"))) self.btnHelp.setIcon( QIcon(QgsApplication.iconPath("mActionHelpContents.svg"))) self.tableResult.installEventFilter(self) # cf. eventFilter method self.tableResult.cellDoubleClicked.connect(self.onChoose) self.tableResult.cellEntered.connect(self.cellEntered) self.editSearch.returnPressed.connect(self.onReturnPressed) self.btnSearch.clicked.connect(self.onReturnPressed) self.btnApply.clicked.connect(self.onApply) self.btnHelp.clicked.connect( lambda: showPluginHelp(filename="../doc/index")) self.btnLocalize.clicked.connect(self.doLocalize) self.btnMask.clicked.connect(self.onMask) self.btnLayer.clicked.connect(self.onLayer) self.singleLayerId = { QgsWkbTypes.PolygonGeometry: None, QgsWkbTypes.LineGeometry: None, QgsWkbTypes.PointGeometry: None, } self.singleLayerName = { QgsWkbTypes.PolygonGeometry: "OSM Place Search Polygons", QgsWkbTypes.LineGeometry: "OSM Place Search Lines", QgsWkbTypes.PointGeometry: "OSM Place Search Points", } self.memoryLayerType = { QgsWkbTypes.PolygonGeometry: "MultiPolygon", QgsWkbTypes.LineGeometry: "MultiLineString", QgsWkbTypes.PointGeometry: "Point", } try: self.cbExtent.setChecked(tools.limitSearchToExtent) except: self.cbExtent.setChecked(tools.limitSearchToExtent) self.currentExtent = self.plugin.canvas.extent() self.tableResult.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) try: self.editSearch.setText(self.plugin.lastSearch) except: pass def cellEntered(self, row, col): item = self.tableResult.item(row, 0) try: self.plugin.canvas.scene().removeItem(self.rubber) self.showItem(item) except: pass def onLayer(self): for r in self.tableResult.selectedRanges(): item = self.tableResult.item(r.topRow(), 0) self.doLayer(item) def onMask(self): for r in self.tableResult.selectedRanges(): item = self.tableResult.item(r.topRow(), 0) self.doMask(item) def populateRow(self, item, idx): id = item["place_id"] name = item["display_name"] try: className = QApplication.translate("nominatim", item["class"], None) except: className = "" try: typeName = QApplication.translate("nominatim", item["type"], None) except: typeName = "" try: wkt = item["geotext"] except: wkt = None try: osm_type = item["osm_type"] except: osm_type = None bbox = {} if osm_type == "node": lat = item["lat"] lng = item["lon"] poFD = ogr.FeatureDefn("Point") poFD.SetGeomType(ogr.wkbPoint) oFLD = ogr.FieldDefn("id", ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn("name", ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) wkt = "POINT({} {})".format(lng, lat) ogrGeom = ogr.CreateGeometryFromWkt(wkt) else: try: bbox = item["boundingbox"] poFD = ogr.FeatureDefn("Rectangle") poFD.SetGeomType(ogr.wkbPolygon) oFLD = ogr.FieldDefn("id", ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn("name", ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) if wkt is None: wkt = "POLYGON(({b[2]} {b[0]}, {b[2]} {b[1]}, {b[3]} {b[1]}, {b[3]} {b[0]}, {b[2]} {b[0]}))".format( b=bbox) ogrGeom = ogr.CreateGeometryFromWkt(wkt) except: lat = item["lat"] lng = item["lon"] poFD = ogr.FeatureDefn("Point") poFD.SetGeomType(ogr.wkbPoint) oFLD = ogr.FieldDefn("id", ogr.OFTString) poFD.AddFieldDefn(oFLD) oFLD = ogr.FieldDefn("name", ogr.OFTString) poFD.AddFieldDefn(oFLD) ogrFeature = ogr.Feature(poFD) wkt = "POINT({} {})".format(lng, lat) ogrGeom = ogr.CreateGeometryFromWkt(wkt) ogrFeature.SetGeometry(ogrGeom) ogrFeature.SetFID(int(idx + 1)) ogrFeature.SetField(str("id"), str(id)) ogrFeature.SetField(str("name"), name) item = QTableWidgetItem(name) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) item.setData(Qt.UserRole, ogrFeature) self.tableResult.setItem(idx, 0, item) itemLibelle = QTableWidgetItem(className) itemLibelle.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableResult.setItem(idx, 1, itemLibelle) itemType = QTableWidgetItem(typeName) itemType.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableResult.setItem(idx, 2, itemType) def populateTable(self, r): idx = 0 self.tableResult.clearContents() self.tableResult.setRowCount(len(r)) for item in r: self.populateRow(item, idx) idx = idx + 1 def doLocalize(self): try: # center bbox = self.plugin.canvas.extent() sourceCrs = self.plugin.canvas.mapSettings().destinationCrs() targetCrs = QgsCoordinateReferenceSystem("EPSG:4326") xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance()) bbox = xform.transform(bbox) params = { "lon": str(bbox.center().x()), "lat": str(bbox.center().y()), "zoom": "10", } r = tools.osmFindNearbyJSON(params, tools.gnOptions) if r != None: self.populateTable(r) else: self.tableResult.clearContents() except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, "Extensions") pass def search(self, txt): try: self.plugin.lastSearch = self.editSearch.text() tools.limitSearchToExtent = self.cbExtent.isChecked() return tools.osmSearch(self.plugin.iface.mapCanvas(), txt) except Exception as e: for m in e.args: QgsMessageLog.logMessage(m, "Extensions") return None def onReturnPressed(self): txt = self.editSearch.text().strip() r = self.search(txt) if r != None: self.populateTable(r) else: self.tableResult.clearContents() def onChoose(self, row, col): item = self.tableResult.item(row, 0) self.go(item) def onApply(self): for item in self.tableResult.selectedItems(): self.go(item) break def transform(self, geom): sourceSRS = QgsCoordinateReferenceSystem("EPSG:4326") mapCrs = self.plugin.canvas.mapSettings().destinationCrs() trsf = QgsCoordinateTransform(sourceSRS, mapCrs, QgsProject.instance()) try: geom.transform(trsf) except TypeError: QgsMessageLog.logMessage( "Nominatim - transformation error. Check map projection.", "Extensions") def getBBox(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) if ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint: mapextent = self.plugin.canvas.extent() ww = mapextent.width() / 100 mapcrs = self.plugin.canvas.mapSettings().destinationCrs() x = geom.boundingBox().center().x() y = geom.boundingBox().center().y() ww = 50.0 if mapcrs.mapUnits() == QgsUnitTypes.DistanceFeet: ww = 150 if mapcrs.mapUnits() == QgsUnitTypes.DistanceDegrees: ww = 0.0005 bbox = QgsRectangle(x - 10 * ww, y - 10 * ww, x + 10 * ww, y + 10 * ww) return bbox else: bbox = geom.boundingBox() rubberRect = QgsRectangle(bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()) return rubberRect def showItem(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) if ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint: self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PointGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(2) self.rubber.setToGeometry(geom, None) else: # dont show if it is larger than the canvas if self.plugin.canvas.extent().contains(geom.boundingBox()): pass else: geom = geom.intersection( QgsGeometry.fromRect(self.plugin.canvas.extent())) self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PolygonGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setWidth(4) self.rubber.setToGeometry(geom, None) def go(self, item, zoom=True): try: self.plugin.canvas.scene().removeItem(self.rubber) except: pass if zoom: bbox = self.getBBox(item) self.plugin.canvas.setExtent(bbox) self.plugin.canvas.refresh() self.showItem(item) def addNewLayer(self, layerName, typ, fields): vl = QgsVectorLayer(self.memoryLayerType[typ], layerName, "memory") if vl: vl.setProviderEncoding("UTF-8") pr = vl.dataProvider() pr.addAttributes(fields.toList()) vl.setCrs(self.plugin.canvas.mapSettings().destinationCrs()) QgsProject.instance().addMapLayer(vl) renderer = vl.renderer() s = renderer.symbol() s.setOpacity(0.85) return vl def doLayer(self, item, singleLayer=None): if singleLayer is None: singleLayer = self.plugin.singleLayer ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) fields = QgsFields() fields.append(QgsField("id", QVariant.String)) fields.append(QgsField("name", QVariant.String)) fet = QgsFeature() fet.initAttributes(2) fet.setFields(fields) fet.setGeometry(geom) fet.setAttribute("id", (ogrFeature.GetFieldAsString("id"))) fet.setAttribute("name", (ogrFeature.GetFieldAsString("name"))) vl = None if not singleLayer: layerId = self.singleLayerId[geom.type()] layerName = self.singleLayerName[geom.type()] vl = QgsProject.instance().mapLayer(layerId) if vl is None: vl = self.addNewLayer(layerName, geom.type(), fields) if vl: self.singleLayerId[geom.type()] = vl.id() else: layerName = "OSM " + ogrFeature.GetFieldAsString("id") vl = self.addNewLayer(layerName, geom.type(), fields) if vl is not None: pr = vl.dataProvider() vl.startEditing() pr.addFeatures([fet]) vl.commitChanges() # mise a jour etendue de la couche vl.updateExtents() """ layerTree = QgsProject.instance().layerTreeRoot().findLayer(vl) if layerTree: self.plugin.iface.layerTreeView().layerTreeModel().refreshLayerLegend( layerTree ) # Refresh legend """ self.go(item, False) return vl def doMask(self, item): mapcrs = self.plugin.canvas.mapSettings().destinationCrs() ogrFeature = item.data(Qt.UserRole) layerName = "OSM " + ogrFeature.GetFieldAsString("id") geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) self.transform(geom) if geom.type() == QgsWkbTypes.PolygonGeometry: try: try: from mask import aeag_mask except: from mask_plugin import aeag_mask aeag_mask.do(mapcrs, {geom}, "Mask " + layerName) self.go(item) except: maskLayer = self.doLayer(item, True) maskLayer.loadNamedStyle( str(DIR_PLUGIN_ROOT / "resources" / "mask.qml")) maskLayer.triggerRepaint()
class DistanceMapTool(QgsMapTool): def __init__(self, iface): self.iface = iface self.line_layer = None self.settings = MySettings() QgsMapTool.__init__(self, iface.mapCanvas()) def activate(self): QgsMapTool.activate(self) self.line_layer = MemoryLayers(self.iface).line_layer self.rubber = QgsRubberBand(self.canvas(), QGis.Point) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIcon(self.settings.value("rubberIcon")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.messageWidget = self.iface.messageBar().createMessage("Intersect It", "Not snapped.") self.messageWidgetExist = True self.messageWidget.destroyed.connect(self.messageWidgetRemoved) def deactivate(self): self.iface.messageBar().popWidget(self.messageWidget) self.rubber.reset() QgsMapTool.deactivate(self) def messageWidgetRemoved(self): self.messageWidgetExist = False def displaySnapInfo(self, match=None): if not self.messageWidgetExist: return if match is None: message = "No snap" else: message = "Snapped to: <b>{}</b>".format(match.layer()) self.messageWidget.setText(message) def canvasMoveEvent(self, mouseEvent): match = self.snap_to_vertex(mouseEvent.pos()) self.rubber.reset(QGis.Point) if match.type() == QgsPointLocator.Vertex and match.layer() != self.line_layer: self.rubber.addPoint(match.point()) self.displaySnapInfo(match) def canvasPressEvent(self, mouseEvent): if mouseEvent.button() != Qt.LeftButton: return match = self.snap_to_vertex(mouseEvent.pos()) if match.type() != QgsPointLocator.Vertex and match.layer() != self.line_layer: point = self.toMapCoordinates(mouseEvent.pos()) else: point = match.point() self.rubber.addPoint(point) distance = Distance(self.iface, point, 1) dlg = DistanceDialog(distance, self.canvas()) if dlg.exec_(): distance.save() self.rubber.reset() def snap_to_vertex(self, pos): """ Temporarily override snapping config and snap to vertices and edges of any editable vector layer, to allow selection of node for editing (if snapped to edge, it would offer creation of a new vertex there). """ map_point = self.toMapCoordinates(pos) tol = QgsTolerance.vertexSearchRadius(self.canvas().mapSettings()) snap_type = QgsPointLocator.Type(QgsPointLocator.Vertex) snap_layers = [] for layer in self.canvas().layers(): if not isinstance(layer, QgsVectorLayer): continue snap_layers.append(QgsSnappingUtils.LayerConfig(layer, snap_type, tol, QgsTolerance.ProjectUnits)) snap_util = self.canvas().snappingUtils() old_layers = snap_util.layers() old_mode = snap_util.snapToMapMode() old_inter = snap_util.snapOnIntersections() snap_util.setLayers(snap_layers) snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) snap_util.setSnapOnIntersections(True) m = snap_util.snapToMap(map_point) snap_util.setLayers(old_layers) snap_util.setSnapToMapMode(old_mode) snap_util.setSnapOnIntersections(old_inter) return m
class MoveTool(QgsMapToolAdvancedDigitizing): """ Map tool class to move or copy an object """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__findVertex = False self.__onMove = False self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def deactivate(self): """ When the action is deselected """ self.__cancel() QgsMapToolAdvancedDigitizing.deactivate(self) def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def __cancel(self): """ To cancel used variables """ if self.__rubberBand is not None: self.canvas().scene().removeItem(self.__rubberBand) self.__rubberBand.reset() self.__rubberBand = None if self.__rubberSnap is not None: self.canvas().scene().removeItem(self.__rubberSnap) self.__rubberSnap.reset() self.__rubberSnap = None self.__isEditing = False self.__findVertex = False self.__onMove = False self.__lastFeatureId = None self.__selectedFeature = None self.__confDlg = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.__removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(), self.__iface) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry(), self.__iface) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in range(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry( QgsGeometry(self.__newFeature.curveToLine()), None) @staticmethod def __newCurve(curved, line_v2, dx, dy): """ To create a new moved line :param curved: if the line is curved :param line_v2: the original line :param dx: x translation :param dy: y translation :return: the new line """ if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in range(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2( self.__selectedFeature.geometry(), self.__iface) vertex = polygon_v2.vertexAt( GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in range(polygon_v2.numInteriorRings()): line_v2 = self.__newCurve(curved[num + 1], polygon_v2.interiorRing(num), dx, dy) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __onConfirmCancel(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage(QCoreApplication.translate( "VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__confDlg.accept() self.__cancel() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage(QCoreApplication.translate( "VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute( field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \ QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__confDlg.accept() self.__cancel() def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and not self.__onMove: laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), [laySettings]) if f_l is not None and self.__lastFeatureId != f_l[0].id(): self.__lastFeatureId = f_l[0].id() self.__layer.setSelectedFeatures([f_l[0].id()]) if f_l is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: if self.__rubberBand is not None: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex( map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry( QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(map_point) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(map_point) else: self.__pointPreview(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(8) if self.__rubberSnap is not None: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(map_point, self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) else: self.__rubberSnap.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self) if intersection is not None: self.__rubberSnap.setIcon(1) point = intersection else: self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__iface.messageBar().pushMessage( QCoreApplication.translate( "VDLTools", "Select vertex for moving (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.__findVertex = True self.setMode(self.CaptureLine) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) else: self.setMode(self.CaptureNone) self.__onMove = True elif self.__findVertex: self.__findVertex = False self.setMode(self.CaptureNone) closest = self.__selectedFeature.geometry().closestVertex( event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = True elif self.__onMove: self.__onMove = False mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections( mapPoint, self.canvas(), self) if intersection is not None: mapPoint = intersection self.__isEditing = True if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect( self.__onConfirmCancel) self.__confDlg.show()
class FinderBox(QComboBox): running = False to_finish = 0 search_started = pyqtSignal() search_finished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.marker = None self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.result_view = QTreeView() self.result_view.setHeaderHidden(True) self.result_view.setMinimumHeight(300) self.result_view.activated.connect(self.itemActivated) self.result_view.pressed.connect(self.itemPressed) self.setView(self.result_view) self.result_model = ResultModel(self) self.setModel(self.result_model) self.finders = finders for finder in list(self.finders.values()): finder.result_found.connect(self.result_found) finder.limit_reached.connect(self.limit_reached) finder.finished.connect(self.finished) self.clearButton = QPushButton(self) self.clearButton.setIcon( QIcon(":/plugins/quickfinder/icons/draft.svg")) self.clearButton.setText('') self.clearButton.setFlat(True) self.clearButton.setCursor(QCursor(Qt.ArrowCursor)) self.clearButton.setStyleSheet('border: 0px; padding: 0px;') self.clearButton.clicked.connect(self.clear) layout = QHBoxLayout(self) self.setLayout(layout) layout.addStretch() layout.addWidget(self.clearButton) layout.addSpacing(20) button_size = self.clearButton.sizeHint() # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) padding = button_size.width() # + frameWidth + 1 self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber #clear marker that the lonlat seted def clearMarker(self): self.mapCanvas.scene().removeItem(self.marker) self.marker = None def clearSelection(self): self.result_model.setSelected(None, self.result_view.palette()) self.rubber.reset() #self.clearMarker() def clear(self): self.clearSelection() self.result_model.clearResults() self.lineEdit().setText('') def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clearSelection() self.clearMarker() QComboBox.keyPressEvent(self, event) def lnglatFinder(self, to_find): import re m = re.match(r'(?P<lon>-?\d*(.\d+))\s+(?P<lat>-?\d*(.\d+))', to_find) if not m: return False x = float(m.group('lon')) y = float(m.group('lat')) return self.zoomLnglat(x, y) def zoomLnglat(self, lng, lat): x, y = lng, lat canvas = self.mapCanvas currExt = canvas.extent() canvasCenter = currExt.center() dx = float(x) - canvasCenter.x() dy = float(y) - canvasCenter.y() xMin = currExt.xMinimum() + dx xMax = currExt.xMaximum() + dx yMin = currExt.yMinimum() + dy yMax = currExt.yMaximum() + dy rect = QgsRectangle(xMin, yMin, xMax, yMax) canvas.setExtent(rect) pt = QgsPointXY(float(x), float(y)) self.marker = QgsVertexMarker(canvas) self.marker.setCenter(pt) self.marker.setIconSize(18) self.marker.setPenWidth(2) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) canvas.refresh() return True # def geocodeFinder(self, to_finder): # print(to_finder[:2]) # if not to_finder[:2] == 'b:': # return False # # address = to_finder[2:] # url = MySettings().value("baiduUrl") # url = url + parse.quote(address) # # response = request.urlopen(url) # content = response.read() # data = json.loads(content) # print(data) # lng, lat = (data['result']['location']['lng'], data['result']['location']['lat']) # from .cood_trans import bd09_to_wgs84 # lng, lat = bd09_to_wgs84(lng, lat) # print(f'{lng}-{lat}') # return self.zoomLnglat(lng, lat) def search(self): # self.geocode() if self.running: return to_find = self.lineEdit().text() if not to_find or to_find == '': return # if not (self.lnglatFinder(to_find) or self.geocodeFinder(to_find)): if not self.lnglatFinder(to_find): self.showPopup() self.running = True self.search_started.emit() self.clearSelection() self.result_model.clearResults() self.result_model.truncateHistory(MySettings().value("historyLength")) self.result_model.setLoading(True) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.finders_to_start = [] for finder in list(self.finders.values()): if finder.activated(): self.finders_to_start.append(finder) bbox = self.mapCanvas.fullExtent() while len(self.finders_to_start) > 0: finder = self.finders_to_start[0] self.finders_to_start.remove(finder) self.result_model.addResult(finder.name) finder.start(to_find, bbox=bbox) # For case there is no finder activated self.finished(None) def stop(self): self.finders_to_start = [] for finder in list(self.finders.values()): if finder.is_running(): finder.stop() self.finished(None) def result_found(self, finder, layername, value, geometry, srid): self.result_model.addResult(finder.name, layername, value, geometry, srid) self.result_view.expandAll() def limit_reached(self, finder, layername): self.result_model.addEllipsys(finder.name, layername) def finished(self, finder): if len(self.finders_to_start) > 0: return for finder in list(self.finders.values()): if finder.is_running(): return self.running = False self.search_finished.emit() self.result_model.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def itemActivated(self, index): item = self.result_model.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.result_model.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) geometry = self.transform_geom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoom_to_rubberband() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) self.rubber.reset(child.geometry.type()) for i in range(0, item.rowCount()): geometry = self.transform_geom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoom_to_rubberband() return if item.__class__.__name__ == 'QStandardItem': self.clearSelection() def transform_geom(self, item): src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapSettings().destinationCrs() geom = QgsGeometry(item.geometry) geom.transform( QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance())) return geom def zoom_to_rubberband(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class Geo360Dialog(QDockWidget, Ui_orbitalDialog): """Geo360 Dialog Class""" def __init__(self, iface, parent=None, featuresId=None, layer=None): QDockWidget.__init__(self) self.setupUi(self) self.DEFAULT_URL = ("http://" + config.IP + ":" + str(config.PORT) + "/viewer.html") self.DEFAULT_EMPTY = ("http://" + config.IP + ":" + str(config.PORT) + "/none.html") self.DEFAULT_BLANK = ("http://" + config.IP + ":" + str(config.PORT) + "/blank.html") # Create Viewer self.CreateViewer() self.plugin_path = os.path.dirname(os.path.realpath(__file__)) self.iface = iface self.canvas = self.iface.mapCanvas() self.parent = parent # Orientation from image self.yaw = math.pi self.bearing = None self.layer = layer self.featuresId = featuresId self.actualPointDx = None self.actualPointSx = None self.actualPointOrientation = None self.selected_features = qgsutils.getToFeature(self.layer, self.featuresId) # Get image path self.current_image = self.GetImage() # Check if image exist if os.path.exists(self.current_image) is False: qgsutils.showUserAndLogMessage(u"Information: ", u"There is no associated image.") self.resetQgsRubberBand() self.ChangeUrlViewer(self.DEFAULT_EMPTY) return # Copy file to local server self.CopyFile(self.current_image) # Set RubberBand self.resetQgsRubberBand() self.setOrientation() self.setPosition() """Update data from Marzipano Viewer""" def onNewData(self, data): try: newYaw = float(data[0]) self.UpdateOrientation(yaw=newYaw) except: None def CreateViewer(self): """Create Viewer""" qgsutils.showUserAndLogMessage(u"Information: ", u"Create viewer", onlyLog=True) self.cef_widget = QWebView() self.cef_widget.setContextMenuPolicy(Qt.NoContextMenu) self.cef_widget.settings().setAttribute(QWebSettings.JavascriptEnabled, True) pano_view_settings = self.cef_widget.settings() pano_view_settings.setAttribute(QWebSettings.WebGLEnabled, True) # pano_view_settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) pano_view_settings.setAttribute( QWebSettings.Accelerated2dCanvasEnabled, True) pano_view_settings.setAttribute(QWebSettings.JavascriptEnabled, True) self.page = _ViewerPage() self.page.newData.connect(self.onNewData) self.cef_widget.setPage(self.page) self.cef_widget.load(QUrl(self.DEFAULT_URL)) self.ViewerLayout.addWidget(self.cef_widget, 1, 0) # def SetInitialYaw(self): # """Set Initial Viewer Yaw""" # self.bearing = self.selected_features.attribute(config.column_yaw) # # self.view.browser.GetMainFrame().ExecuteFunction("InitialYaw", # # self.bearing) # return def RemoveImage(self): """Remove Image""" try: os.remove(self.plugin_path + "/viewer/image.jpg") except OSError: pass def CopyFile(self, src): """Copy Image File in Local Server""" qgsutils.showUserAndLogMessage(u"Information: ", u"Copying image", onlyLog=True) src_dir = src dst_dir = self.plugin_path + "/viewer" # Copy image in local folder img = Image.open(src_dir) rgb_im = img.convert("RGB") dst_dir = dst_dir + "/image.jpg" try: os.remove(dst_dir) except OSError: pass rgb_im.save(dst_dir) def GetImage(self): """Get Selected Image""" try: path = qgsutils.getAttributeFromFeature(self.selected_features, config.column_name) if not os.path.isabs(path): # Relative Path to Project path_project = QgsProject.instance().readPath("./") path = os.path.normpath(os.path.join(path_project, path)) except Exception: qgsutils.showUserAndLogMessage(u"Information: ", u"Column not found.") return qgsutils.showUserAndLogMessage(u"Information: ", str(path), onlyLog=True) return path def ChangeUrlViewer(self, new_url): """Change Url Viewer""" self.cef_widget.load(QUrl(new_url)) def ReloadView(self, newId): """Reaload Image viewer""" self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) # this will activate the window self.activateWindow() self.selected_features = qgsutils.getToFeature(self.layer, newId) self.current_image = self.GetImage() # Check if image exist if os.path.exists(self.current_image) is False: qgsutils.showUserAndLogMessage(u"Information: ", u"There is no associated image.") self.ChangeUrlViewer(self.DEFAULT_EMPTY) self.resetQgsRubberBand() return # Set RubberBand self.resetQgsRubberBand() self.setOrientation() self.setPosition() # Copy file to local server self.CopyFile(self.current_image) self.ChangeUrlViewer(self.DEFAULT_URL) def GetBackNextImage(self): """Get to Back Image""" sender = QObject.sender(self) lys = self.canvas.layers() # Check if mapa foto is loaded if len(lys) == 0: qgsutils.showUserAndLogMessage(u"Information: ", u"You need load the photo layer.") return for layer in lys: if layer.name() == config.layer_name: self.encontrado = True self.iface.setActiveLayer(layer) f = self.selected_features ac_lordem = f.attribute(config.column_order) if sender.objectName() == "btn_back": new_lordem = int(ac_lordem) - 1 else: new_lordem = int(ac_lordem) + 1 # Filter mapa foto layer ids = [ feat.id() for feat in layer.getFeatures(QgsFeatureRequest( ).setFilterExpression(config.column_order + " ='" + str(new_lordem) + "'")) ] if len(ids) == 0: qgsutils.showUserAndLogMessage( u"Information: ", u"There is no superiority that follows.") # Filter mapa foto layer ids = [ feat.id() for feat in layer.getFeatures(QgsFeatureRequest( ).setFilterExpression(config.column_order + " ='" + str(ac_lordem) + "'")) ] # Update selected feature self.ReloadView(ids[0]) if self.encontrado is False: qgsutils.showUserAndLogMessage( u"Information: ", u"You need a layer with images and set the name in the config.py file.", ) return def FullScreen(self, value): """FullScreen action button""" qgsutils.showUserAndLogMessage(u"Information: ", u"Fullscreen.", onlyLog=True) if value: self.showFullScreen() else: self.showNormal() def UpdateOrientation(self, yaw=None): """Update Orientation""" self.bearing = self.selected_features.attribute(config.column_yaw) try: self.actualPointOrientation.reset() except Exception: pass self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.LineGeometry) self.actualPointOrientation.setColor(Qt.blue) self.actualPointOrientation.setWidth(5) self.actualPointOrientation.addPoint(self.actualPointDx) # End Point CS = self.canvas.mapUnitsPerPixel() * 25 A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2) A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2) self.actualPointOrientation.addPoint(QgsPointXY( float(A1x), float(A1y))) # Vision Angle if yaw is not None: angle = float(self.bearing + yaw) * math.pi / -180 else: angle = float(self.bearing) * math.pi / -180 tmpGeom = self.actualPointOrientation.asGeometry() rotatePoint = self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle) self.actualPointOrientation.setToGeometry(rotatePoint, self.dumLayer) # Set Azimut value tmpGeom = rotatePoint.asPolyline() azim = tmpGeom[0].azimuth(tmpGeom[1]) if azim < 0: azim += 360 self.yawLbl.setText("Yaw : " + str(round(yaw, 2)) + " Azimut : " + str(round(azim, 2))) def setOrientation(self, yaw=None): """Set Orientation in the firt time""" self.bearing = self.selected_features.attribute(config.column_yaw) originalPoint = self.selected_features.geometry().asPoint() self.actualPointDx = qgsutils.convertProjection( originalPoint.x(), originalPoint.y(), self.layer.crs().authid(), self.canvas.mapSettings().destinationCrs().authid(), ) self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.LineGeometry) self.actualPointOrientation.setColor(Qt.blue) self.actualPointOrientation.setWidth(5) self.actualPointOrientation.addPoint(self.actualPointDx) # End Point CS = self.canvas.mapUnitsPerPixel() * 25 A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2) A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2) self.actualPointOrientation.addPoint(QgsPointXY( float(A1x), float(A1y))) # Vision Angle if yaw is not None: angle = float(self.bearing + yaw) * math.pi / -180 else: angle = float(self.bearing) * math.pi / -180 tmpGeom = self.actualPointOrientation.asGeometry() self.rotateTool = transformGeometry() epsg = self.canvas.mapSettings().destinationCrs().authid() self.dumLayer = QgsVectorLayer("Point?crs=" + epsg, "temporary_points", "memory") self.actualPointOrientation.setToGeometry( self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle), self.dumLayer) def setPosition(self): """Set RubberBand Position""" # Transform Point originalPoint = self.selected_features.geometry().asPoint() self.actualPointDx = qgsutils.convertProjection( originalPoint.x(), originalPoint.y(), "EPSG:4326", self.canvas.mapSettings().destinationCrs().authid(), ) self.positionDx = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PointGeometry) self.positionDx.setWidth(6) self.positionDx.setIcon(QgsRubberBand.ICON_CIRCLE) self.positionDx.setIconSize(6) self.positionDx.setColor(Qt.black) self.positionSx = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PointGeometry) self.positionSx.setWidth(5) self.positionSx.setIcon(QgsRubberBand.ICON_CIRCLE) self.positionSx.setIconSize(4) self.positionSx.setColor(Qt.blue) self.positionInt = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PointGeometry) self.positionInt.setWidth(5) self.positionInt.setIcon(QgsRubberBand.ICON_CIRCLE) self.positionInt.setIconSize(3) self.positionInt.setColor(Qt.white) self.positionDx.addPoint(self.actualPointDx) self.positionSx.addPoint(self.actualPointDx) self.positionInt.addPoint(self.actualPointDx) def closeEvent(self, _): """Close dialog""" self.resetQgsRubberBand() self.canvas.refresh() self.iface.actionPan().trigger() self.parent.orbitalViewer = None self.RemoveImage() def resetQgsRubberBand(self): """Remove RubbeBand""" try: self.yawLbl.setText("") self.positionSx.reset() self.positionInt.reset() self.positionDx.reset() self.actualPointOrientation.reset() except Exception: None
class FinderBox(QComboBox): running = False toFinish = 0 searchStarted = pyqtSignal() searchFinished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.resultView = QTreeView() self.resultView.setHeaderHidden(True) self.resultView.setMinimumHeight(300) self.resultView.activated.connect(self.itemActivated) self.resultView.pressed.connect(self.itemPressed) self.setView(self.resultView) self.resultModel = ResultModel(self) self.setModel(self.resultModel) self.finders = finders for finder in self.finders.values(): finder.resultFound.connect(self.resultFound) finder.limitReached.connect(self.limitReached) finder.finished.connect(self.finished) self.clearButton = QPushButton(self) self.clearButton.setIcon(QIcon(":/plugins/quickfinder/icons/draft.svg")) self.clearButton.setText('') self.clearButton.setFlat(True) self.clearButton.setCursor(QCursor(Qt.ArrowCursor)) self.clearButton.setStyleSheet('border: 0px; padding: 0px;') self.clearButton.clicked.connect(self.clear) layout = QHBoxLayout(self) self.setLayout(layout) layout.addStretch() layout.addWidget(self.clearButton) layout.addSpacing(20) buttonSize = self.clearButton.sizeHint() # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) padding = buttonSize.width() # + frameWidth + 1 self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def clearSelection(self): self.resultModel.setSelected(None, self.resultView.palette()) self.rubber.reset() def clear(self): self.clearSelection() self.resultModel.clearResults() self.lineEdit().setText('') def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clearSelection() QComboBox.keyPressEvent(self, event) def search(self): if self.running: return toFind = self.lineEdit().text() if not toFind or toFind == '': return self.running = True self.searchStarted.emit() self.clearSelection() self.resultModel.clearResults() self.resultModel.truncateHistory(MySettings().value("historyLength")) self.resultModel.setLoading(True) self.showPopup() QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.findersToStart = [] for finder in self.finders.values(): if finder.activated(): self.findersToStart.append(finder) bbox = self.mapCanvas.fullExtent() while len(self.findersToStart) > 0: finder = self.findersToStart[0] self.findersToStart.remove(finder) self.resultModel.addResult(finder.name) finder.start(toFind, bbox=bbox) # For case there is no finder activated self.finished(None) def stop(self): self.findersToStart = [] for finder in self.finders.values(): if finder.isRunning(): finder.stop() self.finished(None) def resultFound(self, finder, layername, value, geometry, srid): self.resultModel.addResult(finder.name, layername, value, geometry, srid) self.resultView.expandAll() def limitReached(self, finder, layername): self.resultModel.addEllipsys(finder.name, layername) def finished(self, finder): if len(self.findersToStart) > 0: return for finder in self.finders.values(): if finder.isRunning(): return self.running = False self.searchFinished.emit() self.resultModel.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def itemActivated(self, index): item = self.resultModel.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.resultModel.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.resultModel.setSelected(item, self.resultView.palette()) geometry = self.transformGeom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoomToRubberBand() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.resultModel.setSelected(item, self.resultView.palette()) self.rubber.reset(child.geometry.type()) for i in xrange(0, item.rowCount()): geometry = self.transformGeom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoomToRubberBand() return if item.__class__.__name__ == 'QStandardItem': self.clearSelection() def transformGeom(self, item): src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapRenderer().destinationCrs() geom = QgsGeometry(item.geometry) geom.transform(QgsCoordinateTransform(src_crs, dest_crs)) return geom def zoomToRubberBand(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class QuickFinder(QObject): name = u"&Quick Finder" actions = None toolbar = None finders = {} loadingIcon = None def __init__(self, iface): QObject.__init__(self) self.iface = iface self.actions = {} self.finders = {} self.settings = MySettings() self.rubber = None self._init_finders() self.iface.projectRead.connect(self._reload_finders) self.iface.newProjectCreated.connect(self._reload_finders) # translation environment self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(self.plugin_dir, 'i18n', 'quickfinder_{0}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) def initGui(self): self.actions['showSettings'] = QAction( QIcon(":/plugins/quickfinder/icons/settings.svg"), self.tr(u"&Settings"), self.iface.mainWindow()) self.actions['showSettings'].triggered.connect(self.show_settings) self.iface.addPluginToMenu(self.name, self.actions['showSettings']) self.actions['help'] = QAction( QIcon(":/plugins/quickfinder/icons/help.svg"), self.tr("Help"), self.iface.mainWindow()) self.actions['help'].triggered.connect( lambda: QDesktopServices().openUrl( QUrl("http://3nids.github.io/quickfinder"))) self.iface.addPluginToMenu(self.name, self.actions['help']) self._init_toolbar() self.rubber = QgsRubberBand(self.iface.mapCanvas()) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) def unload(self): """ Unload plugin """ for key in self.finders.keys(): self.finders[key].close() for action in self.actions.itervalues(): self.iface.removePluginMenu(self.name, action) if self.toolbar: del self.toolbar if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def _init_toolbar(self): """setup the plugin toolbar.""" self.toolbar = self.iface.addToolBar(self.name) self.toolbar.setObjectName('mQuickFinderToolBar') self.search_action = QAction(QIcon(":/plugins/quickfinder/icons/magnifier13.svg"), self.tr("Search"), self.toolbar) self.stop_action = QAction(QIcon(":/plugins/quickfinder/icons/wrong2.svg"), self.tr("Cancel"), self.toolbar) self.finder_box = FinderBox(self.finders, self.iface, self.toolbar) self.finder_box.search_started.connect(self.search_started) self.finder_box.search_finished.connect(self.search_finished) self.finder_box_action = self.toolbar.addWidget(self.finder_box) self.finder_box_action.setVisible(True) self.search_action.triggered.connect(self.finder_box.search) self.toolbar.addAction(self.search_action) self.stop_action.setVisible(False) self.stop_action.triggered.connect(self.finder_box.stop) self.toolbar.addAction(self.stop_action) self.toolbar.setVisible(True) def _init_finders(self): self.finders['geomapfish'] = GeomapfishFinder(self) self.finders['osm'] = OsmFinder(self) self.finders['project'] = ProjectFinder(self) for key in self.finders.keys(): self.finders[key].message.connect(self.display_message) self.refresh_project() def _reload_finders(self): for key in self.finders.keys(): self.finders[key].close() self.finders[key].reload() self.refresh_project() @pyqtSlot(str, QgsMessageBar.MessageLevel) def display_message(self, message, level): self.iface.messageBar().pushMessage("QuickFinder", message, level) def show_settings(self): if ConfigurationDialog().exec_(): self._reload_finders() def search_started(self): self.search_action.setVisible(False) self.stop_action.setVisible(True) def search_finished(self): self.search_action.setVisible(True) self.stop_action.setVisible(False) def refresh_project(self): if not self.finders['project'].activated: return if not self.settings.value("refreshAuto"): return n_days = self.settings.value("refreshDelay") # do not ask more ofen than 3 days ask_limit = min(3, n_days) recently_asked = self.settings.value("refreshLastAsked") >= n_days_ago_iso_date(ask_limit) if recently_asked: return thresh_date = n_days_ago_iso_date(n_days) uptodate = True for search in self.finders['project'].searches.values(): if search.dateEvaluated <= thresh_date: uptodate = False break if uptodate: return self.settings.setValue("refreshLastAsked", n_days_ago_iso_date(0)) ret = QMessageBox(QMessageBox.Warning, "Quick Finder", QCoreApplication.translate("Auto Refresh", "Some searches are outdated. Do you want to refresh them ?"), QMessageBox.Cancel | QMessageBox.Yes).exec_() if ret == QMessageBox.Yes: RefreshDialog(self.finders['project']).exec_()
class GeodesicMeasureDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent): super(GeodesicMeasureDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.canvas = iface.mapCanvas() qset = QSettings() self.restoreGeometry( qset.value("ShapeTools/MeasureDialogGeometry", QByteArray(), type=QByteArray)) self.closeButton.clicked.connect(self.closeDialog) self.newButton.clicked.connect(self.newDialog) self.saveToLayerButton.clicked.connect(self.saveToLayer) self.saveToLayerButton.setEnabled(False) self.unitsComboBox.addItems(DISTANCE_LABELS) self.tableWidget.setColumnCount(3) self.tableWidget.setSortingEnabled(False) self.tableWidget.setHorizontalHeaderLabels( [tr('Heading To'), tr('Heading From'), tr('Distance')]) self.unitsComboBox.activated.connect(self.unitsChanged) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.lastMotionPt = None self.unitsChanged() self.currentDistance = 0.0 self.pointRb = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.pointRb.setColor(settings.rubberBandColor) self.pointRb.setIconSize(10) self.lineRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.lineRb.setColor(settings.rubberBandColor) self.lineRb.setWidth(3) self.tempRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRb.setColor(settings.rubberBandColor) self.tempRb.setWidth(3) def ready(self): return self.activeMeasuring def stop(self): self.activeMeasuring = False self.lastMotionPt = None def closeEvent(self, event): self.closeDialog() def closeDialog(self): self.clear() QSettings().setValue("ShapeTools/MeasureDialogGeometry", self.saveGeometry()) self.close() def newDialog(self): self.clear() self.initGeodLabel() def initGeodLabel(self): label = tr('Ellipsoid: ') + settings.ellipseDescription self.geodLabel.setText(label) def keyPressed(self, key): index = len(self.capturedPoints) if index <= 0: return if self.motionReady(): if self.lastMotionPt == None: return (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.lastMotionPt) else: if index < 2: return (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 2], self.capturedPoints[index - 1]) distance = self.unitDistance(distance) clipboard = QApplication.clipboard() if key == Qt.Key_1 or key == Qt.Key_F: s = '{:.{prec}f}'.format(startAngle, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Heading to {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_2 or key == Qt.Key_T: s = '{:.{prec}f}'.format(endAngle, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Heading from {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_3 or key == Qt.Key_D: s = '{:.{prec}f}'.format(distance, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Distance {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_4 or key == Qt.Key_A: total = 0.0 num = len(self.capturedPoints) for i in range(1, num): (d, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) total += d total = self.unitDistance(total) # Add in the motion distance if self.motionReady(): total += distance s = '{:.{prec}f}'.format(total, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Total distance {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) else: return def unitsChanged(self): label = "Distance [{}]".format( DISTANCE_LABELS[self.unitsComboBox.currentIndex()]) item = QTableWidgetItem(label) self.tableWidget.setHorizontalHeaderItem(2, item) ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: item = QTableWidgetItem('{:.4f}'.format( self.unitDistance(self.distances[i]))) self.tableWidget.setItem(i, 2, item) i += 1 self.formatTotal() def motionReady(self): if len(self.capturedPoints) > 0 and self.activeMeasuring: return True return False def addPoint(self, pt, button): self.currentDistance = 0 index = len(self.capturedPoints) if index > 0 and pt == self.capturedPoints[index - 1]: # the clicked point is the same as the previous so just ignore it return self.capturedPoints.append(pt) # Add rubber band points canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) ptCanvas = transform.transform(pt.x(), pt.y()) self.pointRb.addPoint(ptCanvas, True) # If there is more than 1 point add it to the table if index > 0: self.saveToLayerButton.setEnabled(True) (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.capturedPoints[index]) self.distances.append(distance) self.insertParams(index, distance, startAngle, endAngle) # Add Rubber Band Line linePts = self.getLinePts(distance, self.capturedPoints[index - 1], self.capturedPoints[index]) self.lineRb.addGeometry(QgsGeometry.fromPolylineXY(linePts), None) self.formatTotal() def inMotion(self, pt): index = len(self.capturedPoints) if index <= 0: return (self.currentDistance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], pt) self.insertParams(index, self.currentDistance, startAngle, endAngle) self.formatTotal() linePts = self.getLinePts(self.currentDistance, self.capturedPoints[index - 1], pt) self.lastMotionPt = pt self.tempRb.setToGeometry(QgsGeometry.fromPolylineXY(linePts), None) def calcParameters(self, pt1, pt2): l = geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x()) az2 = (l['azi2'] + 180) % 360.0 if az2 > 180: az2 = az2 - 360.0 az1 = l['azi1'] # Check to see if the azimuth values should be in the range or 0 to 360 # The default is -180 to 180 if settings.mtAzMode: if az1 < 0: az1 += 360.0 if az2 < 0: az2 += 360 return (l['s12'], az1, az2) def getLinePts(self, distance, pt1, pt2): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) pt1c = transform.transform(pt1.x(), pt1.y()) pt2c = transform.transform(pt2.x(), pt2.y()) if distance < 10000: return [pt1c, pt2c] l = geod.InverseLine(pt1.y(), pt1.x(), pt2.y(), pt2.x()) n = int(math.ceil(distance / 10000.0)) if n > 20: n = 20 seglen = distance / n pts = [pt1c] for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) ptc = transform.transform(g['lon2'], g['lat2']) pts.append(ptc) pts.append(pt2c) return pts def saveToLayer(self): units = self.unitDesignator() canvasCrs = self.canvas.mapSettings().destinationCrs() fields = QgsFields() fields.append(QgsField("label", QVariant.String)) fields.append(QgsField("value", QVariant.Double)) fields.append(QgsField("units", QVariant.String)) fields.append(QgsField("heading_to", QVariant.Double)) fields.append(QgsField("heading_from", QVariant.Double)) fields.append(QgsField("total_dist", QVariant.Double)) layer = QgsVectorLayer("LineString?crs={}".format(canvasCrs.authid()), "Measurements", "memory") dp = layer.dataProvider() dp.addAttributes(fields) layer.updateFields() num = len(self.capturedPoints) total = 0.0 for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) total += distance total = self.unitDistance(total) for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) pts = self.getLinePts(distance, self.capturedPoints[i - 1], self.capturedPoints[i]) distance = self.unitDistance(distance) feat = QgsFeature(layer.fields()) feat.setAttribute(0, "{:.2f} {}".format(distance, units)) feat.setAttribute(1, distance) feat.setAttribute(2, units) feat.setAttribute(3, startA) feat.setAttribute(4, endA) feat.setAttribute(5, total) feat.setGeometry(QgsGeometry.fromPolylineXY(pts)) dp.addFeatures([feat]) label = QgsPalLayerSettings() label.fieldName = 'label' try: label.placement = QgsPalLayerSettings.Line except: label.placement = QgsPalLayerSettings.AboveLine format = label.format() format.setColor(settings.measureTextColor) format.setNamedStyle('Bold') label.setFormat(format) labeling = QgsVectorLayerSimpleLabeling(label) layer.setLabeling(labeling) layer.setLabelsEnabled(True) renderer = layer.renderer() renderer.symbol().setColor(settings.measureLineColor) renderer.symbol().setWidth(0.5) layer.updateExtents() QgsProject.instance().addMapLayer(layer) def insertParams(self, position, distance, startAngle, endAngle): if position > self.tableWidget.rowCount(): self.tableWidget.insertRow(position - 1) item = QTableWidgetItem('{:.4f}'.format(self.unitDistance(distance))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 2, item) item = QTableWidgetItem('{:.4f}'.format(startAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 0, item) item = QTableWidgetItem('{:.4f}'.format(endAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 1, item) def formatTotal(self): total = self.currentDistance ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: total += self.distances[i] i += 1 self.distanceLineEdit.setText('{:.2f}'.format( self.unitDistance(total))) def updateRBColor(self): self.pointRb.setColor(settings.rubberBandColor) self.lineRb.setColor(settings.rubberBandColor) self.tempRb.setColor(settings.rubberBandColor) def clear(self): self.tableWidget.setRowCount(0) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.currentDistance = 0.0 self.distanceLineEdit.setText('') self.pointRb.reset(QgsWkbTypes.PointGeometry) self.lineRb.reset(QgsWkbTypes.LineGeometry) self.tempRb.reset(QgsWkbTypes.LineGeometry) self.saveToLayerButton.setEnabled(False) self.updateRBColor() def unitDistance(self, distance): units = self.unitsComboBox.currentIndex() if units == 0: # kilometers return distance / 1000.0 elif units == 1: # meters return distance elif units == 2: # centimeters return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceCentimeters) elif units == 3: # miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceMiles) elif units == 4: # yards return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceYards) elif units == 5: # feet return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) elif units == 6: # inches return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) * 12 elif units == 7: # nautical miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceNauticalMiles) def unitDesignator(self): units = self.unitsComboBox.currentIndex() return unitsAbbr[units]
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) if hasattr(self.canvas, 'setParallelRenderingEnabled'): self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) gpsspacewidget= QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(0, 0, 212, 76)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.featureformloaded.connect(self.feature_form_loaded) self.connectButtons() def init_qgisproject(self, doc): parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() return self.canvas.mapRenderer().destinationCrs() def showEvent(self, *args, **kwargs): if self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, project, editmode): self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def highlight_active_selection(self, layer, feature, features): self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def clear_selection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position) def gps_first_fix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): self.marker.hide() def select_data_entry(self): def showformerror(form): pass def actions(): for form in self.project.forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form") formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): self.project = project self.actionPan.trigger() try: firstform = project.forms[0] self.load_form(firstform) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.freeze(False) self.canvas.refresh() def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def load_form(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value RoamEvents.open_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class QMap(): def __init__(self, iface): self.iface = iface self.actions = [] self.panels= [] self.navtoolbar = self.iface.mapNavToolToolBar() self.mainwindow = self.iface.mainWindow() self.iface.projectRead.connect(self.projectOpened) self.iface.initializationCompleted.connect(self.setupUI) self.actionGroup = QActionGroup(self.mainwindow) self.actionGroup.setExclusive(True) self.menuGroup = QActionGroup(self.mainwindow) self.menuGroup.setExclusive(True) self.movetool = MoveTool(self.iface.mapCanvas(), []) self.infotool = InfoTool(self.iface.mapCanvas()) self.infotool.infoResults.connect(self.showInfoResults) self.report = PopDownReport(self.iface.messageBar()) self.dialogprovider = DialogProvider(iface.mapCanvas(), iface) self.dialogprovider.accepted.connect(self.clearToolRubberBand) self.dialogprovider.rejected.connect(self.clearToolRubberBand) self.edittool = EditTool(self.iface.mapCanvas(),[]) self.edittool.finished.connect(self.openForm) self.edittool.featuresfound.connect(self.showFeatureSelection) self.infodock = InfoDock(self.iface.mainWindow()) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.infodock) self.infodock.hide() self.band = QgsRubberBand(self.iface.mapCanvas()) self.band.setIconSize(20) self.band.setWidth(10) self.band.setColor(QColor(186, 93, 212, 76)) def showFeatureSelection(self, features): listUi = ListFeaturesForm(self.mainwindow) listUi.loadFeatureList(features) listUi.openFeatureForm.connect(self.openForm) listUi.exec_() def showInfoResults(self, results): self.infodock.clearResults() self.infodock.setResults(results) self.infodock.show() self.infodock.repaint() @property def _mapLayers(self): return QgsMapLayerRegistry.instance().mapLayers() def clearToolRubberBand(self): tool = self.iface.mapCanvas().mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def missingLayers(self, layers): def showError(): html = ["<h1>Missing Layers</h1>", "<ul>"] for layer in layers: html.append("<li>{}</li>".format(layer)) html.append("</ul>") self.errorreport.updateHTML("".join(html)) message = "Seems like {} didn't load correctly".format(utils._pluralstring('layer', len(layers))) utils.warning("Missing layers") map(utils.warning, layers) self.widget = self.iface.messageBar().createMessage("Missing Layers", message, QIcon(":/icons/sad")) button = QPushButton(self.widget) button.setCheckable(True) button.setChecked(self.errorreport.isVisible()) button.setText("Show missing layers") button.toggled.connect(showError) button.toggled.connect(functools.partial(self.errorreport.setVisible)) self.widget.destroyed.connect(self.hideReports) self.widget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.widget, QgsMessageBar.WARNING) def excepthook(self, ex_type, value, tb): """ Custom exception hook so that we can handle errors in a nicer way """ where = ''.join(traceback.format_tb(tb)) msg = '{}'.format(value) utils.critical(msg) def showError(): html = """ <html> <body bgcolor="#FFEDED"> <p><b>{}</b></p> <p align="left"><small>{}</small></p> </body> </html> """.format(msg, where) self.errorreport.updateHTML(html) self.widget = self.iface.messageBar().createMessage("oops", "Looks like an error occurred", QIcon(":/icons/sad")) button = QPushButton(self.widget) button.setCheckable(True) button.setChecked(self.errorreport.isVisible()) button.setText("Show error") button.toggled.connect(showError) button.toggled.connect(functools.partial(self.errorreport.setVisible)) self.widget.destroyed.connect(self.hideReports) self.widget.layout().addWidget(button) self.messageBar.pushWidget(self.widget, QgsMessageBar.CRITICAL) def hideReports(self): self.errorreport.setVisible(False) self.report.setVisible(False) def setupUI(self): """ Set up the main QGIS interface items. Called after QGIS has loaded the plugin. """ self.updateAppSize() utils.settings_notify.settings_changed.connect(self.updateAppSize) self.navtoolbar.setMovable(False) self.navtoolbar.setAllowedAreas(Qt.TopToolBarArea) self.mainwindow.insertToolBar(self.toolbar, self.navtoolbar) self.openProjectAction.trigger() def updateAppSize(self): fullscreen = utils.settings.get("fullscreen", False) if fullscreen: self.mainwindow.showFullScreen() else: self.mainwindow.showMaximized() def setMapTool(self, tool): """ Set the current mapview canvas tool tool -- The QgsMapTool to set """ self.iface.mapCanvas().setMapTool(tool) def createToolBars(self): """ Create all the needed toolbars """ self.menutoolbar = QToolBar("Menu", self.mainwindow) self.menutoolbar.setMovable(False) self.menutoolbar.setAllowedAreas(Qt.LeftToolBarArea) self.mainwindow.addToolBar(Qt.LeftToolBarArea, self.menutoolbar) self.toolbar = QToolBar("QMap", self.mainwindow) self.mainwindow.addToolBar(Qt.TopToolBarArea, self.toolbar) self.toolbar.setMovable(False) self.editingtoolbar = FloatingToolBar("Editing", self.toolbar) self.extraaddtoolbar = FloatingToolBar("Extra Add Tools", self.toolbar) self.syncactionstoolbar = FloatingToolBar("Syncing", self.toolbar) self.syncactionstoolbar.setOrientation(Qt.Vertical) def createActions(self): """ Create all the actions """ self.homeAction = (QAction(QIcon(":/icons/zoomfull"), "Default View", self.mainwindow)) self.gpsAction = (GPSAction(QIcon(":/icons/gps"), self.iface.mapCanvas(), self.mainwindow)) self.openProjectAction = (QAction(QIcon(":/icons/open"), "Projects", self.mainwindow)) self.openProjectAction.setCheckable(True) self.configAction = (QAction(QIcon(":/icons/config"), "Settings", self.mainwindow)) self.configAction.setCheckable(True) self.toggleRasterAction = (QAction(QIcon(":/icons/photo"), "Aerial Photos", self.mainwindow)) self.syncAction = QAction(QIcon(":/icons/sync"), "Sync", self.mainwindow) self.syncAction.setVisible(False) self.editattributesaction = QAction(QIcon(":/icons/edit"), "Edit Attributes", self.mainwindow) self.editattributesaction.setCheckable(True) self.editattributesaction.toggled.connect(functools.partial(self.setMapTool, self.edittool)) self.moveaction = QAction(QIcon(":/icons/move"), "Move Feature", self.mainwindow) self.moveaction.setCheckable(True) self.editingmodeaction = QAction(QIcon(":/icons/edittools"), "Edit Tools", self.mainwindow) self.editingmodeaction.setCheckable(True) self.infoaction = QAction(QIcon(":/icons/info"), "Info", self.mainwindow) self.infoaction.setCheckable(True) self.addatgpsaction = QAction(QIcon(":/icons/gpsadd"), "Add at GPS", self.mainwindow) self.edittool.layersupdated.connect(self.editattributesaction.setVisible) self.movetool.layersupdated.connect(self.moveaction.setVisible) self.movetool.layersupdated.connect(self.editingmodeaction.setVisible) def initGui(self): """ Create all the icons and setup the tool bars. Called by QGIS when loading. This is called before setupUI. """ QApplication.setWindowIcon(QIcon(":/branding/logo")) self.mainwindow.findChildren(QMenuBar)[0].setVisible(False) self.mainwindow.setContextMenuPolicy(Qt.PreventContextMenu) self.mainwindow.setWindowTitle("IntraMaps Roam: Mobile Data Collection") # Disable QGIS logging window popups. We do our own logging QgsMessageLog.instance().messageReceived.disconnect() s = """ QToolButton { padding: 6px; color: #4f4f4f; } QToolButton:hover { padding: 6px; background-color: rgb(211, 228, 255); } QToolBar { background: white; } QCheckBox::indicator { width: 40px; height: 40px; } QLabel { color: #4f4f4f; } QDialog { background-color: rgb(255, 255, 255); } QPushButton { border: 1px solid #e1e1e1; padding: 6px; color: #4f4f4f; } QPushButton:hover { border: 1px solid #e1e1e1; padding: 6px; background-color: rgb(211, 228, 255); } QCheckBox { color: #4f4f4f; } QComboBox::drop-down { width: 30px; } QComboBox { border: 1px solid #d3d3d3; } QStackedWidget { background-color: rgb(255, 255, 255); } """ self.mainwindow.setStyleSheet(s) mainwidget = self.mainwindow.centralWidget() mainwidget.setLayout(QGridLayout()) mainwidget.layout().setContentsMargins(0,0,0,0) newlayout = QGridLayout() newlayout.setContentsMargins(0,0,0,0) newlayout.addWidget(self.iface.mapCanvas(), 0,0,2,1) newlayout.addWidget(self.iface.messageBar(), 0,0,1,1) wid = QWidget() wid.setLayout(newlayout) self.stack = QStackedWidget(self.mainwindow) self.messageBar = QgsMessageBar(wid) self.messageBar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) self.errorreport = PopDownReport(self.messageBar) mainwidget.layout().addWidget(self.stack, 0,0,2,1) mainwidget.layout().addWidget(self.messageBar, 0,0,1,1) self.helppage = HelpPage() helppath = os.path.join(os.path.dirname(__file__) , 'help',"help.html") self.helppage.setHelpPage(helppath) self.settingswidget = SettingsWidget(self.stack) self.projectwidget = ProjectsWidget() self.projectwidget.requestOpenProject.connect(self.loadProject) self.stack.addWidget(wid) self.stack.addWidget(self.projectwidget) self.stack.addWidget(self.helppage) self.stack.addWidget(self.settingswidget) sys.excepthook = self.excepthook def createSpacer(width=30): widget = QWidget() widget.setMinimumWidth(width) return widget self.createToolBars() self.createActions() spacewidget = createSpacer(60) gpsspacewidget = createSpacer() gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.moveaction.toggled.connect(functools.partial(self.setMapTool, self.movetool)) self.infoaction.toggled.connect(functools.partial(self.setMapTool, self.infotool)) showediting = (functools.partial(self.editingtoolbar.showToolbar, self.editingmodeaction, self.moveaction)) self.editingmodeaction.toggled.connect(showediting) self.addatgpsaction.triggered.connect(self.addAtGPS) self.addatgpsaction.setEnabled(self.gpsAction.isConnected) self.gpsAction.gpsfixed.connect(self.addatgpsaction.setEnabled) self.editingtoolbar.addToActionGroup(self.moveaction) self.actionGroup.addAction(self.editingmodeaction) self.actionGroup.addAction(self.editattributesaction) self.actionGroup.addAction(self.infoaction) self.homeAction.triggered.connect(self.zoomToDefaultView) self.openProjectAction.triggered.connect(self.showOpenProjectDialog) self.openProjectAction.triggered.connect(functools.partial(self.stack.setCurrentIndex, 1)) self.configAction.triggered.connect(functools.partial(self.stack.setCurrentIndex, 3)) self.configAction.triggered.connect(self.settingswidget.populateControls) self.configAction.triggered.connect(self.settingswidget.readSettings) self.toggleRasterAction.triggered.connect(self.toggleRasterLayers) self.navtoolbar.insertAction(self.iface.actionZoomIn(), self.iface.actionTouch()) self.navtoolbar.insertAction(self.iface.actionTouch(), self.homeAction) self.navtoolbar.insertAction(self.iface.actionTouch(), self.iface.actionZoomFullExtent()) self.navtoolbar.insertAction(self.homeAction, self.iface.actionZoomFullExtent()) self.navtoolbar.addAction(self.toggleRasterAction) self.navtoolbar.insertWidget(self.iface.actionZoomFullExtent(), spacewidget) self.toolbar.addAction(self.infoaction) self.toolbar.addAction(self.editingmodeaction) self.toolbar.addAction(self.editattributesaction) self.toolbar.addAction(self.syncAction) self.toolbar.addAction(self.gpsAction) self.toolbar.insertWidget(self.syncAction, gpsspacewidget) self.toolbar.insertSeparator(self.gpsAction) self.extraaddtoolbar.addAction(self.addatgpsaction) self.editingtoolbar.addAction(self.moveaction) self.mapview = QAction(QIcon(":/icons/map"), "Map", self.menutoolbar) self.mapview.setCheckable(True) self.mapview.triggered.connect(functools.partial(self.stack.setCurrentIndex, 0)) self.help = QAction(QIcon(":/icons/help"), "Help", self.menutoolbar) self.help.setCheckable(True) self.help.triggered.connect(functools.partial(self.stack.setCurrentIndex, 2)) self.help.setVisible(False) self.projectlabel = QLabel("Project: <br> None") self.projectlabel.setAlignment(Qt.AlignCenter) self.projectlabel.setStyleSheet(""" QLabel { color: #8c8c8c; font: 10px "Calibri" ; }""") self.userlabel = QLabel("User: <br> {user}".format(user=getpass.getuser())) self.userlabel.setAlignment(Qt.AlignCenter) self.userlabel.setStyleSheet(""" QLabel { color: #8c8c8c; font: 10px "Calibri" ; }""") self.quit = QAction(QIcon(":/icons/quit"), "Quit", self.menutoolbar) self.quit.triggered.connect(self.iface.actionExit().trigger) self.menuGroup.addAction(self.mapview) self.menuGroup.addAction(self.openProjectAction) self.menuGroup.addAction(self.help) self.menuGroup.addAction(self.configAction) self.menutoolbar.addAction(self.mapview) self.menutoolbar.addAction(self.openProjectAction) self.menutoolbar.addAction(self.help) self.menutoolbar.addAction(self.configAction) self.menutoolbar.addAction(self.quit) quitspacewidget = createSpacer() quitspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) labelaction = self.menutoolbar.insertWidget(self.configAction, self.userlabel) self.menutoolbar.insertWidget(labelaction, quitspacewidget) self.menutoolbar.insertWidget(labelaction, self.projectlabel) self.setupIcons() self.stack.currentChanged.connect(self.updateUIState) def updateUIState(self, page): """ Update the UI state to reflect the currently selected page in the stacked widget """ def setToolbarsActive(enabled): toolbars = self.mainwindow.findChildren(QToolBar) for toolbar in toolbars: if toolbar == self.menutoolbar: continue toolbar.setEnabled(enabled) def setPanelsVisible(visible): for panel in self.panels: panel.setVisible(visible) ismapview = page == 0 setToolbarsActive(ismapview) setPanelsVisible(ismapview) self.infodock.hide() def addAtGPS(self): """ Add a record at the current GPS location. """ action = self.actionGroup.checkedAction() if not action: return layer = action.data() if not layer: return point = self.gpsAction.position self.addNewFeature(layer=layer, geometry=point) def zoomToDefaultView(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.iface.mapCanvas().setExtent(self.defaultextent) self.iface.mapCanvas().refresh() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ legend = self.iface.legendInterface() #Freeze the canvas to save on UI refresh self.iface.mapCanvas().freeze() for layer in self._mapLayers.values(): if layer.type() == QgsMapLayer.RasterLayer: isvisible = legend.isLayerVisible(layer) legend.setLayerVisible(layer, not isvisible) self.iface.mapCanvas().freeze(False) self.iface.mapCanvas().refresh() def setupIcons(self): """ Update toolbars to have text and icons, change normal QGIS icons to new style """ toolbars = self.mainwindow.findChildren(QToolBar) for toolbar in toolbars: toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolbar.setIconSize(QSize(32, 32)) self.iface.actionTouch().setIconText("Pan") self.iface.actionTouch().setIcon(QIcon(":/icons/pan")) self.iface.actionZoomIn().setIcon(QIcon(":/icons/in")) self.iface.actionZoomOut().setIcon(QIcon(":/icons/out")) self.iface.actionPan().setIcon(QIcon(":/icons/pan")) self.iface.actionZoomFullExtent().setIcon(QIcon(":/icons/home")) self.iface.actionZoomFullExtent().setIconText("Home View") self.actionGroup.addAction(self.iface.actionZoomIn()) self.actionGroup.addAction(self.iface.actionZoomOut()) self.actionGroup.addAction(self.iface.actionTouch()) def projectOpened(self): """ Called when a new project is opened in QGIS. """ for panel in self.panels: self.mainwindow.removeDockWidget(panel) del panel projectpath = QgsProject.instance().fileName() project = QMapProject(os.path.dirname(projectpath), self.iface) self.projectlabel.setText("Project: <br> {}".format(project.name)) self.createFormButtons(projectlayers = project.getConfiguredLayers()) # Enable the raster layers button only if the project contains a raster layer. hasrasters = any(layer.type() for layer in self._mapLayers.values()) self.toggleRasterAction.setEnabled(hasrasters) self.defaultextent = self.iface.mapCanvas().extent() self.connectSyncProviders(project) # Show panels self.panels = list(project.getPanels()) for panel in self.panels: self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea , panel) self.iface.messageBar().popWidget() def captureLayer(self, layer): text = layer.icontext tool = layer.getMaptool(self.iface.mapCanvas()) # Hack until I fix it later if isinstance(tool, PointTool): add = functools.partial(self.addNewFeature, qgslayer) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(functools.partial(self.showToolError, text)) action = QAction(QIcon(layer.icon), text, self.mainwindow) action.setData(layer) action.setCheckable(True) action.toggled.connect(functools.partial(self.setMapTool, tool)) self.toolbar.insertAction(self.editingmodeaction, action) if not tool.isEditTool(): # Connect the GPS tools strip to the action pressed event. showgpstools = (functools.partial(self.extraaddtoolbar.showToolbar, action, None)) action.toggled.connect(showgpstools) self.actionGroup.addAction(action) self.actions.append(action) def editLayer(self, layer): self.edittool.addLayer(layer.QGISLayer) self.edittool.searchRadius = 10 def moveLayer(self, layer): self.movetool.addLayer(layer.QGISLayer) def createFormButtons(self, projectlayers): """ Create buttons for each form that is definded """ # Remove all the old buttons for action in self.actions: self.actionGroup.removeAction(action) self.toolbar.removeAction(action) self.edittool.layers = [] self.movetool.layers = [] capabilitityhandlers = { "capture" : self.captureLayer, "edit" : self.editLayer, "move" : self.moveLayer} for layer in projectlayers: try: qgslayer = QgsMapLayerRegistry.instance().mapLayersByName(layer.name)[0] if qgslayer.type() == QgsMapLayer.RasterLayer: utils.log("We can't support raster layers for data entry") continue layer.QGISLayer = qgslayer except IndexError: utils.log("Layer {} not found in project".format(layer.name)) continue for capability in layer.capabilities: try: capabilitityhandlers[capability](layer) except NoMapToolConfigured: utils.log("No map tool configured") continue except ErrorInMapTool as error: self.iface.messageBar().pushMessage("Error configuring map tool", error.message, level=QgsMessageBar.WARNING) continue def showToolError(self, label, message): self.iface.messageBar().pushMessage(label, message, QgsMessageBar.WARNING) def openForm(self, layer, feature): if not layer.isEditable(): layer.startEditing() self.band.setToGeometry(feature.geometry(), layer) self.dialogprovider.openDialog(feature=feature, layer=layer) self.band.reset() def addNewFeature(self, layer, geometry): fields = layer.pendingFields() feature = QgsFeature() feature.setGeometry( geometry ) feature.initAttributes(fields.count()) feature.setFields(fields) for indx in xrange(fields.count()): feature[indx] = layer.dataProvider().defaultValue(indx) self.openForm(layer, feature) def showOpenProjectDialog(self): """ Show the project selection dialog. """ self.stack.setCurrentIndex(1) self.infodock.hide() path = os.path.join(os.path.dirname(__file__), '..' , 'projects/') projects = getProjects(path, self.iface) self.projectwidget.loadProjectList(projects) def loadProject(self, project): """ Load a project into QGIS. """ utils.log(project) utils.log(project.name) utils.log(project.projectfile) utils.log(project.vaild) (passed, message) = project.onProjectLoad() if not passed: QMessageBox.warning(self.mainwindow, "Project Load Rejected", "Project couldn't be loaded because {}".format(message)) return self.mapview.trigger() self.iface.newProject(False) self.iface.mapCanvas().freeze() self.infodock.clearResults() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler( self.badLayerHandler ) self.iface.messageBar().pushMessage("Project Loading","", QgsMessageBar.INFO) QApplication.processEvents() fileinfo = QFileInfo(project.projectfile) QgsProject.instance().read(fileinfo) self.iface.mapCanvas().updateScale() self.iface.mapCanvas().freeze(False) self.iface.mapCanvas().refresh() self.mainwindow.setWindowTitle("IntraMaps Roam: Mobile Data Collection") self.iface.projectRead.emit() def unload(self): del self.toolbar def connectSyncProviders(self, project): self.syncactionstoolbar.clear() syncactions = list(project.syncprovders()) # Don't show the sync button if there is no sync providers if not syncactions: self.syncAction.setVisible(False) return self.syncAction.setVisible(True) for provider in syncactions: action = QAction(QIcon(":/icons/sync"), "Sync {}".format(provider.name), self.mainwindow) action.triggered.connect(functools.partial(self.syncProvider, provider)) self.syncactionstoolbar.addAction(action) try: self.syncAction.toggled.disconnect() except TypeError: pass try: self.syncAction.triggered.disconnect() except TypeError: pass if len(syncactions) == 1: # If one provider is set then we just connect the main button. self.syncAction.setCheckable(False) self.syncAction.setText("Sync") self.syncAction.triggered.connect(functools.partial(self.syncProvider, syncactions[0])) else: # the sync button because a sync menu self.syncAction.setCheckable(True) self.syncAction.setText("Sync Menu") showsyncoptions = (functools.partial(self.syncactionstoolbar.showToolbar, self.syncAction, None)) self.syncAction.toggled.connect(showsyncoptions) def syncstarted(self): # Remove the old widget if it's still there. # I don't really like this. Seems hacky. try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass except AttributeError: pass self.iface.messageBar().findChildren(QToolButton)[0].setVisible(False) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync in progress", QIcon(":/icons/syncing")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setText("Status") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) pro = QProgressBar() pro.setMaximum(0) pro.setMinimum(0) self.syncwidget.layout().addWidget(pro) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget, QgsMessageBar.INFO) def synccomplete(self): try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass stylesheet = ("QgsMessageBar { background-color: rgba(239, 255, 233); border: 0px solid #b9cfe4; } " "QLabel,QTextEdit { color: #057f35; } ") closebutton = self.iface.messageBar().findChildren(QToolButton)[0] closebutton.setVisible(True) closebutton.clicked.connect(functools.partial(self.report.setVisible, False)) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync Complete", QIcon(":/icons/syncdone")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setChecked(self.report.isVisible()) button.setText("Sync Report") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) pro = QProgressBar() pro.setMaximum(100) pro.setValue(100) self.syncwidget.layout().addWidget(pro) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget) self.iface.messageBar().setStyleSheet(stylesheet) self.iface.mapCanvas().refresh() def syncerror(self): try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass closebutton = self.iface.messageBar().findChildren(QToolButton)[0] closebutton.setVisible(True) closebutton.clicked.connect(functools.partial(self.report.setVisible, False)) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync Error", QIcon(":/icons/syncfail")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setChecked(self.report.isVisible()) button.setText("Sync Report") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget, QgsMessageBar.CRITICAL) self.iface.mapCanvas().refresh() def syncProvider(self, provider): self.syncAction.toggle() provider.syncStarted.connect(functools.partial(self.syncAction.setEnabled, False)) provider.syncStarted.connect(self.syncstarted) provider.syncComplete.connect(self.synccomplete) provider.syncComplete.connect(functools.partial(self.syncAction.setEnabled, True)) provider.syncComplete.connect(functools.partial(self.report.updateHTML)) provider.syncMessage.connect(self.report.updateHTML) provider.syncError.connect(self.report.updateHTML) provider.syncError.connect(self.syncerror) provider.syncError.connect(functools.partial(self.syncAction.setEnabled, True)) provider.startSync()