Пример #1
0
 def getSnapper(self):
     """
     Getter for the snapper
     """
     self.snapper = QgsSnapper(self.iface.mapCanvas().mapRenderer())
     snap_layer = QgsSnapper.SnapLayer()
     snap_layer.mLayer = self.nodeLayer
     snap_layer.mTolerance = 10
     snap_layer.mUnitType = QgsTolerance.Pixels
     snap_layer.mSnapTo = QgsSnapper.SnapToVertex
     self.snapper.setSnapLayers([snap_layer])
     return self.snapper
Пример #2
0
 def snap(self,pos):
     snapper = QgsSnapper( self.iface.mapCanvas().mapSettings() )
     snapNodeLayer = QgsSnapper.SnapLayer()
     snapNodeLayer.mLayer = self.nodeLayer
     snapNodeLayer.mTolerance = 10
     snapNodeLayer.mUnitType = QgsTolerance.Pixels
     snapNodeLayer.mSnapTo = QgsSnapper.SnapToVertex
     snapper.setSnapLayers([snapNodeLayer])
     (_, snappedPoints) = snapper.snapPoint(pos)
     if len(snappedPoints) >= 1:
         self.currentSnappingResult = snappedPoints[0]
         return self.currentSnappingResult.snappedVertex
     else:
         snapper = QgsSnapper( self.iface.mapCanvas().mapSettings() )
         snapReachLayer = QgsSnapper.SnapLayer()
         snapReachLayer.mLayer = self.reachLayer
         snapReachLayer.mTolerance = 10
         snapReachLayer.mUnitType = QgsTolerance.Pixels
         snapReachLayer.mSnapTo = QgsSnapper.SnapToVertexAndSegment
         snapper.setSnapLayers([snapReachLayer])
         (_, snappedPoints) = snapper.snapPoint(pos)
         if len(snappedPoints) >= 1:
             self.currentSnappingResult = snappedPoints[0]
             return self.currentSnappingResult.snappedVertex
         else:
             self.currentSnappingResult = None
             return pos
 def snap(self, pos):
     """
     Snap to nearby points on the wastewater node layer which may be used as connection
     points for this reach.
     :param pos: The position to snap
     :return: The snapped position
     """
     snapper = QgsSnapper(self.iface.mapCanvas().mapSettings())
     snap_nodelayer = QgsSnapper.SnapLayer()
     snap_nodelayer.mLayer = self.nodeLayer
     snap_nodelayer.mTolerance = 10
     snap_nodelayer.mUnitType = QgsTolerance.Pixels
     snap_nodelayer.mSnapTo = QgsSnapper.SnapToVertex
     snapper.setSnapLayers([snap_nodelayer])
     (_, snappedPoints) = snapper.snapPoint(pos)
     if len(snappedPoints) >= 1:
         self.currentSnappingResult = snappedPoints[0]
         return self.currentSnappingResult.snappedVertex
     else:
         snapper = QgsSnapper(self.iface.mapCanvas().mapSettings())
         snap_reachlayer = QgsSnapper.SnapLayer()
         snap_reachlayer.mLayer = self.reachLayer
         snap_reachlayer.mTolerance = 10
         snap_reachlayer.mUnitType = QgsTolerance.Pixels
         snap_reachlayer.mSnapTo = QgsSnapper.SnapToVertexAndSegment
         snapper.setSnapLayers([snap_reachlayer])
         (_, snappedPoints) = snapper.snapPoint(pos)
         if len(snappedPoints) >= 1:
             self.currentSnappingResult = snappedPoints[0]
             return self.currentSnappingResult.snappedVertex
         else:
             self.currentSnappingResult = None
             return pos
Пример #4
0
    def snapPoint(self, event):
        pClicked = QPoint( event.pos().x(), event.pos().y() )
        
        self.snapper = QgsSnapper( self.iface.mapCanvas().mapRenderer() )
        snapLayer = QgsSnapper.SnapLayer()
        snapLayer.mLayer = self.nodeLayer
        snapLayer.mTolerance = 10
        snapLayer.mUnitType = QgsTolerance.Pixels
        snapLayer.mSnapTo = QgsSnapper.SnapToVertex
        self.snapper.setSnapLayers( [snapLayer] )
        
        ( _, snappedPoints ) = self.snapper.snapPoint( pClicked, [] )

        if len( snappedPoints ) == 0:
            return None
        elif len( snappedPoints ) == 1:
            return snappedPoints[0]
        elif len( snappedPoints ) > 1:
            attrObjId = self.getNodeLayer().dataProvider().fieldNameIndex('obj_id')
            attrType = self.getNodeLayer().dataProvider().fieldNameIndex('type')
            attrDescr = self.getNodeLayer().dataProvider().fieldNameIndex('description')
            
            attributes = [attrObjId, attrType, attrDescr ]
            pointIds = [point.snappedAtGeometry for point in snappedPoints]
            nodeFeatures = self.getFeaturesById(self.getNodeLayer(), attributes, pointIds, True)

            # Filter wastewater nodes            
            filteredFeatures = { id: nodeFeatures.featureById(id) for id in nodeFeatures.asDict() if nodeFeatures.attrAsUnicode( nodeFeatures.featureById(id), u'type' ) == u'wastewater_node' }

            # Only one wastewater node left: return this
            if len( filteredFeatures ) == 1:
                return [point for point in snappedPoints if point.snappedAtGeometry == filteredFeatures.iterkeys().next() ][0]

            # Still not sure which point to take?
            # Are there no wastewater nodes filtered? Let the user choose from the reach points
            if len( filteredFeatures ) == 0:
                filteredFeatures = nodeFeatures.asDict()
            
            # Ask the user which point he wants to use 
            actions = dict()
            
            menu = QMenu( self.iface.mapCanvas() )
            
            for id, feature in filteredFeatures.iteritems():
                try:
                    title = feature.attribute( 'description' ) + " (" + feature.attribute( 'obj_id' ) + ")"
                except TypeError:
                    title = " (" + feature.attribute( 'obj_id' ) + ")"
                action = QAction( title, menu )
                actions[action] = point
                menu.addAction( action )
            
            clickedAction = menu.exec_( self.iface.mapCanvas().mapToGlobal( event.pos()  ) )
            
            if clickedAction is not None:
                return actions[clickedAction]
            
            return None
Пример #5
0
 def getSnapper(self):
     """
     Getter for the snapper
     """
     self.snapper = QgsSnapper(self.iface.mapCanvas().mapRenderer())
     snap_layer = QgsSnapper.SnapLayer()
     snap_layer.mLayer = self.nodeLayer
     snap_layer.mTolerance = 10
     snap_layer.mUnitType = QgsTolerance.Pixels
     snap_layer.mSnapTo = QgsSnapper.SnapToVertex
     self.snapper.setSnapLayers([snap_layer])
     return self.snapper
Пример #6
0
    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
Пример #7
0
    def set_up_snap_layer(vlayer, tolerance=None, snapping_type=QgsSnapper.SnapToVertex):

        snap_layer = QgsSnapper.SnapLayer()
        snap_layer.mLayer = vlayer

        if tolerance is None or tolerance < 0:
            (a, b, c, d, tolerance, f) = QgsProject.instance().snapSettingsForLayer(vlayer.id())
            snap_layer.mTolerance = tolerance
        else:
            snap_layer.mTolerance = tolerance

        snap_layer.mUnitType = QgsTolerance.MapUnits
        snap_layer.mSnapTo = snapping_type
        return snap_layer
Пример #8
0
 def activate(self):
     QgsMapTool.activate(self)
     self.rubber.setWidth(self.settings.value("rubberWidth"))
     self.rubber.setColor(self.settings.value("rubberColor"))
     lineLayer = MemoryLayers(self.iface).lineLayer()
     # unset this tool if the layer is removed
     lineLayer.layerDeleted.connect(self.unsetMapTool)
     self.layerId = lineLayer.id()
     # create snapper for this layer
     self.snapLayer = QgsSnapper.SnapLayer()
     self.snapLayer.mLayer = lineLayer
     self.snapLayer.mSnapTo = QgsSnapper.SnapToVertexAndSegment
     self.snapLayer.mTolerance = self.settings.value("selectTolerance")
     if self.settings.value("selectUnits") == "map":
         self.snapLayer.mUnitType = QgsTolerance.MapUnits
     else:
         self.snapLayer.mUnitType = QgsTolerance.Pixels
Пример #9
0
 def getFeatures(self, pixPoint):
     snapper = QgsSnapper(self.mapCanvas.mapRenderer())
     snapper.setSnapLayers([self.snapLayer])
     snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances)
     ok, snappingResults = snapper.snapPoint(pixPoint, [])
     # output snapped features
     features = []
     alreadyGot = []
     for result in snappingResults:
         featureId = result.snappedAtGeometry
         f = QgsFeature()
         if featureId not in alreadyGot:
             if result.layer.getFeatures(QgsFeatureRequest().setFilterFid(
                     featureId)).nextFeature(f) is not False:
                 features.append(QgsFeature(f))
                 alreadyGot.append(featureId)
     return features
    def snapToDimensionLayer(self, pixPoint):
        snapper = QgsSnapper(self.mapCanvas.mapRenderer())
        snapper.setSnapLayers([self.snapLayer])
        snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances)

        ok, snappingResults = snapper.snapPoint(pixPoint, [])
        for result in snappingResults:
            featureId = result.snappedAtGeometry
            f = QgsFeature()
            if self.snapLayer.mLayer.getFeatures(QgsFeatureRequest().setFilterFid(featureId)).nextFeature(f) is not False:
                if self.observationType == "orientation":
                    line = f.geometry().asPolyline()
                    if len(line) != 2:
                        continue
                return f
        return None
 def getFeatures(self, pixPoint):
     snapper = QgsSnapper(self.mapCanvas.mapRenderer())
     snapper.setSnapLayers([self.snapLayer])
     snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances)
     ok, snappingResults = snapper.snapPoint(pixPoint, [])
     # output snapped features
     features = []
     alreadyGot = []
     for result in snappingResults:
         featureId = result.snappedAtGeometry
         f = QgsFeature()
         if featureId not in alreadyGot:
             if result.layer.getFeatures(QgsFeatureRequest().setFilterFid(featureId)).nextFeature(f) is not False:
                 features.append(QgsFeature(f))
                 alreadyGot.append(featureId)
     return features
Пример #12
0
    def getOrientation(self, pixPoint):
        snapperList = []
        scale = self.iface.mapCanvas().mapRenderer().scale()
        for layer in self.iface.mapCanvas().layers():
            if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType():
                if layer.geometryType() in (QGis.Line, QGis.Polygon):
                    if not layer.hasScaleBasedVisibility() or layer.minimumScale() < scale <= layer.maximumScale():
                        snapLayer = QgsSnapper.SnapLayer()
                        snapLayer.mLayer = layer
                        snapLayer.mSnapTo = QgsSnapper.SnapToSegment
                        snapLayer.mTolerance = self.settings.value("selectTolerance")
                        if self.settings.value("selectUnits") == "map":
                            snapLayer.mUnitType = QgsTolerance.MapUnits
                        else:
                            snapLayer.mUnitType = QgsTolerance.Pixels
                        snapperList.append(snapLayer)
        if len(snapperList) == 0:
            return None
        snapper = QgsSnapper(self.canvas.mapRenderer())
        snapper.setSnapLayers(snapperList)
        snapper.setSnapMode(QgsSnapper.SnapWithOneResult)

        f = QgsFeature()
        ok, snappingResults = snapper.snapPoint(pixPoint, [])
        if ok == 0:
            for result in snappingResults:
                if result.layer.getFeatures(QgsFeatureRequest().setFilterFid(result.snappedAtGeometry)).nextFeature(f) is False:
                    continue
                if not isFeatureRendered(self.canvas, result.layer, f):
                    continue
                vertices = (result.afterVertex, result.beforeVertex)
                po = result.snappedVertex
                dist = (po.sqrDist(vertices[0]), po.sqrDist(vertices[1]))
                mindist = min(dist)
                if mindist == 0:
                    return None
                i = dist.index(mindist)
                ve = vertices[i]
                az = po.azimuth(ve)
                return Orientation(self.iface, ve, az)
        else:
            return None
 def getFeatures(self, pixPoint):
     # do the snapping
     snapper = QgsSnapper(self.mapCanvas.mapRenderer())
     snapper.setSnapLayers(self.snapperList)
     snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances)
     ok, snappingResults = snapper.snapPoint(pixPoint, [])
     # output snapped features
     features = []
     alreadyGot = []
     for result in snappingResults:
         featureId = result.snappedAtGeometry
         f = QgsFeature()
         if (result.layer.id(), featureId) not in alreadyGot:
             if result.layer.getFeatures(QgsFeatureRequest().setFilterFid(featureId)).nextFeature(f) is False:
                 continue
             if not isFeatureRendered(self.mapCanvas, result.layer, f):
                 continue
             features.append(QgsFeature(f))
             features[-1].layer = result.layer
             alreadyGot.append((result.layer.id(), featureId))
     return features
 def snap(self, pos):
     """
     Snap to nearby points on the wastewater node layer which may be used as connection
     points for this reach.
     :param pos: The position to snap
     :return: The snapped position
     """
     snapper = QgsSnapper(self.iface.mapCanvas().mapSettings())
     snap_nodelayer = QgsSnapper.SnapLayer()
     snap_nodelayer.mLayer = self.nodeLayer
     snap_nodelayer.mTolerance = 10
     snap_nodelayer.mUnitType = QgsTolerance.Pixels
     snap_nodelayer.mSnapTo = QgsSnapper.SnapToVertex
     snapper.setSnapLayers([snap_nodelayer])
     (_, snappedPoints) = snapper.snapPoint(pos)
     if snappedPoints:
         self.currentSnappingResult = snappedPoints[0]
         return self.currentSnappingResult.snappedVertex
     else:
         snapper = QgsSnapper(self.iface.mapCanvas().mapSettings())
         snap_reachlayer = QgsSnapper.SnapLayer()
         snap_reachlayer.mLayer = self.reachLayer
         snap_reachlayer.mTolerance = 10
         snap_reachlayer.mUnitType = QgsTolerance.Pixels
         snap_reachlayer.mSnapTo = QgsSnapper.SnapToVertexAndSegment
         snapper.setSnapLayers([snap_reachlayer])
         (_, snappedPoints) = snapper.snapPoint(pos)
         if snappedPoints:
             self.currentSnappingResult = snappedPoints[0]
             return self.currentSnappingResult.snappedVertex
         else:
             self.currentSnappingResult = None
             return pos
Пример #15
0
class QgepGraphManager(QObject):
    """
    Manages a graph
    """
    reachLayer = None
    reachLayerId = -1
    nodeLayer = None
    nodeLayerId = -1
    dirty = True
    graph = None
    vertexIds = {}
    nodesOnStructure = defaultdict(list)
    # Logs performance of graph creation
    timings = []

    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.snapper = None

    def setReachLayer(self, reach_layer):
        """
        Set the reach layer (edges)
        """
        self.reachLayer = reach_layer
        self.dirty = True

        if reach_layer:
            self.reachLayerId = reach_layer.id()
        else:
            self.reachLayerId = 0

        if self.nodeLayer and self.reachLayer:
            self.createGraph()

    def setNodeLayer(self, node_layer):
        """
        Set the node layer
        """
        self.dirty = True

        self.nodeLayer = node_layer

        if node_layer:
            self.nodeLayerId = node_layer.id()

        else:
            self.nodeLayerId = 0

        if self.nodeLayer and self.reachLayer:
            self.createGraph()

    def _addVertices(self):
        """
        Initializes the graph with the vertices from the node layer
        """
        node_provider = self.nodeLayer.dataProvider()

        features = node_provider.getFeatures()

        # Add all vertices
        for feat in features:
            fid = feat.id()

            obj_id = feat['obj_id']
            obj_type = feat['type']

            try:
                vertex = feat.geometry().asPoint()
            except AttributeError:
                # TODO Add to problem log
                pass
            self.graph.add_node(fid, dict(point=vertex, objType=obj_type))

            self.vertexIds[unicode(obj_id)] = fid

        self._profile("add vertices")

    def _addEdges(self):
        """
        Initializes the graph with the edges
        """
        # Add all edges (reach)
        reach_provider = self.reachLayer.dataProvider()

        features = reach_provider.getFeatures()

        # Loop through all reaches
        for feat in features:
            try:
                obj_id = feat['obj_id']
                obj_type = feat['type']
                from_obj_id = feat['from_obj_id']
                to_obj_id = feat['to_obj_id']

                length = feat['length_calc']

                pt_id1 = self.vertexIds[from_obj_id]
                pt_id2 = self.vertexIds[to_obj_id]

                props = {
                    'weight': length,
                    'feature': feat.id(),
                    'baseFeature': obj_id,
                    'objType': obj_type
                }
                self.graph.add_edge(pt_id1, pt_id2, props)
            except KeyError as e:
                print e

        self._profile("add edges")

    def refresh(self):
        """
        Refreshes the network graph. It will force a refresh of the materialized views in the database and then reload
        and recreate the graph.
        """
        uri = QgsDataSourceURI(self.nodeLayer.dataProvider().dataSourceUri())

        db = QSqlDatabase.addDatabase("QPSQL")  # Name of the driver -- doesn't change

        str_connect_option = "requiressl=0;service=" + uri.service()
        db.setConnectOptions(str_connect_option)

        if not db.open():
            self.iface.messageBar().pushMessage(self.tr("Warning"), db.lastError().text(),
                                                level=QgsMessageBar.CRITICAL)

        query_template = "REFRESH MATERIALIZED VIEW qgep.vw_network_segment;"
        query = QSqlQuery(db)
        if not query.exec_(query_template):
            str_result = query.lastError().text()
            self.iface.messageBar().pushMessage(self.tr("Warning"), str_result, level=QgsMessageBar.CRITICAL)
        else:
            self.iface.messageBar().pushMessage(self.tr("Success"), "vw_network_segment successfully updated",
                                                level=QgsMessageBar.SUCCESS, duration=2)

        query_template = "REFRESH MATERIALIZED VIEW qgep.vw_network_node;"
        query = QSqlQuery(db)
        if not query.exec_(query_template):
            str_result = query.lastError().text()
            self.iface.messageBar().pushMessage(self.tr("Warning"), str_result, level=QgsMessageBar.CRITICAL)
        else:
            self.iface.messageBar().pushMessage(self.tr("Success"), "vw_network_node successfully updated",
                                                level=QgsMessageBar.SUCCESS, duration=2)
        # recreate networkx graph
        self.graph.clear()
        self.createGraph()

    def _profile(self, name):
        """
        Adds a performance profile snapshot with the given name
        """
        spenttime = 0
        if self.timings:
            spenttime = time.clock() - self.timings[-1][1]
        self.timings.append((name, spenttime))

    # Creates a network graph
    def createGraph(self):
        """
        Create a graph
        """
        self._profile("create graph")
        # try:
        self.vertexIds = {}
        self.nodesOnStructure = defaultdict(list)
        self._profile("initiate dicts")
        self.graph = nx.DiGraph()

        self._profile("initiate graph")

        self._addVertices()
        self._addEdges()

        self.print_profile()
        self.dirty = False

    def getNodeLayer(self):
        """
        Getter for the node layer
        """
        return self.nodeLayer

    def getReachLayer(self):
        """
        Getter for the reach layer
        :return:
        """
        return self.reachLayer

    def getNodeLayerId(self):
        """
        Getter for the node layer's id
        """
        return self.nodeLayerId

    def getReachLayerId(self):
        """
        Getter for the reach layer's id
        """
        return self.reachLayerId

    def getSnapper(self):
        """
        Getter for the snapper
        """
        self.snapper = QgsSnapper(self.iface.mapCanvas().mapRenderer())
        snap_layer = QgsSnapper.SnapLayer()
        snap_layer.mLayer = self.nodeLayer
        snap_layer.mTolerance = 10
        snap_layer.mUnitType = QgsTolerance.Pixels
        snap_layer.mSnapTo = QgsSnapper.SnapToVertex
        self.snapper.setSnapLayers([snap_layer])
        return self.snapper

    def snapPoint(self, event):
        """
        Snap to a point on this network
        :param event: A QMouseEvent
        """
        clicked_point = QPoint(event.pos().x(), event.pos().y())

        self.snapper = self.getSnapper()

        (_, snapped_points) = self.snapper.snapPoint(clicked_point, [])

        if not snapped_points:
            return None
        elif len(snapped_points) == 1:
            return snapped_points[0]
        elif len(snapped_points) > 1:

            point_ids = [point.snappedAtGeometry for point in snapped_points]
            node_features = self.getFeaturesById(self.getNodeLayer(), point_ids)

            # Filter wastewater nodes
            filtered_features = {
                fid: node_features.featureById(fid)
                for fid in node_features.asDict()
                if node_features.attrAsUnicode(node_features.featureById(fid), u'type') == u'wastewater_node'
            }

            # Only one wastewater node left: return this
            if len(filtered_features) == 1:
                points = (point for point
                          in snapped_points
                          if point.snappedAtGeometry == filtered_features.iterkeys().next())
                return points[0]

            # Still not sure which point to take?
            # Are there no wastewater nodes filtered? Let the user choose from the reach points
            if not filtered_features:
                filtered_features = node_features.asDict()

            # Ask the user which point he wants to use
            actions = dict()

            menu = QMenu(self.iface.mapCanvas())

            for _, feature in filtered_features.iteritems():
                try:
                    title = feature.attribute('description') + " (" + feature.attribute('obj_id') + ")"
                except TypeError:
                    title = " (" + feature.attribute('obj_id') + ")"
                action = QAction(title, menu)
                actions[action] = point
                menu.addAction(action)

            clicked_action = menu.exec_(self.iface.mapCanvas().mapToGlobal(event.pos()))

            if clicked_action is not None:
                return actions[clicked_action]

            return None

    def shortestPath(self, start_point, end_point):
        """
        Finds the shortes path from the start point
        to the end point
        :param start_point: The start node
        :param end_point:   The end node
        :return:       A (path, edges) tuple
        """
        if self.dirty:
            self.createGraph()

        try:
            path = nx.algorithms.dijkstra_path(self.graph, start_point, end_point)
            edges = [(u, v, self.graph[u][v]) for (u, v) in zip(path[0:], path[1:])]

            p = (path, edges)

        except nx.NetworkXNoPath:
            print "no path found"
            p = ([], [])

        return p

    def getTree(self, node, reverse=False):
        """
        Get
        :param node:    A start node
        :param reverse: Should the graph be reversed (upstream search)
        :return:        A list of edges
        """
        if self.dirty:
            self.createGraph()

        if reverse:
            my_graph = self.graph.reverse()
        else:
            my_graph = self.graph

        # Returns pred, weight
        pred, _ = nx.bellman_ford(my_graph, node)
        edges = [(v, u, my_graph[v][u]) for (u, v) in pred.items() if v is not None]

        return edges

    def getEdgeGeometry(self, edges):
        """
        Get the geometry for some edges
        :param edges:  A list of edges
        :return:       A list of polylines
        """
        cache = self.getFeaturesById(self.reachLayer, edges)
        polylines = [feat.geometry().asPolyline() for feat in cache.asDict().values()]
        return polylines

    # pylint: disable=no-self-use
    def getFeaturesById(self, layer, ids):
        """
        Get some features by their id
        """
        feat_cache = QgepFeatureCache(layer)
        data_provider = layer.dataProvider()

        features = data_provider.getFeatures()

        for feat in features:
            if feat.id() in ids:
                feat_cache.addFeature(feat)

        return feat_cache

    # pylint: disable=no-self-use
    def getFeaturesByAttr(self, layer, attr, values):
        """
        Get some features by an attribute value
        """
        feat_cache = QgepFeatureCache(layer)
        data_provider = layer.dataProvider()

        # Batch query and filter locally
        features = data_provider.getFeatures()

        for feat in features:
            if feat_cache.attrAsUnicode(feat, attr) in values:
                feat_cache.addFeature(feat)

        return feat_cache

    def print_profile(self):
        """
        Will print some performance profiling information
        """
        for (name, spenttime) in self.timings:
            print name + ":" + str(spenttime)
Пример #16
0
class QgepGraphManager():
    reachLayer = None
    reachLayerId = -1
    nodeLayer = None
    nodeLayerId = -1
    specialStructureLayer = None
    specialStructureLayerId = -1
    dirty = True
    graph = None
    vertexIds = {}
    nodesOnStructure = defaultdict(list)
    # Logs performance of graph creation
    timings = []
    
    def __init__( self, iface ):
        self.iface = iface
        
    def setReachLayer( self, reachLayer ):
        self.reachLayer = reachLayer
        self.dirty = True
        
        if reachLayer:
            self.reachLayerId = reachLayer.id()
        else:
            self.reachLayerId = 0
            
        if self.nodeLayer and self.reachLayer:
            self.createGraph()
        
    def setNodeLayer( self, nodeLayer ):
        self.dirty = True
        
        self.nodeLayer = nodeLayer
        
        if nodeLayer:
            self.nodeLayerId = nodeLayer.id()

        else:
            self.nodeLayerId = 0
            
        if self.nodeLayer and self.reachLayer:
            self.createGraph()
            
    def setSpecialStructureLayer( self, specialStructureLayer ):
        self.specialStructureLayer = specialStructureLayer
        
        if specialStructureLayer:
            self.specialStructureLayerId = specialStructureLayer.id()
        
    def _addVertices(self):
        nodeProvider = self.nodeLayer.dataProvider()
        
        features = nodeProvider.getFeatures()
        
        # Add all vertices
        for feat in features:
            featId = feat.id()
            
            objId = feat['obj_id']
            objType = feat['type']
            
            vertex = feat.geometry().asPoint()
            self.graph.add_node( featId, dict( point=vertex, objType=objType ) )
            
            self.vertexIds[ unicode( objId ) ] = featId
        
        self._profile( "add vertices" )
            
    def _addEdges( self ):
        # Add all edges (reach)
        reachProvider = self.reachLayer.dataProvider()
        
        features = reachProvider.getFeatures()
        
        #Loop through all reaches
        for feat in features:
            try:
                objId     = feat['obj_id']
                objType   = feat['type']
                fromObjId = feat['from_obj_id']
                toObjId   = feat['to_obj_id']
                
                length    = feat['length_calc']
                
                ptId1 = self.vertexIds[ fromObjId ]
                ptId2 = self.vertexIds[ toObjId ]
                
                props = { \
                  'weight': length,\
                  'feature': feat.id(),\
                  'baseFeature': objId,\
                  'objType': objType\
                }
                self.graph.add_edge( ptId1, ptId2, props )
            except KeyError as e:
                print e

        self._profile( "add edges" )
            
        
    def _profile(self,name):
        spenttime = 0
        if len( self.timings ) != 0:
            spenttime = time.clock() - self.timings[-1][1]
        self.timings.append( (name, spenttime) )
    
    # Creates a network graph
    def createGraph(self):
        self._profile( "create graph" )
        
        #try:
        self.vertexIds = {}
        self.nodesOnStructure = defaultdict( list )
        self._profile( "initiate dicts" )
        self.graph = nx.DiGraph()
        
        self._profile( "initiate graph" )
        
        self._addVertices()
        self._addEdges()
        
        self.print_profile()
        self.dirty = False
    
    def getNodeLayer(self):
        return self.nodeLayer
    
    def getSpecialStructureLayer(self):
        return self.specialStructureLayer
    
    def getReachLayer(self):
        return self.reachLayer
    
    def getNodeLayerId(self):
        return self.nodeLayerId
    
    def getReachLayerId(self):
        return self.reachLayerId
    
    def getSnapper(self):
        return self.snapper
    
    def snapPoint(self, event):
        pClicked = QPoint( event.pos().x(), event.pos().y() )
        
        self.snapper = QgsSnapper( self.iface.mapCanvas().mapRenderer() )
        snapLayer = QgsSnapper.SnapLayer()
        snapLayer.mLayer = self.nodeLayer
        snapLayer.mTolerance = 10
        snapLayer.mUnitType = QgsTolerance.Pixels
        snapLayer.mSnapTo = QgsSnapper.SnapToVertex
        self.snapper.setSnapLayers( [snapLayer] )
        
        ( _, snappedPoints ) = self.snapper.snapPoint( pClicked, [] )

        if len( snappedPoints ) == 0:
            return None
        elif len( snappedPoints ) == 1:
            return snappedPoints[0]
        elif len( snappedPoints ) > 1:
            attrObjId = self.getNodeLayer().dataProvider().fieldNameIndex('obj_id')
            attrType = self.getNodeLayer().dataProvider().fieldNameIndex('type')
            attrDescr = self.getNodeLayer().dataProvider().fieldNameIndex('description')
            
            attributes = [attrObjId, attrType, attrDescr ]
            pointIds = [point.snappedAtGeometry for point in snappedPoints]
            nodeFeatures = self.getFeaturesById(self.getNodeLayer(), attributes, pointIds, True)

            # Filter wastewater nodes            
            filteredFeatures = { id: nodeFeatures.featureById(id) for id in nodeFeatures.asDict() if nodeFeatures.attrAsUnicode( nodeFeatures.featureById(id), u'type' ) == u'wastewater_node' }

            # Only one wastewater node left: return this
            if len( filteredFeatures ) == 1:
                return [point for point in snappedPoints if point.snappedAtGeometry == filteredFeatures.iterkeys().next() ][0]

            # Still not sure which point to take?
            # Are there no wastewater nodes filtered? Let the user choose from the reach points
            if len( filteredFeatures ) == 0:
                filteredFeatures = nodeFeatures.asDict()
            
            # Ask the user which point he wants to use 
            actions = dict()
            
            menu = QMenu( self.iface.mapCanvas() )
            
            for id, feature in filteredFeatures.iteritems():
                try:
                    title = feature.attribute( 'description' ) + " (" + feature.attribute( 'obj_id' ) + ")"
                except TypeError:
                    title = " (" + feature.attribute( 'obj_id' ) + ")"
                action = QAction( title, menu )
                actions[action] = point
                menu.addAction( action )
            
            clickedAction = menu.exec_( self.iface.mapCanvas().mapToGlobal( event.pos()  ) )
            
            if clickedAction is not None:
                return actions[clickedAction]
            
            return None
        
    
    # Finds the shortes path from the start point
    # to the end point
    def shortestPath(self,pStart,pEnd):
        if self.dirty:
            self.createGraph()
        
        try:
            path = nx.algorithms.dijkstra_path( self.graph, pStart, pEnd )
            edges = [(u,v,self.graph[u][v]) for (u,v)in zip(path[0:], path[1:])]

            p = ( path, edges )
        
        except nx.NetworkXNoPath:
            print "no path found"
            p = ([],[])
            
        return p
            
    def getTree(self,node,reverse=False):
        if self.dirty:
            self.createGraph()
        
        if reverse:
            myGraph = self.graph.reverse()
        else:
            myGraph = self.graph
            
        # Returns pred, weight
        pred, _ = nx.bellman_ford(myGraph, node)
        edges = [(v,u,myGraph[v][u]) for (u,v) in pred.items() if v is not None]
        
        return edges
    
    def getEdgeGeometry(self, edges):
        cache = self.getFeaturesById(self.reachLayer, self.reachLayer.dataProvider().attributeIndexes(), edges, True)
        polylines = [feat.geometry().asPolyline() for feat in cache.asDict().values()]
        return polylines
    
    def getFeaturesById(self, layer, attributes, ids, fetchGeometry):
        featCache = QgepFeatureCache(layer)
        dataProvider = layer.dataProvider()
        
        features = dataProvider.getFeatures()
        
        for feat in features:
            if feat.id() in ids:
                featCache.addFeature( feat )

        
        return featCache

    def getFeaturesByAttr(self, layer, attributes, attr, values, fetchGeometry):
        featCache = QgepFeatureCache(layer)
        dataProvider = layer.dataProvider()
        
        # Batch query and filter locally
        features = dataProvider.getFeatures()

        for feat in features:
            if featCache.attrAsUnicode( feat, attr ) in values :
                featCache.addFeature( feat )
                feat = QgsFeature()
        
        return featCache

    def print_profile(self):
        for (name, spenttime) in self.timings:
            print name + ":" + str( spenttime ) 
Пример #17
0
class QgepGraphManager(QObject):
    """
    Manages a graph
    """
    reachLayer = None
    reachLayerId = -1
    nodeLayer = None
    nodeLayerId = -1
    dirty = True
    graph = None
    vertexIds = {}
    nodesOnStructure = defaultdict(list)
    # Logs performance of graph creation
    timings = []

    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.snapper = None

    def setReachLayer(self, reach_layer):
        """
        Set the reach layer (edges)
        """
        self.reachLayer = reach_layer
        self.dirty = True

        if reach_layer:
            self.reachLayerId = reach_layer.id()
        else:
            self.reachLayerId = 0

        if self.nodeLayer and self.reachLayer:
            self.createGraph()

    def setNodeLayer(self, node_layer):
        """
        Set the node layer
        """
        self.dirty = True

        self.nodeLayer = node_layer

        if node_layer:
            self.nodeLayerId = node_layer.id()

        else:
            self.nodeLayerId = 0

        if self.nodeLayer and self.reachLayer:
            self.createGraph()

    def _addVertices(self):
        """
        Initializes the graph with the vertices from the node layer
        """
        node_provider = self.nodeLayer.dataProvider()

        features = node_provider.getFeatures()

        # Add all vertices
        for feat in features:
            fid = feat.id()

            obj_id = feat['obj_id']
            obj_type = feat['type']

            try:
                vertex = feat.geometry().asPoint()
            except AttributeError:
                # TODO Add to problem log
                pass
            self.graph.add_node(
                fid, dict(point=vertex, objType=obj_type, objId=obj_id))

            self.vertexIds[unicode(obj_id)] = fid

        self._profile("add vertices")

    def _addEdges(self):
        """
        Initializes the graph with the edges
        """
        # Add all edges (reach)
        reach_provider = self.reachLayer.dataProvider()

        features = reach_provider.getFeatures()

        # Loop through all reaches
        for feat in features:
            try:
                obj_id = feat['obj_id']
                obj_type = feat['type']
                from_obj_id = feat['from_obj_id']
                to_obj_id = feat['to_obj_id']

                length = feat['length_calc']

                pt_id1 = self.vertexIds[from_obj_id]
                pt_id2 = self.vertexIds[to_obj_id]

                props = {
                    'weight': length,
                    'feature': feat.id(),
                    'baseFeature': obj_id,
                    'objType': obj_type
                }
                self.graph.add_edge(pt_id1, pt_id2, props)
            except KeyError as e:
                print e

        self._profile("add edges")

    def refresh(self):
        """
        Refreshes the network graph. It will force a refresh of the materialized views in the database and then reload
        and recreate the graph.
        """
        uri = QgsDataSourceURI(self.nodeLayer.dataProvider().dataSourceUri())

        db = QSqlDatabase.addDatabase(
            "QPSQL")  # Name of the driver -- doesn't change

        str_connect_option = "requiressl=0;service=" + uri.service()
        db.setConnectOptions(str_connect_option)

        if not db.open():
            self.iface.messageBar().pushMessage(self.tr("Warning"),
                                                db.lastError().text(),
                                                level=QgsMessageBar.CRITICAL)

        query_template = "REFRESH MATERIALIZED VIEW qgep.vw_network_segment;"
        query = QSqlQuery(db)
        if not query.exec_(query_template):
            str_result = query.lastError().text()
            self.iface.messageBar().pushMessage(self.tr("Warning"),
                                                str_result,
                                                level=QgsMessageBar.CRITICAL)
        else:
            self.iface.messageBar().pushMessage(
                self.tr("Success"),
                "vw_network_segment successfully updated",
                level=QgsMessageBar.SUCCESS,
                duration=2)

        query_template = "REFRESH MATERIALIZED VIEW qgep.vw_network_node;"
        query = QSqlQuery(db)
        if not query.exec_(query_template):
            str_result = query.lastError().text()
            self.iface.messageBar().pushMessage(self.tr("Warning"),
                                                str_result,
                                                level=QgsMessageBar.CRITICAL)
        else:
            self.iface.messageBar().pushMessage(
                self.tr("Success"),
                "vw_network_node successfully updated",
                level=QgsMessageBar.SUCCESS,
                duration=2)
        # recreate networkx graph
        self.graph.clear()
        self.createGraph()

    def _profile(self, name):
        """
        Adds a performance profile snapshot with the given name
        """
        spenttime = 0
        if self.timings:
            spenttime = time.clock() - self.timings[-1][1]
        self.timings.append((name, spenttime))

    # Creates a network graph
    def createGraph(self):
        """
        Create a graph
        """
        self._profile("create graph")
        # try:
        self.vertexIds = {}
        self.nodesOnStructure = defaultdict(list)
        self._profile("initiate dicts")
        self.graph = nx.DiGraph()

        self._profile("initiate graph")

        self._addVertices()
        self._addEdges()

        self.print_profile()
        self.dirty = False

    def getNodeLayer(self):
        """
        Getter for the node layer
        """
        return self.nodeLayer

    def getReachLayer(self):
        """
        Getter for the reach layer
        :return:
        """
        return self.reachLayer

    def getNodeLayerId(self):
        """
        Getter for the node layer's id
        """
        return self.nodeLayerId

    def getReachLayerId(self):
        """
        Getter for the reach layer's id
        """
        return self.reachLayerId

    def getSnapper(self):
        """
        Getter for the snapper
        """
        self.snapper = QgsSnapper(self.iface.mapCanvas().mapRenderer())
        snap_layer = QgsSnapper.SnapLayer()
        snap_layer.mLayer = self.nodeLayer
        snap_layer.mTolerance = 10
        snap_layer.mUnitType = QgsTolerance.Pixels
        snap_layer.mSnapTo = QgsSnapper.SnapToVertex
        self.snapper.setSnapLayers([snap_layer])
        return self.snapper

    def snapPoint(self, event):
        """
        Snap to a point on this network
        :param event: A QMouseEvent
        """
        clicked_point = QPoint(event.pos().x(), event.pos().y())

        self.snapper = self.getSnapper()

        (_, snapped_points) = self.snapper.snapPoint(clicked_point, [])

        if not snapped_points:
            return None
        elif len(snapped_points) == 1:
            return snapped_points[0]
        elif len(snapped_points) > 1:

            point_ids = [point.snappedAtGeometry for point in snapped_points]
            node_features = self.getFeaturesById(self.getNodeLayer(),
                                                 point_ids)

            # Filter wastewater nodes
            filtered_features = {
                fid: node_features.featureById(fid)
                for fid in node_features.asDict()
                if node_features.attrAsUnicode(node_features.featureById(fid),
                                               u'type') == u'wastewater_node'
            }

            # Only one wastewater node left: return this
            if len(filtered_features) == 1:
                points = (point for point in snapped_points
                          if point.snappedAtGeometry ==
                          filtered_features.iterkeys().next())
                return points[0]

            # Still not sure which point to take?
            # Are there no wastewater nodes filtered? Let the user choose from the reach points
            if not filtered_features:
                filtered_features = node_features.asDict()

            # Ask the user which point he wants to use
            actions = dict()

            menu = QMenu(self.iface.mapCanvas())

            for _, feature in filtered_features.iteritems():
                try:
                    title = feature.attribute(
                        'description') + " (" + feature.attribute(
                            'obj_id') + ")"
                except TypeError:
                    title = " (" + feature.attribute('obj_id') + ")"
                action = QAction(title, menu)
                actions[action] = point
                menu.addAction(action)

            clicked_action = menu.exec_(self.iface.mapCanvas().mapToGlobal(
                event.pos()))

            if clicked_action is not None:
                return actions[clicked_action]

            return None

    def shortestPath(self, start_point, end_point):
        """
        Finds the shortes path from the start point
        to the end point
        :param start_point: The start node
        :param end_point:   The end node
        :return:       A (path, edges) tuple
        """
        if self.dirty:
            self.createGraph()

        try:
            path = nx.algorithms.dijkstra_path(self.graph, start_point,
                                               end_point)
            edges = [(u, v, self.graph[u][v])
                     for (u, v) in zip(path[0:], path[1:])]

            p = (path, edges)

        except nx.NetworkXNoPath:
            print "no path found"
            p = ([], [])

        return p

    def getTree(self, node, reverse=False):
        """
        Get
        :param node:    A start node
        :param reverse: Should the graph be reversed (upstream search)
        :return:        A list of edges
        """
        if self.dirty:
            self.createGraph()

        if reverse:
            my_graph = self.graph.reverse()
        else:
            my_graph = self.graph

        # Returns pred, weight
        pred, _ = nx.bellman_ford(my_graph, node)
        edges = [(v, u, my_graph[v][u]) for (u, v) in pred.items()
                 if v is not None]
        nodes = [
            my_graph.node[n] for n in set(pred.keys() + pred.values())
            if n is not None
        ]

        return nodes, edges

    def getEdgeGeometry(self, edges):
        """
        Get the geometry for some edges
        :param edges:  A list of edges
        :return:       A list of polylines
        """
        cache = self.getFeaturesById(self.reachLayer, edges)
        polylines = [
            feat.geometry().asPolyline() for feat in cache.asDict().values()
        ]
        return polylines

    # pylint: disable=no-self-use
    def getFeaturesById(self, layer, ids):
        """
        Get some features by their id
        """
        feat_cache = QgepFeatureCache(layer)
        data_provider = layer.dataProvider()

        features = data_provider.getFeatures()

        for feat in features:
            if feat.id() in ids:
                feat_cache.addFeature(feat)

        return feat_cache

    # pylint: disable=no-self-use
    def getFeaturesByAttr(self, layer, attr, values):
        """
        Get some features by an attribute value
        """
        feat_cache = QgepFeatureCache(layer)
        data_provider = layer.dataProvider()

        # Batch query and filter locally
        features = data_provider.getFeatures()

        for feat in features:
            if feat_cache.attrAsUnicode(feat, attr) in values:
                feat_cache.addFeature(feat)

        return feat_cache

    def print_profile(self):
        """
        Will print some performance profiling information
        """
        for (name, spenttime) in self.timings:
            print name + ":" + str(spenttime)
Пример #18
0
 def __init__( self, iface ):
     self.iface = iface
     self.snapper = QgsSnapper( self.iface.mapCanvas().mapRenderer() )