How do I explore a map without deleting everything in Streamlit-Folium?

Question:

I’m using streamlit-folium to visualize a map in Streamlit and let the user select a custom number of points.
The map has a default starting point but in my wish the user can explore the map with the help of a search bar.

import folium
import streamlit as st
from folium.plugins import Draw
from geopy.geocoders import Nominatim
from streamlit_folium import st_folium

# Default location
x, y = [41.00, 29.00]

# Search for another location
location_input = st.text_input("Search in the map")
if location_input:
    location = Nominatim(user_agent="GetLoc")
    getLocation = location.geocode(location_input)
    x, y = getLocation.latitude, getLocation.longitude

# Draw the map centered in location
m = folium.Map(location=[x,y],zoom_start=12)
Draw(
    draw_options={
        'polyline': False, 'rectangle': False, 
        'circle': False, 'polygon': False,
        'circlemarker': False
    },
    edit_options={'remove': False}
).add_to(m)
Map = st_folium(m, width = 700, height=500)

My problem is that when I search for a new location, all the markers I have put on the map disappear.

Default

default with a marker

After searching

after search

Hope someone can help. I will keep updating this post if I reach something on my own.
Thank you very much.

Asked By: chc

||

Answers:

I solved my problem. Here’s the code:

import folium
import streamlit as st
from folium.plugins import Draw
from streamlit_folium import st_folium
from typing import Union

import geocoder
import requests
import urllib.parse

def latlon(address: str) -> tuple[float, float]:
    url = f"https://nominatim.openstreetmap.org/search/{urllib.parse.quote(address)}?format=json"
    response = requests.get(url).json()
    return response[0]["lat"], response[0]["lon"]

def geolocalize() -> Union[tuple[float, float], None]:
    try:
        latitude, longitude = geocoder.ip('me').latlng
        return latitude, longitude
    except:
        return None

def geo_map(
    name: str,
    search_hint: dict[str, str],
    extra_hint: dict[str, str],
    submit: dict[str, str],
    lang: str
) -> dict[list[float],str]:
    c1, c2 = st.columns(2)

    with c1:

        if "geolocalized" not in st.session_state:
            st.session_state.geolocalized = geolocalize()
            
        if f"location_input_{name}" not in st.session_state:
            st.session_state[f"location_input_{name}"] = ""
        
        location_input = st.text_input(
            label="", placeholder=search_hint[lang], key=f"text_input_{name}"
        )

        try:
            if location_input==st.session_state[f"location_input_{name}"]:
                raise Exception
            x, y = latlon(location_input)
            st.session_state[f"location_input_{name}"]=location_input
            st.session_state[f"location_coord_{name}"]=[x,y]
        except:
            last_clicked = None
            if f"lastclicked_{name}" in st.session_state:
                last_clicked = st.session_state[f"lastclicked_{name}"]
            last_marker = None
            if f"lastmarker_{name}" in st.session_state:
                last_marker = st.session_state[f"lastmarker_{name}"]

            if f"location_coord_{name}" in st.session_state and st.session_state[f"location_coord_{name}"]:
                x, y = st.session_state[f"location_coord_{name}"]
            elif last_marker or last_clicked:
                x, y =  last_marker[::-1] or [last_clicked["lat"], last_clicked["lng"]]
            else:
                x, y =  [45.971750,13.639878]
                        or st.session_state.geolocalized 
                        or [45.652020, 13.783930] # HQ

        ## Map
        m = folium.Map(location=[x,y], zoom_start=15)
        draw_options={foo: False for foo in ['polyline', 'rectangle', 'circle', 'polygon', 'circlemarker']}
        ##draw_options['marker'] = {'icon'=}
        Draw(
            draw_options=draw_options,
            edit_options={'edit':False, 'remove': False}
        ).add_to(m)

        if f"points_{name}" in st.session_state:
            for point in st.session_state[f"points_{name}"]:
                x, y = [float(n) for n in point[1:-1].split(',')]
                folium.Marker(
                    #?
                    location=[y,x], 
                    #?
                    tooltip = st.session_state[f"points_{name}"][point] or None
                ).add_to(m)

        Map = st_folium(m, width = 700, height=500, key=f"map_{name}")

        if f"points_{name}" not in st.session_state:
            st.session_state[f"points_{name}"] = {}
        if f"lastclicked_{name}" not in st.session_state:
            st.session_state[f"lastclicked_{name}"] = None
        if f"lastmarker_{name}" not in st.session_state:
            st.session_state[f"lastmarker_{name}"] = None

    with c2:

        points = Map.get("all_drawings")
        if points:
            form = st.form(f"labels_{name}")
            labels = []
            for i, p in enumerate(points):
                labels.append(form.text_input(
                        f"{extra_hint[lang]} {i+1}",
                        ""
                    )
                )
            sub = form.form_submit_button(submit[lang])
            if sub:
                st.session_state[f"lastclicked_{name}"] = Map["last_clicked"]
                st.session_state[f"lastmarker_{name}"] = 
                    Map["last_active_drawing"]["geometry"]["coordinates"]
                
                points = Map.get("all_drawings")
                for point, label in zip(points, labels):
                    coordinates_p = point["geometry"]["coordinates"]
                    st.session_state[f"points_{name}"][str(coordinates_p)] = label

    return st.session_state[f"points_{name}"]

Usage example:


map_hint = {
    'eng': 'Search in the map',
    'ita': 'Cerca nella mappa',
    'slo': 'Iskanje na zemljevidu'
}
extra_hint1 = {
    'eng': 'Name or reason for favorite place',
    'ita': 'Nome o motivo del posto preferito',
    'slo': 'Ime ali razlog za najljubši kraj'
}
submit = {
    'eng': 'Submit (click twice)',
    'ita': 'Invia (clicca due volte)',
    'slo': 'Pošlji (dvakrat kliknite)'
}
map1 = geo_map(
    'map1',
    search_hint=map_hint,
    extra_hint=extra_hint1,
    submit=submit,
    lang='eng'
)
Answered By: chc
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.