152 lines
6.1 KiB
Python
152 lines
6.1 KiB
Python
import streamlit as st
|
|
import pandas as pd # package for database connection
|
|
import folium # package for creating maps
|
|
from streamlit_folium import folium_static # package for displaying maps on streamlit
|
|
from geopy.geocoders import Nominatim # package for geolocation conversion
|
|
import sqlite3 # package for database connection
|
|
from datetime import datetime # package for timestamp
|
|
from folium.features import CustomIcon # package for custom icons on map
|
|
|
|
|
|
def pythonx_geomap():
|
|
# Initialize the database
|
|
conn = sqlite3.connect('./files/student_locations.db')
|
|
# Create a table to store student locations
|
|
c = conn.cursor()
|
|
c.execute('''CREATE TABLE IF NOT EXISTS students
|
|
(id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
city TEXT,
|
|
state TEXT,
|
|
country TEXT,
|
|
latitude REAL,
|
|
longitude REAL,
|
|
timestamp DATETIME)''')
|
|
conn.commit()
|
|
|
|
|
|
st.title("Lesson 4: Geographical Data Visualization")
|
|
st.markdown("""
|
|
This lesson demonstrates how to handle geolocation data and visualize it using maps in Python.
|
|
|
|
The main functionalities include:
|
|
|
|
- **User Input for Location**: Users can input their city and state or city and country through a form.
|
|
|
|
- **Geolocation Processing**: The input location is processed to obtain latitude and longitude coordinates.
|
|
|
|
- **Data Storage**: The processed location data is saved to a [SQLite](https://www.geeksforgeeks.org/python-sqlite/) database.
|
|
|
|
- **Map Visualization**: All stored student locations are displayed on an interactive map.
|
|
|
|
- **Statistics Display**: The code also provides statistics on the total number of students, unique cities, and unique countries, along with the top 5 cities.
|
|
""")
|
|
|
|
# Display the source code
|
|
with st.expander("See Source Code"):
|
|
with open(__file__, "r", encoding="utf-8") as f:
|
|
st.code(f.read(), language="python")
|
|
|
|
st.subheader("Student Location Tracker")
|
|
# Input form for student location
|
|
with st.form("student_form"):
|
|
input_city = st.text_input("Enter your City, State (e.g.: Amarillo,TX), or City, Country (Toronto, Canada):")
|
|
|
|
submitted = st.form_submit_button("Submit")
|
|
|
|
if submitted:
|
|
# converting addresses (like "Mountain View, CA") into geographic
|
|
# coordinates (like latitude 37.423021 and longitude -122.083739)
|
|
lat, lon, city, state, country = get_location(input_city)
|
|
# Save the location data to the database
|
|
if lat and lon:
|
|
save_student(conn, city, state, country, lat, lon)
|
|
st.success(f"Location saved: {city}, {country}")
|
|
else:
|
|
st.error("Unable to find the location. Please try a different city name.")
|
|
|
|
# Display all students on a map
|
|
st.subheader('All Student Locations')
|
|
df = get_all_students(conn)
|
|
if not df.empty:
|
|
m = create_map(df)
|
|
folium_static(m)
|
|
else:
|
|
st.write("No student data available yet.")
|
|
|
|
# Display student statistics
|
|
st.subheader('Student Statistics')
|
|
if not df.empty:
|
|
st.write(f"Total students: {len(df)}")
|
|
st.write(f"Total cities: {df['city'].nunique()}")
|
|
st.write(f"Total countries: {df['country'].nunique()}")
|
|
st.write("Top 5 cities:")
|
|
st.write(df['city'].value_counts().head())
|
|
st.write("Top 5 countries:")
|
|
st.write(df['country'].value_counts().head())
|
|
else:
|
|
st.write("No student data available yet.")
|
|
|
|
# Close the database connection
|
|
conn.close()
|
|
|
|
|
|
# convert address to latitude and longitude
|
|
def get_location(city):
|
|
geolocator = Nominatim(user_agent="student_location_app")
|
|
try:
|
|
location = geolocator.geocode(city, language="en")
|
|
st.write(location)
|
|
if location:
|
|
location_str = str(location).split(",")
|
|
state, country = location_str[2], location_str[-1]
|
|
return location.latitude, location.longitude, location.raw.get('name'), state, country
|
|
else:
|
|
return None, None, None, None, None
|
|
except Exception as e:
|
|
st.write(e)
|
|
return None, None, None, None, None
|
|
|
|
# save student location to database
|
|
def save_student(conn, city, state, country, lat, lon):
|
|
c = conn.cursor()
|
|
timestamp = datetime.now()
|
|
c.execute("INSERT INTO students (city, state, country, latitude, longitude, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
|
|
(city, state, country, lat, lon, timestamp))
|
|
conn.commit()
|
|
|
|
# get all student locations from database
|
|
def get_all_students(conn):
|
|
df = pd.read_sql_query("SELECT * from students", conn)
|
|
return df
|
|
|
|
# create map with student locations
|
|
def create_map(df):
|
|
# Create a map centered at the US
|
|
m = folium.Map(location=[41.2706, -97.1749], zoom_start=4)
|
|
|
|
# Group by city and count occurrences
|
|
city_counts = df.groupby(['city', 'state', 'latitude', 'longitude', 'country']).size().reset_index(name='count')
|
|
|
|
# Normalize marker sizes
|
|
max_count = city_counts['count'].max()
|
|
min_size, max_size = 5, 20 # min and max marker sizes
|
|
|
|
# Add markers for each city
|
|
for _, row in city_counts.iterrows():
|
|
# Create a custom icon
|
|
icon = CustomIcon(
|
|
icon_image='./images/BuffaloMarker.png', # Replace with the path to your icon file
|
|
icon_size=(30, 30), # Adjust the size as needed
|
|
icon_anchor=(15, 30), # Adjust anchor point if needed
|
|
popup_anchor=(0, -30) # Adjust popup anchor if needed
|
|
)
|
|
# Calculate marker size based on count
|
|
folium.Marker(
|
|
location=[row['latitude'], row['longitude']],
|
|
popup=f"{row['city']} <br> {row['count']}",
|
|
tooltip=f"{row['city']}, {row['country']}",
|
|
icon=icon
|
|
).add_to(m)
|
|
|
|
return m
|