Returning Latitude, Longitude values from folium map on mouse click to python script (streamlit webapp)
Asked Answered
K

4

10

I am writing a webapp where the user will click on the map and the latitude, longitude will be returned to the python script. the webapp is written on streamlit and for the map I am using folium. Currently, I used folium.LatLngPopup() to get the on click lat long of the clicked location, but I am very confused how to retrieve the values in my python script for further processing.

This is how it looks currently:

This is how it looks currently

#code snippet

import streamlit as st
from streamlit_folium import folium_static
import folium
m = folium.Map()
m.add_child(folium.LatLngPopup())
folium_static(m)

There might not be a native solution to it but any workaround will be appreciated!

Kohn answered 14/8, 2020 at 13:19 Comment(5)
What specific part are you confused about or having trouble with?Alyssaalyssum
Passing those latitude longitude values from the pop-up to python variable.Kohn
Currently, the package only displays a Folium map, it does not have bi-directional data transfer. If this is functionality you desire, I suggest opening an issue on the GitHub repository (I'm the package developer)Lisabeth
Hey Randy, I used your package, thanks for having it there in the first place. I'll open an issue on github regarding it. But is there a way to deal with it for now, I tried a many ways but my lack of JS knowledge and folium's limitation on Js->python communication kinda held me implementing it.Kohn
No, there isn't a way to deal with it at this moment. As I said, data only flows one-way currently, from Python to JavaScript. There are no return values.Lisabeth
S
5

If you can directly use the value instead of assigning it, you can just use map['last_clicked']['lat'] and map['last_clicked']['lng'] where map is defined. For example here I just print every location's lat/lng, but instead of just returning them, in a function you can do any operation you need:

import folium as fl
from streamlit_folium import st_folium
import streamlit as st

def get_pos(lat,lng):
    return lat,lng

m = fl.Map()

m.add_child(fl.LatLngPopup())

map = st_folium(m, height=350, width=700)


data = get_pos(map['last_clicked']['lat'],map['last_clicked']['lng'])

if data is not None:
    st.write(data)
Symbolics answered 7/6, 2022 at 15:28 Comment(3)
Please provide working code with continuous lat/lng output, explain how you run this with the streamlit server, and how you exit.Swellfish
Is it possible to feed the click event-data back into streamlit-folium? I was expecting to be able to e.g. center the map on the location of the mouse click, but I have not had any luck in getting the click data back into the map and have it react like I expected.Ruebenrueda
Answered your question not2qubit in new answer :)Yuen
O
0

I recently encountered the same problem to get the latitude and longitude then print it or save the values to a variable. Apparently, I found out the usual streamlit is static as I tried to create a streamlit.button which is supposed to add 1 to variable counter if clicked. Result: the variable counter is printed as 1, regardless how many I clicked.

To be able to take the input, the streamlit component should be 'bi-directional'. I might not be able to explain it clearly as I am still newb both in python or in javascript, but in short it suppose to listen to the change in the values.

The solution is to create the bi-directional component and later I found this awesome repo streamlit-light-leaflet. To run it, just follow along the README.md. Assuming you already install streamlit in the virtual env, do the npm install and npm run start. Then run the 'streamlit run file.py' in different terminal/command line. Click the url from the terminal where you run the streamlit.

If the map doesn't show, go to my_component/frontend/src/index.tsx and put your mapbox.com accessToken. To get the accessToken, you should sign up first to mapbox.com. It would be best practice to use .env and call your mapbox accessToken from there, to keep the code cleaner.

Okinawa answered 25/5, 2021 at 17:38 Comment(0)
Y
0

Building on the answer of francesco morri, I added the missing parts to make it run that address the questions under it. Super grateful about francescos answer.

# my_app.py
import folium as fl
from streamlit_folium import st_folium
import streamlit as st


def get_pos(lat, lng):
    return lat, lng


m = fl.Map()

m.add_child(fl.LatLngPopup())

map = st_folium(m, height=350, width=700)

data = None
if map.get("last_clicked"):
    data = get_pos(map["last_clicked"]["lat"], map["last_clicked"]["lng"])

if data is not None:
    st.write(data) # Writes to the app
    print(data) # Writes to terminal

Run it with:

streamlit run my_app.py
Yuen answered 29/6, 2023 at 10:3 Comment(0)
D
0

In case you are using Gradio, here how to get the coordinate value when the user is clicking on a button:

import gradio as gr
from gradio_folium import Folium
from folium import Map, Element, LatLngPopup
import pandas as pd
import pathlib

def click(coord):
    print(coord)

def inject_javascript(folium_map):
    script = """<script>
    document.addEventListener('DOMContentLoaded', function() {
        map_name_1.on('click', function(e) {
            window.state_data = e.latlng
        });
    });
    </script>
    """
    folium_map.get_root().html.add_child(Element(script))

with gr.Blocks() as demo:
    map = Map(location=[25.7617, 80.1918])
    map._name, map._id = "map_name", "1"

    LatLngPopup().add_to(map)

    inject_javascript(map)
    fol = Folium(value=map, height=400, elem_id="map-component")
    txt = gr.Textbox(value="No coordinates selected", label="Latitude, Longitude", elem_id="coord-component", visible=False)
    js =  """
    (textBox) => {
        const iframeMap = document.getElementById('map-component').getElementsByTagName('iframe')[0];
        const latlng = iframeMap.contentWindow.state_data;
        if (!latlng) { return; }
        //document.getElementById('coord-component').getElementsByTagName('textarea')[0].value = `${latlng.lat},${latlng.lng}`;
        return `${latlng.lat},${latlng.lng}`;
    }
    """
    button = gr.Button("Get results")
    button.click(click, inputs=[txt], js=js)

demo.launch()
Dreeda answered 24/4, 2024 at 10:18 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.