6

How do I get X,Y coordinate with mouse click with enabling snapping so that only line's vertex point coordinates will be selected?

By using following code I can get latitude longitude of clicked position in map canvas of QGIS but cannot enable the snapping option .

from qgis.gui import QgsMapToolEmitPoint
class PrintClickedPoint(QgsMapToolEmitPoint):
    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)

    def canvasPressEvent( self, e ):
        point = self.toMapCoordinates(self.canvas.mouseLastXY())
        point = list(point)
        iface.mapCanvas().snappingUtils().toggleEnabled()
        print(point)

canvas_clicked = PrintClickedPoint( iface.mapCanvas() )
iface.mapCanvas().setMapTool( canvas_clicked )
1
  • You don't have any reference to the current layer. Maybe that is required.
    – wingnut
    Apr 9, 2021 at 6:15

1 Answer 1

6

Try the example below. Paste into a new editor in the Python console, select an active layer (must be a vector layer, since there is no safeguard in the script to handle raster layers etc!) and click run to test.

class PrintSnappedPoint(QgsMapToolEmitPoint):

    def __init__(self, canvas, transform=False):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.transform = transform
        self.project = QgsProject.instance()
        self.l = iface.activeLayer()
        self.i = QgsSnapIndicator(self.canvas)
        self.u = self.canvas.snappingUtils()
        self.c = self.u.config()
        self.c.setEnabled(True)
        self.c.setMode(QgsSnappingConfig.AdvancedConfiguration)
        self.s = QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.VertexFlag, 25.00,
                    QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)
        
        #Define source and destination crs's and instantiate QgsCoordinateTransform class
        self.src_crs = self.project.crs()
        #Best to change epsg code below to a local projected crs (e.g. UTM)
        self.dst_crs = QgsCoordinateReferenceSystem('EPSG:3857')
        self.x_form = QgsCoordinateTransform(self.src_crs, self.dst_crs, self.project)

    def canvasMoveEvent(self, e):
        m = self.u.snapToMap(e.pos())
        self.i.setMatch(m)

    def canvasPressEvent(self, e):
        if self.i.match().type():
            pointxy = self.i.match().point()
        else:
            pointxy = None
        if pointxy:
            if self.transform:
                tr = self.x_form.transform(pointxy)
                point = [tr.x(), tr.y()]
            else:
                point = [pointxy.x(), pointxy.y()]
            print('Point snapped to vertex: {}'.format(point))
            
    def deactivate(self):
        self.s = QgsSnappingConfig.IndividualLayerSettings(False, QgsSnappingConfig.NoSnapFlag, 25.00,
                    QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)


canvas = iface.mapCanvas()
#to transform snapped click-points, pass True as 2nd argument to constructor
T = PrintSnappedPoint(canvas, True)
canvas.setMapTool(T)

Edit based on comments:

I have updated the code above to give an example of using QgsCoordinateTransform class to transform the the snapped click-point from the project crs to another crs of your choosing. In this example my project crs is epsg:4326 so I transform the point from that to epsg:3857. However, I would recommend changing the epsg code of the destination crs to an accurate local projected crs such as a UTM zone appropriate to your area.

To use the transform function, pass True as the 2nd argument to the constructor of the PrintSnappedPoint class constructor. If you don't pass a 2nd argument, the transform parameter will default to False and the snapped points will be printed according to the project crs.

Expected results shown in the gif below:

enter image description here

3
  • Thank You @Ben Code works perfectly.Can you please suggest how to get X,Y coordinate rather than latitude and longitude?
    – Devenepali
    Apr 13, 2021 at 5:34
  • No problem @Devenepali. If I understand what you are asking here, the answer is that if you simply use a projected crs you will get XY coordinates instead of geographic degrees. You can also use the QgsCoordinateTransform class to do an on-the-fly transformation from the current project crs to any projected crs. I have updated my answer to show an example of how to do this.
    – Ben W
    Apr 13, 2021 at 10:43
  • thanks for the reply it works perfectly in QGIS console environment. As I tried to implanted this feature in the QGIS plugin , I want to press the push bottom and run the above code and get location selected by user. I connected the function to the push bottom making the object of PrintSnappedPoint class with canvas = iface.mapCanvas() object and called canvas.setMapTool(T). But that does not seems getting any effect in QGIS canvas. Should I need to implement multi-threading or should I need to add any other code so that above code can work in plugin environment. Thanks again Ben.
    – Devenepali
    Sep 15, 2021 at 0:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.