Obtaining clickData (lat, lon) on maps

Hello everybody . I m new here
I have the problem of the topic and i cant resolve it
I m looking for a code that is able to do the job .
im using python 3.10 and dash
in a first approach
I want to click on a map everywhere of the europe.
I want to get the coordinates lon lat in a callback
And i want to place something in the map to say that this point is selected
I want to have the coordinates that are displayed
All the previous examples are complex or doesnt work for me
I dont understand . Dash plotly can do so magic things . And this simple task is a wall for me
I m on it for a while. Maybe the problem is between the screen of my computer and my chair.
Please someone has a code example who works ?
Angelo

hey @Angelo28300 welcome to the forums.

What did you try so far? Could you provide sample code?

EDIT, and example:

from dash import Dash, html, Input, Output, Patch
import dash_leaflet as dl
import json


app = Dash()
app.layout = html.Div(
    [
        dl.Map(
            id='map',
            children=[
                dl.TileLayer()
            ],
            center=[56, 10],
            zoom=6,
            style={'height': '50vh'}
        ),
        html.Div(id='out')
    ]
)


@app.callback(
    Output('out', 'children'),
    Output('map', 'children'),
    Input('map', 'clickData'),
    prevent_initial_call=True
)
def do(click_data):
    # extract coordinates form click_data
    coordinates = click_data['latlng']
    lat, lon = coordinates.values()

    # create Patch() instance, add Marker layer at click coordinates
    patched = Patch()
    patched.append(dl.Marker(position=[lat, lon]))

    return json.dumps(coordinates), patched


if __name__ == '__main__':
    app.run(debug=True)

I tried the proposed code and it works partially fine .
I didn’t know the use of the Patch object .
The proposed code add a point on the map at each click . I would like that it was replaced by the new point at each click and the old disappear of the map
But it is the first time i have a begining of solution.
Thanks a lot AIMPED

1 Like

You’re welcome @Angelo28300. You don’t necessarily have to use the Patch() (partial updates), it decreases the data going from server to client.

An other example:

from dash import Dash, html, Input, Output
import dash_leaflet as dl
import json


app = Dash()
app.layout = html.Div(
    [
        dl.Map(
            id='map',
            children=[
                dl.TileLayer()
            ],
            center=[56, 10],
            zoom=6,
            style={'height': '50vh'}
        ),
        html.Div(id='out')
    ]
)


@app.callback(
    Output('out', 'children'),
    Output('map', 'children'),
    Input('map', 'clickData'),
    prevent_initial_call=True
)
def do(click_data):
    # extract coordinates form click_data
    coordinates = click_data['latlng']
    lat, lon = coordinates.values()

    return json.dumps(coordinates), [dl.TileLayer(), dl.Marker(position=[lat, lon])]


if __name__ == '__main__':
    app.run(debug=True)

If you need further assistance, let us know.

Wonderful. It’s really what i need . Thanks a lot. Is it possible to set a limit to the map?, for example , i want that the user can not select a point out of a box that is centered on europa.

Sure, you could do this. I would actually check, if the clicked point is within the boundaries of a geojson file (in your case, one of Europe).

from dash import Dash, html, Input, Output, Patch
import dash_leaflet as dl
import json
from shapely.geometry import shape, Point


with open('assets/europe.geojson', 'r') as f:
    js = json.load(f)


def is_point_in(lat, lon, geojson=js):
    point_in = False

    # create shapely point
    point = Point(lat, lon)

    # check each feature in geojson, if point is within bounds
    for feature in geojson['features']:
        # create shapely shape from geojson feature
        polygon = shape(feature['geometry'])

        if polygon.contains(point):
            point_in = True
            break

    return point_in


app = Dash()
app.layout = html.Div(
    [
        dl.Map(
            id='map',
            children=[
                dl.TileLayer(),
                # dl.GeoJSON(
                #     url='assets/europe.geojson'
                # )
                # ^^ comment in if you want to show the outline on map
            ],
            center=[49, 10],
            zoom=4,
            style={'height': '50vh'}
        ),
        html.Div(id='out')
    ]
)


@app.callback(
    Output('out', 'children'),
    Output('map', 'children'),
    Input('map', 'clickData'),
    prevent_initial_call=True
)
def do(click_data):
    # extract coordinates form click_data
    coordinates = click_data['latlng']
    lat, lon = coordinates.values()

    # check if clicked point is within Europe bounds
    point_in_europe = is_point_in(lon, lat)

    # Patch() instance
    patched = Patch()

    if point_in_europe:
        # patched[2] = dl.Marker(position=[lat, lon])
        # ^^ use this line if you are showing the geojson layer with european outline
        patched[1] = dl.Marker(position=[lat, lon])

    return json.dumps(coordinates), patched


if __name__ == '__main__':
    app.run(debug=True)

For my tests I used this geojson file.

1 Like

Woah . I tested it. It runs very well . Thanks a lot AIMPED .
testing the europa outlining , i see that If i click on the map one time the outlines disappears and the point appears . After that, the outlines dont come anymore , i need to reload the webpage to get it again .
Is it possible to preserve the outlining when one point is selected
Angelo

Just change

patched[1] = dl.Marker(position=[lat, lon])

To

patched[2] = dl.Marker(position=[lat, lon])
1 Like

Thanks a lot . I understood my mistake.
It works really fine
Angelo

1 Like

Yall might find this useful: https://geojson-maps.kyd.au/

1 Like