pythongpsipstreamlitinteractive

GPS tracking streamlit in mobile device


I'm running a Streamlit app where I try to retrieve the user's geolocation in streamlit. However, when using geocoder.ip("me"), the coordinates returned are 45, -121, which point to Oregon, USA, rather than my actual location.

This is the function I use:

def get_lat_lon():
    # Use geocoder to get the location based on IP
    g = geocoder.ip('me')
    
    if g.ok:
        lat = g.latlng[0]  # Latitude
        lon = g.latlng[1]  # Longitude
        return lat, lon
    else:
        st.error("Could not retrieve location from IP address.")
        return None, None

I would like to find a solution that can work in an streamlit app so by clicking a st.button I can call a function that retrieves my lat, long.


Solution

  • Consider the following simple Streamlit app:

    import streamlit as st
    import requests
    import geocoder
    from typing import Optional, Tuple
    
    def get_location_geocoder() -> Tuple[Optional[float], Optional[float]]:
        """
        Get location using geocoder library
        """
        g = geocoder.ip('me')
        if g.ok:
            return g.latlng[0], g.latlng[1]
        return None, None
    
    def get_location_ipapi() -> Tuple[Optional[float], Optional[float]]:
        """
        Fallback method using ipapi.co service
        """
        try:
            response = requests.get('https://ipapi.co/json/')
            if response.status_code == 200:
                data = response.json()
                lat = data.get('latitude')
                lon = data.get('longitude')
                
                if lat is not None and lon is not None:
                    # Store additional location data in session state
                    st.session_state.location_data = {
                        'city': data.get('city'),
                        'region': data.get('region'),
                        'country': data.get('country_name'),
                        'ip': data.get('ip')
                    }
                    return lat, lon
        except requests.RequestException as e:
            st.error(f"Error retrieving location from ipapi.co: {str(e)}")
        return None, None
    
    def get_location() -> Tuple[Optional[float], Optional[float]]:
        """
        Tries to get location first using geocoder, then falls back to ipapi.co
        """
        # Try geocoder first
        lat, lon = get_location_geocoder()
        
        # If geocoder fails, try ipapi
        if lat is None:
            st.info("Primary geolocation method unsuccessful, trying alternative...")
            lat, lon = get_location_ipapi()
        
        return lat, lon
    
    def show_location_details():
        """
        Displays the additional location details if available
        """
        if 'location_data' in st.session_state:
            data = st.session_state.location_data
            st.write("Location Details:")
            col1, col2 = st.columns(2)
            
            with col1:
                st.write("📍 City:", data['city'])
                st.write("🏘️ Region:", data['region'])
            
            with col2:
                st.write("🌍 Country:", data['country'])
                st.write("🔍 IP:", data['ip'])
    
    def main():
        st.title("IP Geolocation Demo")
        st.write("This app will attempt to detect your location using IP geolocation.")
        
        if st.button("Get My Location", type="primary"):
            with st.spinner("Retrieving your location..."):
                lat, lon = get_location()
                
                if lat is not None and lon is not None:
                    st.success("Location retrieved successfully!")
                    
                    # Create two columns for coordinates
                    col1, col2 = st.columns(2)
                    with col1:
                        st.metric("Latitude", f"{lat:.4f}")
                    with col2:
                        st.metric("Longitude", f"{lon:.4f}")
                    
                    # Show additional location details if available
                    show_location_details()
                    
                    # Display location on a map
                    st.write("📍 Location on Map:")
                    st.map(data={'lat': [lat], 'lon': [lon]}, zoom=10)
                else:
                    st.error("Could not determine your location. Please check your internet connection and try again.")
                    
    if __name__ == "__main__":
        main()
    
    

    The app above allows users to click a button to generate their location using the geocoder library. I added a back up in case geocoder doesn't work as expected, using the ipapi.co api, it has been proven to be more accurate at times. There will be some limitations with this approach, it will be less accurate relative to browser geolocation API alternatives. Nevertheless it works on all devices and doesn't require permissions, whereas the browser geolocation api version will. Below are some pictures of what the app from above looks like. The geolocation button:

    streamlit button

    And the resulting output:

    Streamlit button example

    Depending on how exact the location requirements are it might be worth exploring the browser geolocation api alternative.