2

I have a working code that selects 4 closest points from a mouse click and creates a convex hull using the selection.

def canvasReleaseEvent(self, mouseEvent):
    """ 
    Each time the mouse is clicked on the map canvas, perform 
    the following tasks:
        Loop through all visible vector point layers and for each:
            Ensure no features are selected
            Determine the distance of the closest features in the layer to the mouse click
            Keep track of the layer id and id of the closest feature
            Select the ids of the closest features and draw a hull convex using them """

    layerData = []

    for layer in self.canvas.layers():

        if layer.geometryType() != QGis.Point:

            # Ignore this layer as it's not a vector
            continue

        if layer.featureCount() == 0:
            # There are no features - skip
            continue

        layer.removeSelection()

        # Determine the location of the click in real-world coords
        layerPoint = self.toLayerCoordinates( layer, mouseEvent.pos() )
        shortestDistance = float("inf")
        closestFeatureId = -1

        for f in layer.getFeatures():
            dist = f.geometry().distance( QgsGeometry.fromPoint( layerPoint) )
            shortestDistance = dist
            closestFeatureId = f.id()

            info = (layer, closestFeatureId, shortestDistance)
            layerData.append(info)

        if not len(layerData) > 0:
            # Looks like no vector layers were found - do nothing
            return

        # Sort the layer information by shortest distance
        layerData.sort( key=lambda element: element[2] )
        selected_fid = []
        feat_geom = []
        for (layer, closestFeatureId, shortestDistance) in layerData:
            if len(selected_fid) < 4:
                selected_fid.append((layer, closestFeatureId, shortestDistance)) 
                points = layer.select(closestFeatureId)
            else:
                break

        # Convert selected features to list of QgsPoints
        selected_features = layer.selectedFeatures()
        for i in selected_features:
           geomp = i.geometry()
           f_geomp = geomp.asPoint()
           feat_geom.append(f_geomp)

        # Convert list of QgsPoints to multipoint geometry
        wkt = 'MULTIPOINT('            
        for point in feat_geom:
            x = point.x()
            y = point.y()
            wkt += '({} {}), '.format(x, y)
        wkt = wkt[:-1] + ')'
        print(wkt)
        # Create convex hull from nearest selected points (multipoint geometry)   
        multipoint = QgsGeometry.fromWkt(wkt)
        convexhull = multipoint.convexHull()


        #create memory layer if it doesn't exists or get it if it exists
        ly = None
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            if layer.name() == 'rejon':
                ly = layer
        if ly is None: 
            ly = QgsVectorLayer('Polygon?crs=epsg:27700', 'rejon', "memory")
            QgsMapLayerRegistry.instance().addMapLayers([ly])

        pr = ly.dataProvider()
        ly.startEditing()

        #add features and save changes
        poly = QgsFeature()
        poly.setGeometry(convexhull)
        pr.addFeatures([poly])
        ly.commitChanges()

Next step would to make sure that the same points are not used again with each mouse click to select closest points and create a convex hull. The idea is to create a set of convex hulls using each point only once. Operator has to be able to delete a polygon and use these points again, so the distinction if a point has already been used has to be made with relation to intersection with convex hull. Something like this:

  1. Check if (and which) points are within a polygon

I took care of that with this:

        intersections = []
        point_layer = QgsMapLayerRegistry.instance().mapLayersByName( 'flood_depth_points' )[0]
        polygon_layer = QgsMapLayerRegistry.instance().mapLayersByName( 'rejon' )[0]

        for m in point_layer.getFeatures():
            for n in polygon_layer.getFeatures():
                if polygon_layer.featureCount() == 0:
            # There are no features - skip
                    continue
                if m.geometry().buffer(1,0).intersects(n.geometry()):
                    intersections.append(m.id())
                    break
        print(len(intersections))
        print(intersections)
  1. If yes, alter the selection of closest points
  2. If no, select points and create a convex hull.

I don't know how to, based on that, alter selection of the closest points.

UPDATE: I got what I wanted by storing id's of points that intersect with existing polygons in intersections list:

        polygon_layer = QgsMapLayerRegistry.instance().mapLayersByName( 'rejon' )[0]

        intersections = []
        for m in layer.getFeatures():
            for n in polygon_layer.getFeatures():
                if polygon_layer.featureCount() == 0:
            # There are no features - skip
                    continue
                if m.geometry().buffer(1,0).intersects(n.geometry()):
                    inter_attrs = m.id()
                    inf = (inter_attrs)
                    intersections.append(inf)
                    break

and then checking if points' id's that the script iterates through after a mouse click exist in a said list before appending the point's id to layerData:

        for f in layer.getFeatures():
            dist = f.geometry().distance( QgsGeometry.fromPoint( layerPoint) )
            shortestDistance = dist

            # check if a point has already been used to create a polygon
            if f.id() not in intersections:
                closestFeatureId = f.id()
            info = (layer, closestFeatureId, shortestDistance)
            layerData.append(info)
  • 1
    Welcome to GIS SE, can you post your solution as an answer since it sounds like you figured it out? – artwork21 Jan 4 at 13:15
  • I've already updated my original post with relevant code fragments. – Dreamscape Jan 4 at 13:42
  • please post it as a answer then people can upvote it (and you will gain reputation) – Ian Turton Jan 4 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.