Example #1
0
class AddPipeTool(QgsMapTool):
    def __init__(self, data_dock, params):
        QgsMapTool.__init__(self, data_dock.iface.mapCanvas())

        self.iface = data_dock.iface
        """:type : QgisInterface"""
        self.data_dock = data_dock
        """:type : DataDock"""
        self.params = params

        self.mouse_pt = None
        self.mouse_clicked = False
        self.first_click = False
        self.rubber_band = QgsRubberBand(self.canvas(), False)
        self.rubber_band.setColor(QColor(255, 128, 128))
        self.rubber_band.setWidth(1)
        self.rubber_band.setBrushStyle(Qt.Dense4Pattern)
        self.rubber_band.reset()

        self.snapper = None
        self.snapped_feat_id = None
        self.snapped_vertex = None
        self.snapped_vertex_nr = None
        self.vertex_marker = QgsVertexMarker(self.canvas())
        self.elev = None

        self.diameter_dialog = None

    def canvasPressEvent(self, event):
        if event.button() == Qt.RightButton:
            self.mouse_clicked = False

        if event.button() == Qt.LeftButton:
            self.mouse_clicked = True

    def canvasMoveEvent(self, event):

        self.mouse_pt = self.toMapCoordinates(event.pos())

        last_ix = self.rubber_band.numberOfVertices()

        self.rubber_band.movePoint(last_ix - 1,
                                   (self.snapped_vertex if self.snapped_vertex
                                    is not None else self.mouse_pt))

        elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay,
                                                      self.mouse_pt, 1)
        self.elev = elev
        if elev is not None:
            self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev))
        else:
            self.data_dock.lbl_elev_val.setText('-')

        # Mouse not clicked: snapping to closest vertex
        match = self.snapper.snapToMap(self.mouse_pt)

        if match.isValid():
            # Pipe starts from an existing vertex
            self.snapped_feat_id = match.featureId()
            self.snapped_vertex = match.point()
            self.snapped_vertex_nr = match.vertexIndex()

            self.vertex_marker.setCenter(self.snapped_vertex)
            self.vertex_marker.setColor(QColor(255, 0, 0))
            self.vertex_marker.setIconSize(10)
            self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE)
            self.vertex_marker.setPenWidth(3)
            self.vertex_marker.show()
        #
        else:

            # It's a new, isolated pipe
            self.snapped_vertex = None
            self.snapped_feat_id = None
            self.vertex_marker.hide()

    def canvasReleaseEvent(self, event):

        # if not self.mouse_clicked:
        #     return
        if event.button() == Qt.LeftButton:

            # Update rubber bands
            self.rubber_band.addPoint(
                (self.snapped_vertex
                 if self.snapped_vertex is not None else self.mouse_pt), True)
            if self.first_click:
                self.rubber_band.addPoint(
                    (self.snapped_vertex if self.snapped_vertex is not None
                     else self.mouse_pt), True)

            self.first_click = not self.first_click

        elif event.button() == Qt.RightButton:

            # try:

            pipe_band_geom = self.rubber_band.asGeometry()

            # No rubber band geometry and feature snapped: pop the context menu
            if self.rubber_band.size(
            ) == 0 and self.snapped_feat_id is not None:
                menu = QMenu()
                section_action = menu.addAction('Section...')  # TODO: softcode
                diameter_action = menu.addAction(
                    'Change diameter...')  # TODO: softcode
                action = menu.exec_(self.iface.mapCanvas().mapToGlobal(
                    QPoint(event.pos().x(),
                           event.pos().y())))

                pipe_ft = vector_utils.get_feats_by_id(self.params.pipes_vlay,
                                                       self.snapped_feat_id)[0]
                if action == section_action:
                    if self.params.dem_rlay is None:
                        self.iface.messageBar().pushMessage(
                            Parameters.plug_in_name,
                            'No DEM selected. Cannot edit section.',
                            Qgis.Warning, 5)  # TODO: softcode
                    else:
                        # Check whether the pipe is all inside the DEM
                        pipe_pts = pipe_ft.geometry().asPolyline()

                        for pt in pipe_pts:
                            if not self.params.dem_rlay.extent().contains(pt):
                                self.iface.messageBar().pushMessage(
                                    Parameters.plug_in_name,
                                    'Some pipe vertices fall outside of the DEM. Cannot edit section.',
                                    Qgis.Warning, 5)  # TODO: softcode
                                return

                        # Check whether the start/end nodes have an elevation value
                        (start_node_ft,
                         end_node_ft) = NetworkUtils.find_start_end_nodes(
                             self.params, pipe_ft.geometry())
                        start_node_elev = start_node_ft.attribute(
                            Junction.field_name_elev)
                        end_node_elev = end_node_ft.attribute(
                            Junction.field_name_elev)
                        if start_node_elev == NULL or end_node_elev == NULL:
                            self.iface.messageBar().pushMessage(
                                Parameters.plug_in_name,
                                'Missing elevation value in start or end node attributes. Cannot edit section.',
                                Qgis.Warning, 5)  # TODO: softcode
                            return

                        pipe_dialog = PipeSectionDialog(
                            self.iface.mainWindow(), self.iface, self.params,
                            pipe_ft)
                        pipe_dialog.exec_()

                elif action == diameter_action:

                    old_diam = pipe_ft.attribute(Pipe.field_name_diameter)
                    self.diameter_dialog = DiameterDialog(
                        self.iface.mainWindow(), self.params, old_diam)
                    self.diameter_dialog.exec_()  # Exec creates modal dialog
                    new_diameter = self.diameter_dialog.get_diameter()
                    if new_diameter is None:
                        return

                    # Update pipe diameter
                    vector_utils.update_attribute(self.params.pipes_vlay,
                                                  pipe_ft,
                                                  Pipe.field_name_diameter,
                                                  new_diameter)

                    # Check if a valve is present
                    adj_valves = NetworkUtils.find_links_adjacent_to_link(
                        self.params, self.params.pipes_vlay, pipe_ft, True,
                        True, False)

                    if adj_valves['valves']:
                        self.iface.messageBar().pushMessage(
                            Parameters.plug_in_name,
                            'Valves detected on the pipe: need to update their diameters too.',
                            Qgis.Warning, 5)  # TODO: softcode

                return

            # There's a rubber band: create new pipe
            if pipe_band_geom is not None:

                #XPSS ADDED - transform rubber band CRS
                canvas_crs = self.canvas().mapSettings().destinationCrs()
                #pipes_vlay = Parameters().pipes_vlay()
                #print(pipes_vlay)
                qepanet_crs = self.params.pipes_vlay.sourceCrs()
                crs_transform = QgsCoordinateTransform(canvas_crs, qepanet_crs,
                                                       QgsProject.instance())
                pipe_band_geom.transform(crs_transform)

                # Finalize line
                rubberband_pts = pipe_band_geom.asPolyline()[:-1]

                if len(rubberband_pts) == 0:
                    self.iface.mapCanvas().refresh()
                    return

                rubberband_pts = self.remove_duplicated_point(rubberband_pts)

                if len(rubberband_pts) < 2:
                    self.rubber_band.reset()
                    return

                # Check whether the pipe points are located on existing nodes
                junct_nrs = [0]
                for p in range(1, len(rubberband_pts) - 1):
                    overlapping_nodes = NetworkUtils.find_overlapping_nodes(
                        self.params, rubberband_pts[p])
                    if overlapping_nodes['junctions'] or overlapping_nodes[
                            'reservoirs'] or overlapping_nodes['tanks']:
                        junct_nrs.append(p)

                junct_nrs.append(len(rubberband_pts) - 1)

                new_pipes_nr = len(junct_nrs) - 1

                new_pipes_fts = []
                new_pipes_eids = []

                for np in range(new_pipes_nr):

                    pipe_eid = NetworkUtils.find_next_id(
                        self.params.pipes_vlay, Pipe.prefix)  # TODO: softcode
                    #demand = float(self.data_dock.txt_pipe_demand.text())
                    length_units = 'm'  #TODO soft code
                    diameter = float(self.data_dock.cbo_pipe_dia.\
                                     currentText())
                    diameter_units = self.data_dock.cbo_pipe_dia_units.\
                        currentText()
                    #loss = float(self.data_dock.txt_pipe_loss.text())
                    #status = self.data_dock.cbo_pipe_status.currentText()
                    material = self.data_dock.cbo_pipe_mtl.currentText()
                    roughness = float(self.data_dock.txt_roughness.text())
                    #pipe_desc = self.data_dock.txt_pipe_desc.text()
                    #pipe_tag = self.data_dock.cbo_pipe_tag.currentText()
                    num_edu = 1
                    zone_id = 0
                    velocity = 0
                    velocity_units = 'm/s'
                    frictionloss = 0
                    frictionloss_units = 'm'

                    pipe_ft = LinkHandler.create_new_pipe(
                        self.params, pipe_eid, length_units, diameter,
                        diameter_units, 0, roughness, " ", material,
                        rubberband_pts[junct_nrs[np]:junct_nrs[np + 1] + 1],
                        True, " ", " ", num_edu, zone_id, velocity,
                        velocity_units, frictionloss, frictionloss_units)
                    self.rubber_band.reset()

                    new_pipes_fts.append(pipe_ft)
                    new_pipes_eids.append(pipe_eid)

                # emitter_coeff_s = self.data_dock.txt_junction_emit_coeff.text()
                #
                # if emitter_coeff_s is None or emitter_coeff_s == '':
                #     emitter_coeff = float(0)
                # else:
                #     emitter_coeff = float(self.data_dock.txt_junction_emit_coeff.text())

                # # Description
                # junction_desc = self.data_dock.txt_junction_desc.text()
                #
                # # Tag
                # junction_tag = self.data_dock.cbo_junction_tag.currentText()

                zone_end = 0
                pressure = 0
                pressure_units = self.data_dock.cbo_rpt_units_pressure.currentText(
                )

                # Create start and end node, if they don't exist
                (start_junction,
                 end_junction) = NetworkUtils.find_start_end_nodes(
                     self.params, new_pipes_fts[0].geometry())
                new_start_junction = None
                if not start_junction:
                    new_start_junction = rubberband_pts[0]
                    junction_eid = NetworkUtils.find_next_id(
                        self.params.junctions_vlay, Junction.prefix)
                    elev = raster_utils.read_layer_val_from_coord(
                        self.params.dem_rlay, new_start_junction, 1)
                    if elev is None:
                        elev = 0
                        # If elev is none, and the DEM is selected, it's better to inform the user
                        if self.params.dem_rlay is not None:
                            self.iface.messageBar().pushMessage(
                                Parameters.plug_in_name,
                                'Elevation value not available: element elevation set to 0.',
                                Qgis.Warning, 5)  # TODO: softcode
                    deltaz = float(0)
                    #j_demand = float(self.data_dock.txt_junction_demand.text())

                    # pattern = self.data_dock.cbo_junction_pattern.itemData(
                    #     self.data_dock.cbo_junction_pattern.currentIndex())
                    # if pattern is not None:
                    #     pattern_id = pattern.id
                    # else:
                    #     pattern_id = None
                    NodeHandler.create_new_junction(self.params,
                                                    new_start_junction,
                                                    junction_eid, elev, 0,
                                                    deltaz, None, 0, " ", " ",
                                                    zone_end, pressure,
                                                    pressure_units)

                (start_junction,
                 end_junction) = NetworkUtils.find_start_end_nodes(
                     self.params,
                     new_pipes_fts[len(new_pipes_fts) - 1].geometry())
                new_end_junction = None
                if not end_junction:
                    new_end_junction = rubberband_pts[len(rubberband_pts) - 1]
                    junction_eid = NetworkUtils.find_next_id(
                        self.params.junctions_vlay, Junction.prefix)
                    elev = raster_utils.read_layer_val_from_coord(
                        self.params.dem_rlay, new_end_junction, 1)
                    if elev is None:
                        elev = 0
                        # If elev is none, and the DEM is selected, it's better to inform the user
                        if self.params.dem_rlay is not None:
                            self.iface.messageBar().pushMessage(
                                Parameters.plug_in_name,
                                'Elevation value not available: element elevation set to 0.',
                                Qgis.Warning, 5)  # TODO: softcode
                    deltaz = float(0)

                    # pattern = self.data_dock.cbo_junction_pattern.itemData(
                    #     self.data_dock.cbo_junction_pattern.currentIndex())
                    # if pattern is not None:
                    #     pattern_id = pattern.id
                    # else:
                    #     pattern_id = None
                    NodeHandler.create_new_junction(self.params,
                                                    new_end_junction,
                                                    junction_eid, elev, 0,
                                                    deltaz, None, 0, " ", " ",
                                                    zone_end, pressure,
                                                    pressure_units)

                # If end or start node intersects a pipe, split it
                if new_start_junction:
                    for pipe_ft in self.params.pipes_vlay.getFeatures():
                        if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[0] and\
                                        pipe_ft.geometry().distance(QgsGeometry.fromPointXY(new_start_junction)) < self.params.tolerance:
                            LinkHandler.split_pipe(self.params, pipe_ft,
                                                   new_start_junction)

                if new_end_junction:
                    for pipe_ft in self.params.pipes_vlay.getFeatures():
                        if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[-1] and\
                                        pipe_ft.geometry().distance(QgsGeometry.fromPointXY(new_end_junction)) < self.params.tolerance:
                            LinkHandler.split_pipe(self.params, pipe_ft,
                                                   new_end_junction)

            self.iface.mapCanvas().refresh()

        # except Exception as e:
        #     self.rubber_band.reset()
        #     self.iface.messageBar().pushWarning('Cannot add new pipe to ' + self.params.pipes_vlay.name() + ' layer', repr(e))
        #     traceback.print_exc(file=sys.stdout)

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.rubber_band.reset()

    def activate(self):

        self.update_snapper()

        # Editing
        if not self.params.junctions_vlay.isEditable():
            self.params.junctions_vlay.startEditing()
        if not self.params.pipes_vlay.isEditable():
            self.params.pipes_vlay.startEditing()

    def deactivate(self):

        # QgsProject.instance().setSnapSettingsForLayer(self.params.junctions_vlay.id(),
        #                                               True,
        #                                               QgsSnapper.SnapToVertex,
        #                                               QgsTolerance.MapUnits,
        #                                               0,
        #                                               True)
        #
        # QgsProject.instance().setSnapSettingsForLayer(self.params.reservoirs_vlay.id(),
        #                                               True,
        #                                               QgsSnapper.SnapToVertex,
        #                                               QgsTolerance.MapUnits,
        #                                               0,
        #                                               True)
        # QgsProject.instance().setSnapSettingsForLayer(self.params.tanks_vlay.id(),
        #                                               True,
        #                                               QgsSnapper.SnapToVertex,
        #                                               QgsTolerance.MapUnits,
        #                                               0,
        #                                               True)
        # QgsProject.instance().setSnapSettingsForLayer(self.params.pipes_vlay.id(),
        #                                               True,
        #                                               QgsSnapper.SnapToSegment,
        #                                               QgsTolerance.MapUnits,
        #                                               0,
        #                                               True)

        # self.rubber_band.reset()
        self.data_dock.btn_add_pipe.setChecked(False)

        self.canvas().scene().removeItem(self.vertex_marker)

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True

    def reset_marker(self):
        self.outlet_marker.hide()
        self.canvas().scene().removeItem(self.outlet_marker)

    def remove_duplicated_point(self, pts):

        # This is needed because the rubber band sometimes returns duplicated points
        purged_pts = [pts[0]]
        for p in enumerate(list(range(len(pts) - 1)), 1):
            if pts[p[0]] == pts[p[0] - 1]:
                continue
            else:
                purged_pts.append(pts[p[0]])

        return purged_pts

    def update_snapper(self):
        layers = {
            self.params.junctions_vlay: QgsSnappingConfig.Vertex,
            self.params.reservoirs_vlay: QgsSnappingConfig.Vertex,
            self.params.tanks_vlay: QgsSnappingConfig.Vertex,
            self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment
        }

        self.snapper = NetworkUtils.set_up_snapper(layers,
                                                   self.iface.mapCanvas(),
                                                   self.params.snap_tolerance)
        self.snapper.toggleEnabled()

    # Needed by Observable
    def update(self, observable):
        self.update_snapper()
Example #2
0
class osmSearchDialog(QDockWidget , Ui_osmSearch ):
    def __init__(self,iface):
        self.iface = iface
        QDockWidget.__init__(self)
        self.setupUi(self)
        self.iface.addDockWidget(Qt.BottomDockWidgetArea,self)
        self.canvas = self.iface.mapCanvas()
        
        self.rb = QgsRubberBand(self.canvas, QGis.Point)
        self.rb.setColor(QColor( 255, 0, 0, 150 ))
        self.searchCacheLimit = 1000
        
        self.wgs84 = QgsCoordinateReferenceSystem()
        self.wgs84.createFromSrid(4326)
        self.proj = self.canvas.mapRenderer().destinationCrs()
        self.transform = QgsCoordinateTransform(self.wgs84, self.proj)
        
        self.bSearch.clicked.connect(self.startSearch)
        self.eOutput.currentItemChanged.connect(self.itemChanged)
        self.eOutput.clickedOutsideOfItems.connect(self.itemChanged)
        self.eText.cleared.connect(self.clearEdit)
        self.canvas.mapRenderer().destinationSrsChanged.connect(self.crsChanged)
        self.iface.newProjectCreated.connect(self.clearEdit)
        self.iface.projectRead.connect(self.clearEdit)
        self.cbCenter.stateChanged.connect(self.autocenter)
        
        db = cacheDB()
        self.autocompleteList = db.getAutocompleteList()
        db.closeConnection()
        self.completer = QCompleter(self.autocompleteList)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.eText.setCompleter(self.completer)

    def startSearch(self):
        text = self.eText.text().encode('utf-8')
        if text == "":
            self.clearEdit()
        #url = 'http://open.mapquestapi.com/nominatim/v1/search.php'
        url = 'http://nominatim.openstreetmap.org/search'
        params = urllib.urlencode({'q': text,'format': 'json','polygon_text':'1'})
        response = json.load(urllib2.urlopen(url+'?'+params))
        self.loadData(response)

    def loadData(self, data):
        self.rb.reset(QGis.Point)
        self.eOutput.clear()
        items = []
        for d in data:
            try:
                geometry = d['geotext']
            except KeyError:
                geometry = 'POINT(%s %s)' % (d['lon'], d['lat'])
            item = QTreeWidgetItem([d['display_name'], d['type']])
            item.setData(0, Qt.UserRole, geometry)
            if geometry.lower().startswith('point'):
                item.setIcon(0, QgsApplication.getThemeIcon('/mIconPointLayer.svg'))
            elif geometry.lower().startswith('linestring'):
                item.setIcon(0, QgsApplication.getThemeIcon('/mIconLineLayer.svg'))
            elif geometry.lower().startswith('polygon'):
                item.setIcon(0, QgsApplication.getThemeIcon('/mIconPolygonLayer.svg'))
            items.append(item)
        if items:
            self.eOutput.insertTopLevelItems(0, items)
            self.addSearchTerm(unicode(self.eText.text().lower()))
            
        else:
            self.iface.messageBar().pushMessage('Nothing was found!', QgsMessageBar.CRITICAL, 2)

    def itemChanged(self, current=None, previous=None):
        if current:
            wkt = str(current.data(0,Qt.UserRole))
            geom = QgsGeometry.fromWkt(wkt)
            if self.proj.srsid() != 4326:
                try:
                    geom.transform(self.transform)
                except:
                    self.iface.messageBar().pushMessage('CRS transformation error!', QgsMessageBar.CRITICAL, 2)
                    self.rb.reset(QGis.Point)
                    return
            self.rb.setToGeometry(geom, None)
            if self.cbCenter.isChecked():
                self.moveCanvas(geom.centroid().asPoint(), self.canvas.extent())
        else:
            self.rb.reset(QGis.Point)
            self.eOutput.setCurrentItem(None)

    def crsChanged(self):
        self.proj = self.canvas.mapRenderer().destinationCrs()
        self.transform = QgsCoordinateTransform(self.wgs84, self.proj)

    def clearEdit(self):
        self.eOutput.clear()
        self.eText.clear()
        if hasattr(self, 'rb'):
            self.rb.reset(QGis.Point)
    
    def setCompleter(self):
        self.completer.model().setStringList(self.autocompleteList)
    
    def addSearchTerm(self, text):
        if not text in self.autocompleteList:
            self.autocompleteList.append(text)
            self.setCompleter()
        while len(self.autocompleteList) > self.searchCacheLimit:
            self.autocompleteList.pop(0)

    def autocenter(self, state):
        if state and self.rb.size():
            self.moveCanvas(self.rb.asGeometry().centroid().asPoint(), self.canvas.extent())

    def moveCanvas(self, newCenter, oldExtent):
        newExtent = QgsRectangle(oldExtent)
        newExtent.scale(1, newCenter)
        self.canvas.setExtent(newExtent)
        self.canvas.refresh()