# -*- coding: utf-8 -*- """ Created on Fri Nov 11 07:26:42 2022 @author: mritchey """ import streamlit as st from streamlit_folium import st_folium import pandas as pd import numpy as np import folium from geopy.geocoders import Nominatim from geopy.extra.rate_limiter import RateLimiter from scipy import spatial from vincenty import vincenty from threading import Thread @st.cache_data def alt_vertices(): return pd.read_parquet('Atlantic Shore Vertices_short.parquet') @st.cache_data def gulf_vertices(): return pd.read_parquet('Gulf Shore Vertices2.parquet') @st.cache_data def convert_df(df): return df.to_csv(index=0).encode('utf-8') @st.cache_data def geocode(address): try: address2 = address.replace(' ', '+').replace(',', '%2C') df = pd.read_json( f'https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address={address2}&benchmark=2020&format=json') results = df.iloc[:1, 0][0][0]['coordinates'] lat, lon = results['y'], results['x'] except: geolocator = Nominatim(user_agent="GTA Lookup") geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1) location = geolocator.geocode(address) lat, lon = location.latitude, location.longitude return lat, lon @st.cache_data def thread_census_geocode_single(func_input, df): cols = df.columns.to_list()[:4] df['Address_q'] = df[cols[0]]+' %2C '+df[cols[1]] + \ ' %2C '+df[cols[2]]+' '+df[cols[3]].str[:5] df['Address_q'] = df['Address_q'].str.replace(' ', '+') address_values = df['Address_q'].values results_thread = [] def thread_func(value_input): response = func_input(value_input) results_thread.append(response) return True threads = [] for i in range(len(address_values)): process = Thread(target=thread_func, args=[address_values[i]]) process.start() threads.append(process) for process in threads: process.join() results = pd.merge(df, pd.concat(results_thread), on='Address_q') return results def map_results(results): for index, row in results.iterrows(): gulf_dis, alt_dis = results.loc[index, 'Distance to Gulf'], results.loc[index, 'Distance to Atlantic'] html = f"""
Gulf: {gulf_dis.round(1)} miles
Altantic: {alt_dis.round(1)} miles"""
iframe = folium.IFrame(html)
popup = folium.Popup(iframe,
min_width=140,
max_width=140)
folium.Marker(location=[results.loc[index, 'Lat_input'],
results.loc[index, 'Lon_input']],
fill_color='#43d9de',
popup=popup,
radius=8).add_to(m)
return folium
def distance(x):
left_coords = (x[0], x[1])
right_coords = (x[2], x[3])
return vincenty(left_coords, right_coords, miles=True)
def nearest_neighbor(df_input, df_points):
df_input = df_input.reset_index(drop=1)
df_points = df_points.reset_index(drop=1)
df_input2 = df_input[['Lon', 'Lat']]
df_points = df_points.reset_index()
df_kd_fires = spatial.KDTree(df_points[['Lon', 'Lat']])
ktree = df_kd_fires.query(df_input2, k=1, workers=-1)
output = pd.DataFrame(ktree[0], ktree[1]).reset_index()
results = pd.merge(output, df_points, how='left', on='index')
results2 = df_input.join(results, lsuffix='_input', rsuffix='_attached')
results2['HubDist'] = pd.DataFrame([distance(i) for i in results2[[
'Lat_input', 'Lon_input', 'Lat_attached', 'Lon_attached']].to_numpy()])
results2['HubDist'] = results2['HubDist'].astype(float).round(2)
return results2.drop(columns=['index', 0])
def distance_coast(df):
Atl_vertices = alt_vertices() # Attach to
Gulf_vertices = gulf_vertices() # Attach to
df_atl = nearest_neighbor(df, Atl_vertices[['Lat', 'Lon']]).rename(columns={"HubDist": "Distance to Atlantic",
"Lat_attached": "Lat_atl", "Lon_attached": "Lon_atl"})
df_gulf = nearest_neighbor(df, Gulf_vertices[['Lat', 'Lon']]).rename(columns={"HubDist": "Distance to Gulf",
"Lat_attached": "Lat_gulf", "Lon_attached": "Lon_gulf"})
df_gulf = df_gulf.drop(columns=['Lat_input', 'Lon_input'])
final = pd.concat([df_atl, df_gulf], axis=1)
final = final.loc[:, ~final.columns.duplicated()]
return final
def census_geocode_single_address(address):
try:
df = pd.read_json(
f'https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address={address}&benchmark=2020&format=json')
results = df.iloc[:1, 0][0][0]['coordinates']
x, y = results['x'], results['y']
except:
x, y = np.nan, np.nan
return pd.DataFrame({'Address_q': address, 'Lat': y, 'Lon': x}, index=[0])
st.set_page_config(layout="wide")
col1, col2 = st.columns((2))
address = st.sidebar.text_input(
"Address", "123 Main Street, Columbus, OH 43215")
uploaded_file = st.sidebar.file_uploader("Choose a file")
address_file = st.sidebar.radio('Choose',
('Single Address', 'Addresses (Geocode: Will take a bit)', 'Lat Lons'))
if address_file == 'Lat Lons' and not None:
try:
df = pd.read_csv(uploaded_file)[['Lat', 'Lon']]
except:
st.header('Make Sure there is a Lat and Lon Field')
elif address_file == 'Addresses (Geocode: Will take a bit)' and not None:
try:
df = pd.read_csv(uploaded_file)
cols = df.columns.to_list()[:4]
with st.spinner("Geocoding: Hang On..."):
df3 = thread_census_geocode_single(
census_geocode_single_address, df)
df = df3.query("Lat==Lat")
errors = df3.query("Lat!=Lat")[cols].reset_index(drop=1)
except:
st.header('Make Sure File is Loaded First and then hit "Addresses"')
else:
lat, lon = geocode(address)
df = pd.DataFrame({'Lat': lat, 'Lon': lon}, index=[0])
results = distance_coast(df)
if address_file == 'Addresses':
results = results[cols+['Lat_input', 'Lon_input',
'Distance to Gulf', 'Distance to Atlantic']]
else:
results = results[['Lat_input', 'Lon_input',
'Distance to Gulf', 'Distance to Atlantic']]
m = folium.Map(location=[39.50, -98.35], zoom_start=3)
with col1:
st.title('Distance to Coast')
map_results(results)
st_folium(m, height=500, width=500)
with col2:
st.title('Results')
results.index = np.arange(1, len(results) + 1)
st.dataframe(results)
csv = convert_df(results)
st.download_button(
label="Download data as CSV",
data=csv,
file_name='Results.csv',
mime='text/csv')
try:
if errors.shape[0] > 0:
st.header('Errors')
errors.index = np.arange(1, len(errors) + 1)
st.dataframe(errors)
# st.table(errors.assign(hack='').set_index('hack'))
csv2 = convert_df(errors)
st.download_button(
label="Download Errors as CSV",
data=csv2,
file_name='Errors.csv',
mime='text/csv')
except:
pass
st.markdown(""" """, unsafe_allow_html=True)