import pandas as pd import plotly.express as px import streamlit as st from lat_lon_parser import parse, to_str_deg_min_sec from st_aggrid import AgGrid class DataFrames: Dframe = pd.DataFrame() st.title("GPS Coordinate Converter") st.write( """This program allows you to convert coordinates from degree-minute-second (13°15'6.20"N) to decimal (13.2517222222222) and vice versa. Please choose a file containing the latitude and longitude columns. """ ) decimal_to_degrees_sample_file_path = "samples/Decimal_to_DMS.xlsx" degrees_to_decimal_sample_file_path = "samples/DMS_to_Decimal.xlsx" col1, col2, col3 = st.columns(3) with col1: st.download_button( label="Download Decimal_to_DMS Sample File", data=open(decimal_to_degrees_sample_file_path, "rb").read(), file_name="Decimal_to_DMS.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ) with col2: st.download_button( label="Download DMS_to_Decimal Sample File", data=open(degrees_to_decimal_sample_file_path, "rb").read(), file_name="DMS_to_Decimal.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ) uploaded_file = st.file_uploader("Choose a file", type=["xlsx"]) col1_list = [] if uploaded_file is not None: DataFrames.Dframe = pd.read_excel(uploaded_file, keep_default_na=False) col1_list = DataFrames.Dframe.columns.tolist() latitude_dd = st.selectbox("Choose Latitude Column", options=col1_list) longitude_dd = st.selectbox("Choose Longitude Column", options=col1_list) conversion_choice = st.selectbox( "Choose Conversion Type", options=["Decimal", "DegMinSec"] ) if st.button("CONVERT", type="primary"): try: # if not latitude_dd or not longitude_dd: # st.error("Please choose the latitude and longitude columns") # if DataFrames.Dframe.empty: # st.error("Please choose a file first") df = DataFrames.Dframe.copy() df["converted_latitude"] = df[latitude_dd] df["converted_longitude"] = df[longitude_dd] if conversion_choice == "Decimal": df["converted_longitude"] = df["converted_longitude"].str.replace( "O", "W" ) df["converted_latitude"] = df["converted_latitude"].apply(parse) df["converted_longitude"] = df["converted_longitude"].apply(parse) else: df["converted_latitude"] = df["converted_latitude"].apply( to_str_deg_min_sec ) df["converted_longitude"] = df["converted_longitude"].apply( to_str_deg_min_sec ) df["converted_latitude"] = df["converted_latitude"].apply( lambda x: x.replace("-", "") + "S" if "-" in x else x + "N" ) df["converted_longitude"] = df["converted_longitude"].apply( lambda x: x.replace("-", "") + "W" if "-" in x else x + "E" ) DataFrames.Dframe = df st.success("Coordinates converted Sucessfully") @st.fragment def table_data(): if DataFrames.Dframe is not None: AgGrid( DataFrames.Dframe, fit_columns_on_grid_load=True, theme="streamlit", enable_enterprise_modules=True, filter=True, # columns_auto_size_mode=ColumnsAutoSizeMode.FIT_CONTENTS, ) table_data() # Display map visualization st.subheader("📍 Map Visualization") try: map_df = DataFrames.Dframe.copy() # Determine which columns contain decimal coordinates for mapping if conversion_choice == "Decimal": lat_col = "converted_latitude" lon_col = "converted_longitude" else: # For DMS output, use original decimal source columns lat_col = latitude_dd lon_col = longitude_dd # Ensure they are numeric map_df[lat_col] = pd.to_numeric(map_df[lat_col], errors="coerce") map_df[lon_col] = pd.to_numeric(map_df[lon_col], errors="coerce") # Create hover text with available columns hover_cols = [ col for col in map_df.columns if col not in [lat_col, lon_col, "hover_text"] ] if hover_cols: map_df["hover_text"] = map_df[hover_cols].astype(str).agg( "
".join, axis=1 ) else: map_df["hover_text"] = "Point" # Filter out invalid coordinates map_df = map_df.dropna(subset=[lat_col, lon_col]) if not map_df.empty: fig = px.scatter_mapbox( map_df, lat=lat_col, lon=lon_col, hover_name="hover_text", zoom=5, height=500, ) fig.update_layout( mapbox_style="open-street-map", margin={"r": 0, "t": 0, "l": 0, "b": 0}, ) fig.update_traces(marker=dict(size=12, color="#FF4B4B")) st.plotly_chart(fig, use_container_width=True) else: st.warning("No valid coordinates available for map display.") except Exception as map_error: st.warning( f"Could not display map. Ensure coordinates are valid decimal values. Error: {map_error}" ) except Exception as e: st.error( f"An error occurred. Make sure the file contains the latitude and longitude columns. Error: {e}" ) else: st.info("Please choose a file containing the latitude and longitude columns")