import gradio as gr from gradio_folium import Folium import folium import pandas as pd import numpy as np from geopy.distance import geodesic def calculate_distances(coords): distances = [] num_coords = len(coords) shortest_distances = [] for i in range(num_coords): shortest_distance = float('inf') closest_pair = None for j in range(num_coords): if i != j: distance = geodesic(coords[i], coords[j]).kilometers distances.append((distance, coords[i], coords[j])) if distance < shortest_distance: shortest_distance = distance closest_pair = (coords[i], coords[j]) if closest_pair: shortest_distances.append((shortest_distance, closest_pair[0], closest_pair[1])) return distances, shortest_distances def plot_on_map(coords_list, shortest_distance, longest_shortest_distance, avg_distance): all_coords = [coord for coords in coords_list for coord in coords] avg_lat = np.mean([coord[0] for coord in all_coords]) avg_lon = np.mean([coord[1] for coord in all_coords]) m = folium.Map(location=[avg_lat, avg_lon], zoom_start=10) colors = ['blue', 'red', 'green', 'purple', 'orange', 'darkblue', 'lightgray', 'darkgreen', 'cadetblue', 'pink'] for i, coords in enumerate(coords_list): group_marker_color = colors[i % len(colors)] for coord in coords: store_name = f"Store: {coord}" if len(coords) == 1: store_name = "Only 1 store" folium.Marker(location=coord, popup=store_name, icon=folium.Icon(color=group_marker_color)).add_to(m) if shortest_distance: folium.PolyLine(locations=[shortest_distance[1], shortest_distance[2]], color='green', weight=5, tooltip=f'Shortest Distance: {shortest_distance[0]:.2f} km').add_to(m) if longest_shortest_distance: folium.PolyLine(locations=[longest_shortest_distance[1], longest_shortest_distance[2]], color='red', weight=5, tooltip=f'Longest Among the Shortest Distances : {longest_shortest_distance[0]:.2f} km').add_to(m) return m def generate_map_with_stats(postcode_groups_input): df = pd.read_csv("speedmart99.csv") df['postcode'] = df['postcode'].astype(str).str.zfill(5) # Ensure postcodes are 5 digits long postcode_groups = postcode_groups_input.split(',') coords_list = [] for postcode_group_input in postcode_groups: key_length = len(postcode_group_input) # Filter rows where the postcode starts with the user input (considering the key length) group = df[df['postcode'].str[:key_length] == postcode_group_input] if not group.empty: coords = group['coords'].apply(lambda x: tuple(map(float, x.strip('()').split(',')))).tolist() coords_list.append(coords) # Combine all coordinates into a single list all_coords = [coord for coords in coords_list for coord in coords] if all_coords: distances, shortest_distances = calculate_distances(all_coords) if shortest_distances: shortest_distance = min(shortest_distances, key=lambda x: x[0]) longest_shortest_distance = max(shortest_distances, key=lambda x: x[0]) avg_distance = np.mean([dist[0] for dist in shortest_distances]) map_object = plot_on_map([all_coords], shortest_distance, longest_shortest_distance, avg_distance) statistics = [] for i, postcode_group in enumerate(postcode_groups): statistics.append(f"Postcode Group {i+1}: {postcode_group}") statistics.append(f"Shortest Distance (green): {shortest_distance[0]:.2f} km") statistics.append(f"Longest Among the Shortest Distances (red): {longest_shortest_distance[0]:.2f} km") statistics.append(f"Average of the Shortest Distances: {avg_distance:.2f} km") return map_object, "\n".join(statistics) else: return None, "No data found for the specified postcode groups." iface = gr.Interface( fn=generate_map_with_stats, inputs="text", outputs=[Folium(), "text"], title="99 Speedmart Stores Analyzer", description="This app allows you to input postcode areas and visualize the locations of 99 Speedmart stores within those areas.\n \nYou can analyze the longest, shortest and average distances between stores. \nEnter the first few digits of the postcode (up to 5 digits) and you may combine multiple postcode areas by separating them by commas.\n\n\nYou may try: \n\n 811,813 for Tebrau(811xx) and Kempas(813xx) \n\n 4 for Klang(4xxxx) \n\n 83 for Batu Pahat(83xxx)", article="\n\n\n*Store location data was last updated in February 2024.", ) iface.launch(share=True)