Upload 6 files
Browse files- app.py +110 -0
- cluster_model.py +295 -0
- config.py +4 -0
- convex_hull.py +99 -0
- maplegend.py +95 -0
- requirements.txt +9 -0
app.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from cluster_model import *
|
| 4 |
+
from streamlit_folium import folium_static
|
| 5 |
+
|
| 6 |
+
#reducing heading space
|
| 7 |
+
|
| 8 |
+
#SET PAGE WIDE
|
| 9 |
+
st.set_page_config(page_title='Identifying Commercial Centre Using Machine Learning',layout="centered")
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
st.markdown("""
|
| 13 |
+
<style>
|
| 14 |
+
.css-18e3th9 {
|
| 15 |
+
padding-top: 0rem;
|
| 16 |
+
padding-bottom: 10rem;
|
| 17 |
+
padding-left: 5rem;
|
| 18 |
+
padding-right: 5rem;
|
| 19 |
+
}
|
| 20 |
+
.css-1d391kg {
|
| 21 |
+
padding-top: 3.5rem;
|
| 22 |
+
padding-right: 1rem;
|
| 23 |
+
padding-bottom: 3.5rem;
|
| 24 |
+
padding-left: 1rem;
|
| 25 |
+
}
|
| 26 |
+
</style>
|
| 27 |
+
""", unsafe_allow_html=True)
|
| 28 |
+
|
| 29 |
+
st.sidebar.title("About")
|
| 30 |
+
st.sidebar.info(
|
| 31 |
+
"""
|
| 32 |
+
[GitHub repository](https://github.com/Sowmyad15/Identifying-Commercial-Centers-Using-Machine-Learning)
|
| 33 |
+
"""
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
st.sidebar.title("Contact")
|
| 37 |
+
st.sidebar.info(
|
| 38 |
+
"""
|
| 39 |
+
Sowmya D
|
| 40 |
+
[GitHub](https://github.com/Sowmyad15) | [LinkedIn](https://www.linkedin.com/in/sowmya-d-4b01711b2/)
|
| 41 |
+
"""
|
| 42 |
+
)
|
| 43 |
+
#Title of the page with CSS
|
| 44 |
+
|
| 45 |
+
st.markdown(""" <style> .font {
|
| 46 |
+
font-size:40px ; font-family: 'Verdana'; color: #000000;}
|
| 47 |
+
</style> """, unsafe_allow_html=True)
|
| 48 |
+
st.markdown('<p class="font">Identifying Commercial Centers</p>', unsafe_allow_html=True)
|
| 49 |
+
|
| 50 |
+
with st.expander("About this project"):
|
| 51 |
+
st.info("""Even though several global data are available regarding geolocations, demography of the planet, they are of not in supervised structure from which insights cannot be drawn. A Commercial Centre contains a high concentration of business, civic and cultural activities, also known as downtown. It is important to get to know the commercial city centres if you want to start any business as it also helps in identifying customer needs and in developing your business too.
|
| 52 |
+
To identify commercial centre of any city, clustering of Point of Interest(POI) of the city data with the correct amenities of interest is needed.
|
| 53 |
+
This web app provides the Commercial centre of the city using Machine Learning.
|
| 54 |
+
""")
|
| 55 |
+
|
| 56 |
+
city = st.text_input('Enter City Name:',help="City name is case sensitive, Kindly provide the exact name")
|
| 57 |
+
|
| 58 |
+
if city:
|
| 59 |
+
with st.spinner("Fetching City Data"):
|
| 60 |
+
try:
|
| 61 |
+
df=fetch_city_data(city)
|
| 62 |
+
|
| 63 |
+
st.header("Point of Interest in "+city)
|
| 64 |
+
st.markdown("The following data represents the Point of Interest Data of "+city)
|
| 65 |
+
st.dataframe(df,width=1000,height=500,use_container_width=True)
|
| 66 |
+
x=cluster_models(df)
|
| 67 |
+
with st.expander("How it works?"):
|
| 68 |
+
st.success("""A city from user is taken, whose data is fetched from Open Street Map (OSM),
|
| 69 |
+
after pre-processing the data, outliers are removed using Density-Based Spatial Clustering of Applications with Noise (DBSCAN) and
|
| 70 |
+
clusters are plotted on map using K-Means.Along with identifying the commercial centre, it also forms clusters of top 5 amenities in the city too.""")
|
| 71 |
+
|
| 72 |
+
st.header("Commercial Centers in "+city)
|
| 73 |
+
folium_static(mapplot(x[0],x[1],x[2]),height=500)
|
| 74 |
+
|
| 75 |
+
dx=amenity_df(df)
|
| 76 |
+
|
| 77 |
+
top5name=list(dx.iloc[:,0])
|
| 78 |
+
|
| 79 |
+
barplt=barplot(dx)
|
| 80 |
+
|
| 81 |
+
tab1, tab2,tab3,tab4,tab5,tab6= st.tabs(["📈 Chart",top5name[0],top5name[1],top5name[2],top5name[3],top5name[4]])
|
| 82 |
+
|
| 83 |
+
with tab1:
|
| 84 |
+
st.subheader("Top Amenities")
|
| 85 |
+
st.plotly_chart(barplt)
|
| 86 |
+
|
| 87 |
+
with tab2:
|
| 88 |
+
st.header(top5name[0])
|
| 89 |
+
folium_static(top5(dx,0),width=725,height=500)
|
| 90 |
+
|
| 91 |
+
with tab3:
|
| 92 |
+
st.header(top5name[1])
|
| 93 |
+
folium_static(top5(dx,1),width=725,height=500)
|
| 94 |
+
|
| 95 |
+
with tab4:
|
| 96 |
+
st.header(top5name[2])
|
| 97 |
+
folium_static(top5(dx,2),width=725,height=500)
|
| 98 |
+
|
| 99 |
+
with tab5:
|
| 100 |
+
st.header(top5name[3])
|
| 101 |
+
folium_static(top5(dx,3),width=725,height=500)
|
| 102 |
+
|
| 103 |
+
with tab6:
|
| 104 |
+
st.header(top5name[4])
|
| 105 |
+
folium_static(top5(dx,4),width=725,height=500)
|
| 106 |
+
|
| 107 |
+
except KeyError:
|
| 108 |
+
st.error("Oops Couldnt find city details")
|
| 109 |
+
|
| 110 |
+
|
cluster_model.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
import overpy
|
| 4 |
+
from sklearn.cluster import KMeans,DBSCAN
|
| 5 |
+
from convex_hull import *
|
| 6 |
+
import config
|
| 7 |
+
from maplegend import *
|
| 8 |
+
|
| 9 |
+
import plotly.express as px
|
| 10 |
+
import folium
|
| 11 |
+
from folium import plugins
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
api = overpy.Overpass()
|
| 15 |
+
|
| 16 |
+
#Get City details
|
| 17 |
+
def fetch_city_data(city_name):
|
| 18 |
+
|
| 19 |
+
res = api.query(f"""[out:json];
|
| 20 |
+
area[name='{city_name}'][boundary=administrative]->.searchArea;
|
| 21 |
+
(node["amenity"](area.searchArea);
|
| 22 |
+
way["amenity"](area.searchArea);
|
| 23 |
+
relation["amenity"](area.searchArea);
|
| 24 |
+
);
|
| 25 |
+
(._;
|
| 26 |
+
>;
|
| 27 |
+
);
|
| 28 |
+
out;
|
| 29 |
+
""")
|
| 30 |
+
return reduce(res)
|
| 31 |
+
|
| 32 |
+
#Remove unnecessary amenity
|
| 33 |
+
def reduce(res):
|
| 34 |
+
tags = []
|
| 35 |
+
for i in res.nodes:
|
| 36 |
+
if len(i.tags) != 0:
|
| 37 |
+
i_tags = i.tags
|
| 38 |
+
i_tags['node_id'] = i.id
|
| 39 |
+
i_tags['lat'] = float(i.lat)
|
| 40 |
+
i_tags['lon'] = float(i.lon)
|
| 41 |
+
tags.append(i.tags)
|
| 42 |
+
df = pd.DataFrame(tags)
|
| 43 |
+
df = df[['node_id', 'lat', 'lon', 'name', 'amenity']]
|
| 44 |
+
df = df.dropna(subset=['node_id', 'lat', 'amenity'])
|
| 45 |
+
remove_amenity = [
|
| 46 |
+
'arts_centre',
|
| 47 |
+
"Ayurvedic Hospital",
|
| 48 |
+
"baby_hatch",
|
| 49 |
+
"bench",
|
| 50 |
+
"bicycle_parking",
|
| 51 |
+
"bicycle_rental",
|
| 52 |
+
"bicycle_repair_station",
|
| 53 |
+
"bureau_de_change",
|
| 54 |
+
"car_rental",
|
| 55 |
+
"car_wash",
|
| 56 |
+
"charging_station",
|
| 57 |
+
"fountain",
|
| 58 |
+
"grave_yard",
|
| 59 |
+
"House",
|
| 60 |
+
"language_school",
|
| 61 |
+
"loading_dock"
|
| 62 |
+
"meditation_centre",
|
| 63 |
+
"motorcycle_parking",
|
| 64 |
+
"orphanage",
|
| 65 |
+
"parking entrance"
|
| 66 |
+
"payment_terminal",
|
| 67 |
+
"photo_booth",
|
| 68 |
+
"post_depot",
|
| 69 |
+
"recycling",
|
| 70 |
+
"shelter",
|
| 71 |
+
"social_centre",
|
| 72 |
+
"social_facility",
|
| 73 |
+
"telephone",
|
| 74 |
+
"training",
|
| 75 |
+
"tuition",
|
| 76 |
+
"vending_machine",
|
| 77 |
+
"veterinary",
|
| 78 |
+
"waste_basket",
|
| 79 |
+
"waste_disposal",
|
| 80 |
+
"waste_transfer_station",
|
| 81 |
+
"water_point",
|
| 82 |
+
"weighbridge",]
|
| 83 |
+
for val in remove_amenity:
|
| 84 |
+
df = df[df.amenity != val]
|
| 85 |
+
df.name = np.where(df.name.isnull(), df.amenity, df.name)
|
| 86 |
+
return df
|
| 87 |
+
|
| 88 |
+
def locplot(df):
|
| 89 |
+
coords1=df[['lat','lon']].to_numpy()
|
| 90 |
+
map_osm = folium.Map(location=coords1[0])
|
| 91 |
+
# create a polygon with the coordinates
|
| 92 |
+
for cords in coords1:
|
| 93 |
+
folium.CircleMarker(location=[cords[0], cords[1]],radius=2,weight=2).add_to(map_osm)
|
| 94 |
+
return map_osm
|
| 95 |
+
|
| 96 |
+
def cluster_models(data):
|
| 97 |
+
db_return = dbscan(data)
|
| 98 |
+
df =db_return[0]
|
| 99 |
+
n_cluster=db_return[1]
|
| 100 |
+
knn_return=Kmeans(df,n_cluster)
|
| 101 |
+
return cluster_coords(knn_return)
|
| 102 |
+
|
| 103 |
+
def dbscan(data):
|
| 104 |
+
x=data.copy()
|
| 105 |
+
coords = x[['lat', 'lon']].to_numpy()
|
| 106 |
+
dbsc = (DBSCAN(eps=config.epsilon,min_samples=config.min_samples, algorithm='ball_tree', metric='haversine').fit(np.radians(coords)))
|
| 107 |
+
cluster_labels = dbsc.labels_
|
| 108 |
+
num_clusters = len(set(cluster_labels))
|
| 109 |
+
clusters = pd.Series([ coords[ cluster_labels == n ] for n in range (num_clusters) ])
|
| 110 |
+
core_samples = np.zeros_like(cluster_labels, dtype='bool')
|
| 111 |
+
core_samples[dbsc.core_sample_indices_] = True
|
| 112 |
+
s = pd.Series(core_samples, name='bools')
|
| 113 |
+
df=[x[s.values],num_clusters,clusters]
|
| 114 |
+
return df
|
| 115 |
+
|
| 116 |
+
def Kmeans(data, num_clusters):
|
| 117 |
+
x=data.copy()
|
| 118 |
+
coords1=x[['lat','lon']].to_numpy()
|
| 119 |
+
kmeans = KMeans(num_clusters, init = 'k-means++', random_state = config.random_state)
|
| 120 |
+
y_kmeans = kmeans.fit_predict(coords1)
|
| 121 |
+
km=[num_clusters,coords1,y_kmeans,data]
|
| 122 |
+
return km
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def cluster_coords(Kn):
|
| 126 |
+
num_clusters=Kn[0]
|
| 127 |
+
coords1=Kn[1]
|
| 128 |
+
y_kmeans=Kn[2]
|
| 129 |
+
df=Kn[3]
|
| 130 |
+
most_significant = []
|
| 131 |
+
least_significant = []
|
| 132 |
+
for i in range(num_clusters):
|
| 133 |
+
if len(coords1[y_kmeans == i]) > 5:
|
| 134 |
+
if len(coords1[y_kmeans == i]) > 45:
|
| 135 |
+
most_significant.append(apply_convex_hull(coords1[y_kmeans == i]))
|
| 136 |
+
else:
|
| 137 |
+
least_significant.append(apply_convex_hull(coords1[y_kmeans == i]))
|
| 138 |
+
|
| 139 |
+
return most_significant,least_significant,coords1
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def mapplot(most_significant,least_significant,coords1):
|
| 144 |
+
map_osm = folium.Map(location=coords1[0])
|
| 145 |
+
#Add Plugins
|
| 146 |
+
# add tiles to map, Create a tile layer to append on a Map
|
| 147 |
+
folium.raster_layers.TileLayer('Open Street Map').add_to(map_osm)
|
| 148 |
+
folium.raster_layers.TileLayer('Stamen Terrain').add_to(map_osm)
|
| 149 |
+
folium.raster_layers.TileLayer('Stamen Toner').add_to(map_osm)
|
| 150 |
+
folium.raster_layers.TileLayer('Stamen Watercolor').add_to(map_osm)
|
| 151 |
+
folium.raster_layers.TileLayer('CartoDB Positron').add_to(map_osm)
|
| 152 |
+
folium.raster_layers.TileLayer('CartoDB Dark_Matter').add_to(map_osm)
|
| 153 |
+
# add layer control to show different maps
|
| 154 |
+
folium.LayerControl().add_to(map_osm)
|
| 155 |
+
minimap = plugins.MiniMap(toggle_display=True,position='bottomleft')
|
| 156 |
+
# add minimap to map
|
| 157 |
+
map_osm.add_child(minimap)
|
| 158 |
+
# add full screen button to map
|
| 159 |
+
plugins.Fullscreen(position='topright').add_to(map_osm)
|
| 160 |
+
# create a polygon with the coordinates
|
| 161 |
+
for cords in coords1:
|
| 162 |
+
folium.CircleMarker(location=[cords[0], cords[1]],radius=1,color='blue').add_to(map_osm)
|
| 163 |
+
for i in range(len(least_significant)):
|
| 164 |
+
folium.Polygon(least_significant[i],
|
| 165 |
+
color="blue",
|
| 166 |
+
weight=2,
|
| 167 |
+
fill=True,
|
| 168 |
+
fill_color="yellow",
|
| 169 |
+
fill_opacity=0.4).add_to(map_osm)
|
| 170 |
+
|
| 171 |
+
for i in range(len(most_significant)):
|
| 172 |
+
folium.Polygon(most_significant[i],
|
| 173 |
+
color="black",
|
| 174 |
+
weight=2,
|
| 175 |
+
fill=True,
|
| 176 |
+
fill_color="red",
|
| 177 |
+
fill_opacity=0.4).add_to(map_osm)
|
| 178 |
+
|
| 179 |
+
#Legend
|
| 180 |
+
|
| 181 |
+
macro=temp()
|
| 182 |
+
|
| 183 |
+
map_osm.add_child(macro)
|
| 184 |
+
|
| 185 |
+
return map_osm
|
| 186 |
+
|
| 187 |
+
def amenity_df(df):
|
| 188 |
+
#Group the amenity
|
| 189 |
+
food_list = ['restaurant', 'fast_food', 'cafe', 'bar', 'ice_cream', 'fast_food','bar', 'food_court', 'club', 'drinking_water']
|
| 190 |
+
market_list = ['marketplace', 'internet_cafe']
|
| 191 |
+
bank_list = ['atm', 'bank', ]
|
| 192 |
+
toilets_list = ['toilets']
|
| 193 |
+
education_list = ['school', 'college', 'university']
|
| 194 |
+
hospital_list = ['pharmacy', 'hospital', 'clinic', 'dentist', 'nursing_home']
|
| 195 |
+
parking_list = ['parking']
|
| 196 |
+
entertainment_list = ['cinema', 'theatre', 'nightclub', 'cafe','coworking_space', 'studio', 'internet_cafe',
|
| 197 |
+
'swimming_pool', 'library','pub']
|
| 198 |
+
worship_list = ['place_of_worship']
|
| 199 |
+
fuel_list = ['fuel', 'fire_station']
|
| 200 |
+
others_list = ['post_box', 'community_centre', 'post_office', 'embassy', 'police', 'bus_station', 'public_building',
|
| 201 |
+
'taxi']
|
| 202 |
+
|
| 203 |
+
amenity_list = ['food_list', 'market_list', 'bank_list', 'toilets_list', 'education_list',
|
| 204 |
+
'hospital_list', 'parking_list', 'entertainment_list', 'worship_list', 'fuel_list', 'others_list']
|
| 205 |
+
food,market,bank,toilets,education,hospital,parking,entertainment,worship,fuel,others = [],[],[],[],[],[],[],[],[],[],[]
|
| 206 |
+
|
| 207 |
+
for value in df.values:
|
| 208 |
+
if value[4] in food_list: food.append([value[1], value[2]])
|
| 209 |
+
elif value[4] in market_list: market.append([value[1], value[2]])
|
| 210 |
+
elif value[4] in bank_list: bank.append([value[1], value[2]])
|
| 211 |
+
elif value[4] in toilets_list: toilets.append([value[1], value[2]])
|
| 212 |
+
elif value[4] in education_list: education.append([value[1], value[2]])
|
| 213 |
+
elif value[4] in hospital_list: hospital.append([value[1], value[2]])
|
| 214 |
+
elif value[4] in parking_list: parking.append([value[1], value[2]])
|
| 215 |
+
elif value[4] in entertainment_list: entertainment.append([value[1], value[2]])
|
| 216 |
+
elif value[4] in worship_list: worship.append([value[1], value[2]])
|
| 217 |
+
elif value[4] in fuel_list: fuel.append([value[1], value[2]])
|
| 218 |
+
elif value[4] in others_list: others.append([value[1], value[2]])
|
| 219 |
+
|
| 220 |
+
amenities_list = [food,market,bank,toilets,education,hospital,parking,entertainment,worship,fuel,others]
|
| 221 |
+
amenities_str = ['Food','Market','Bank','Toilets','Education','Hospital','Parking','Entertainment','Worship','Fuel','Others']
|
| 222 |
+
x=[]
|
| 223 |
+
|
| 224 |
+
for i,item in enumerate(amenities_list):
|
| 225 |
+
x.append(len(item))
|
| 226 |
+
|
| 227 |
+
dx=pd.DataFrame(list(zip(amenities_str,amenities_list,x)),columns=['Amenity','lat_lon','Count']).sort_values(by=['Count'],ascending=False)
|
| 228 |
+
|
| 229 |
+
return dx
|
| 230 |
+
|
| 231 |
+
def barplot(dx):
|
| 232 |
+
dx_plot=dx[['Amenity','Count']]
|
| 233 |
+
|
| 234 |
+
fig = px.bar(dx_plot, x='Amenity', y='Count',color='Count',width=725,height=500)
|
| 235 |
+
|
| 236 |
+
return fig
|
| 237 |
+
|
| 238 |
+
def top5(dx,ilocation):
|
| 239 |
+
|
| 240 |
+
dx=dx.head(5)
|
| 241 |
+
|
| 242 |
+
amenity_name = dx.iloc[ilocation,0]
|
| 243 |
+
amenity_array = dx.iloc[ilocation,1]
|
| 244 |
+
|
| 245 |
+
amenities_df = pd.DataFrame(amenity_array, columns = ['lat', 'lon'])
|
| 246 |
+
coords12=amenities_df[['lat','lon']].to_numpy()
|
| 247 |
+
|
| 248 |
+
# Fitting K-Means to the dataset
|
| 249 |
+
if len(amenity_array) < 60:
|
| 250 |
+
n_clusters = 5
|
| 251 |
+
else:
|
| 252 |
+
n_clusters = 20
|
| 253 |
+
|
| 254 |
+
kmeans = KMeans(n_clusters, init = 'k-means++', random_state = 42)
|
| 255 |
+
y_kmeans = kmeans.fit_predict(amenity_array)
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
polygon = []
|
| 259 |
+
amenity_array = np.array(amenity_array)
|
| 260 |
+
for i in range(n_clusters):
|
| 261 |
+
polygon.append(apply_convex_hull(amenity_array[y_kmeans == i]))
|
| 262 |
+
|
| 263 |
+
polygon = [i for i in polygon if i is not None]
|
| 264 |
+
|
| 265 |
+
map_osm = folium.Map(location=coords12[0])
|
| 266 |
+
#Add Plugins
|
| 267 |
+
# add tiles to map, Create a tile layer to append on a Map
|
| 268 |
+
folium.raster_layers.TileLayer('Open Street Map').add_to(map_osm)
|
| 269 |
+
folium.raster_layers.TileLayer('Stamen Terrain').add_to(map_osm)
|
| 270 |
+
folium.raster_layers.TileLayer('Stamen Toner').add_to(map_osm)
|
| 271 |
+
folium.raster_layers.TileLayer('Stamen Watercolor').add_to(map_osm)
|
| 272 |
+
folium.raster_layers.TileLayer('CartoDB Positron').add_to(map_osm)
|
| 273 |
+
folium.raster_layers.TileLayer('CartoDB Dark_Matter').add_to(map_osm)
|
| 274 |
+
# add layer control to show different maps
|
| 275 |
+
folium.LayerControl().add_to(map_osm)
|
| 276 |
+
minimap = plugins.MiniMap(toggle_display=True)
|
| 277 |
+
# add minimap to map
|
| 278 |
+
map_osm.add_child(minimap)
|
| 279 |
+
# add full screen button to map
|
| 280 |
+
plugins.Fullscreen(position='topright').add_to(map_osm)
|
| 281 |
+
# create a polygon with the coordinates
|
| 282 |
+
for cords in coords12:
|
| 283 |
+
folium.CircleMarker(location=[cords[0], cords[1]],radius=2,weight=1).add_to(map_osm)
|
| 284 |
+
|
| 285 |
+
for i in range(len(polygon)):
|
| 286 |
+
folium.Polygon(polygon[i],
|
| 287 |
+
color="blue",
|
| 288 |
+
weight=2,
|
| 289 |
+
fill=True,
|
| 290 |
+
fill_color="yellow",
|
| 291 |
+
fill_opacity=0.4).add_to(map_osm)
|
| 292 |
+
|
| 293 |
+
return map_osm
|
| 294 |
+
|
| 295 |
+
|
config.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
kms_per_radian = 6371.0088
|
| 2 |
+
epsilon = 0.5 / kms_per_radian
|
| 3 |
+
min_samples = 10
|
| 4 |
+
random_state = 42
|
convex_hull.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Convex Hull
|
| 2 |
+
# point class with x, y as point
|
| 3 |
+
class Point:
|
| 4 |
+
def __init__(self, x, y):
|
| 5 |
+
self.x = x
|
| 6 |
+
self.y = y
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def Left_index(points):
|
| 10 |
+
"""Finding the left most point"""
|
| 11 |
+
minn = 0
|
| 12 |
+
for i in range(1, len(points)):
|
| 13 |
+
if points[i].x < points[minn].x:
|
| 14 |
+
minn = i
|
| 15 |
+
elif points[i].x == points[minn].x:
|
| 16 |
+
if points[i].y > points[minn].y:
|
| 17 |
+
minn = i
|
| 18 |
+
return minn
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def orientation(p, q, r):
|
| 22 |
+
"""
|
| 23 |
+
To find orientation of ordered triplet (p, q, r).
|
| 24 |
+
The function returns following values
|
| 25 |
+
0 --> p, q and r are colinear
|
| 26 |
+
1 --> Clockwise
|
| 27 |
+
2 --> Counterclockwise
|
| 28 |
+
"""
|
| 29 |
+
val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
|
| 30 |
+
|
| 31 |
+
if val == 0:
|
| 32 |
+
return 0
|
| 33 |
+
elif val > 0:
|
| 34 |
+
return 1
|
| 35 |
+
else:
|
| 36 |
+
return 2
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def convexHull(points, n):
|
| 40 |
+
# There must be at least 3 points
|
| 41 |
+
if n < 3:
|
| 42 |
+
return
|
| 43 |
+
|
| 44 |
+
# Find the leftmost point
|
| 45 |
+
l = Left_index(points)
|
| 46 |
+
|
| 47 |
+
hull = []
|
| 48 |
+
|
| 49 |
+
"""
|
| 50 |
+
Start from leftmost point, keep moving counterclockwise
|
| 51 |
+
until reach the start point again. This loop runs O(h)
|
| 52 |
+
times where h is number of points in result or output.
|
| 53 |
+
"""
|
| 54 |
+
p = l
|
| 55 |
+
q = 0
|
| 56 |
+
while True:
|
| 57 |
+
# Add current point to result
|
| 58 |
+
hull.append(p)
|
| 59 |
+
|
| 60 |
+
"""
|
| 61 |
+
Search for a point 'q' such that orientation(p, x,
|
| 62 |
+
q) is counterclockwise for all points 'x'. The idea
|
| 63 |
+
is to keep track of last visited most counterclock-
|
| 64 |
+
wise point in q. If any point 'i' is more counterclock-
|
| 65 |
+
wise than q, then update q.
|
| 66 |
+
"""
|
| 67 |
+
q = (p + 1) % n
|
| 68 |
+
|
| 69 |
+
for i in range(n):
|
| 70 |
+
|
| 71 |
+
# If i is more counterclockwise than current q, then update q
|
| 72 |
+
if orientation(points[p], points[i], points[q]) == 2:
|
| 73 |
+
q = i
|
| 74 |
+
|
| 75 |
+
"""
|
| 76 |
+
Now q is the most counterclockwise with respect to p
|
| 77 |
+
Set p as q for next iteration, so that q is added to
|
| 78 |
+
result 'hull'
|
| 79 |
+
"""
|
| 80 |
+
p = q
|
| 81 |
+
|
| 82 |
+
# While we don't come to first point
|
| 83 |
+
if p == l:
|
| 84 |
+
break
|
| 85 |
+
|
| 86 |
+
# Print Result
|
| 87 |
+
result = []
|
| 88 |
+
for each in hull:
|
| 89 |
+
result.append([points[each].x, points[each].y])
|
| 90 |
+
return result
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def apply_convex_hull(coordinates):
|
| 94 |
+
points = []
|
| 95 |
+
for coordinate in coordinates:
|
| 96 |
+
x, y = coordinate[0], coordinate[1]
|
| 97 |
+
points.append(Point(x, y))
|
| 98 |
+
|
| 99 |
+
return convexHull(points, len(points))
|
maplegend.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from branca.element import Template, MacroElement
|
| 2 |
+
def temp():
|
| 3 |
+
#code copied from: https://nbviewer.org/gist/talbertc-usgs/18f8901fc98f109f2b71156cf3ac81cd
|
| 4 |
+
template = """
|
| 5 |
+
{% macro html(this, kwargs) %}
|
| 6 |
+
|
| 7 |
+
<!doctype html>
|
| 8 |
+
<html lang="en">
|
| 9 |
+
<head>
|
| 10 |
+
<meta charset="utf-8">
|
| 11 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 12 |
+
<title>jQuery UI Draggable - Default functionality</title>
|
| 13 |
+
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
| 14 |
+
|
| 15 |
+
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
| 16 |
+
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
| 17 |
+
|
| 18 |
+
<script>
|
| 19 |
+
$( function() {
|
| 20 |
+
$( "#maplegend" ).draggable({
|
| 21 |
+
start: function (event, ui) {
|
| 22 |
+
$(this).css({
|
| 23 |
+
right: "auto",
|
| 24 |
+
top: "auto",
|
| 25 |
+
bottom: "auto"
|
| 26 |
+
});
|
| 27 |
+
}
|
| 28 |
+
});
|
| 29 |
+
});
|
| 30 |
+
|
| 31 |
+
</script>
|
| 32 |
+
</head>
|
| 33 |
+
<body>
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
<div id='maplegend' class='maplegend'
|
| 37 |
+
style='position: absolute; z-index:9999; border:2px solid grey; background-color:rgba(255, 255, 255, 0.8);
|
| 38 |
+
border-radius:6px; padding: 10px; font-size:14px; right: 20px; bottom: 20px;'>
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
<div class='legend-scale'>
|
| 42 |
+
<ul class='legend-labels'>
|
| 43 |
+
<li><span style='background:red;opacity:0.7;'></span>Most Significant Cluster</li>
|
| 44 |
+
<li><span style='background:yellow;opacity:0.7;'></span>Least Significant Cluster</li>
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
</ul>
|
| 48 |
+
</div>
|
| 49 |
+
</div>
|
| 50 |
+
|
| 51 |
+
</body>
|
| 52 |
+
</html>
|
| 53 |
+
|
| 54 |
+
<style type='text/css'>
|
| 55 |
+
|
| 56 |
+
.maplegend .legend-scale ul {
|
| 57 |
+
margin: 0;
|
| 58 |
+
margin-bottom: 5px;
|
| 59 |
+
padding: 0;
|
| 60 |
+
float: left;
|
| 61 |
+
list-style: none;
|
| 62 |
+
}
|
| 63 |
+
.maplegend .legend-scale ul li {
|
| 64 |
+
font-size: 80%;
|
| 65 |
+
list-style: none;
|
| 66 |
+
margin-left: 0;
|
| 67 |
+
line-height: 18px;
|
| 68 |
+
margin-bottom: 2px;
|
| 69 |
+
}
|
| 70 |
+
.maplegend ul.legend-labels li span {
|
| 71 |
+
display: block;
|
| 72 |
+
float: left;
|
| 73 |
+
height: 16px;
|
| 74 |
+
width: 30px;
|
| 75 |
+
margin-right: 5px;
|
| 76 |
+
margin-left: 0;
|
| 77 |
+
border: 1px solid #999;
|
| 78 |
+
}
|
| 79 |
+
.maplegend .legend-source {
|
| 80 |
+
font-size: 80%;
|
| 81 |
+
color: #777;
|
| 82 |
+
clear: both;
|
| 83 |
+
}
|
| 84 |
+
.maplegend a {
|
| 85 |
+
color: #777;
|
| 86 |
+
}
|
| 87 |
+
</style>
|
| 88 |
+
{% endmacro %}"""
|
| 89 |
+
|
| 90 |
+
macro = MacroElement()
|
| 91 |
+
macro._template = Template(template)
|
| 92 |
+
|
| 93 |
+
#map_osm.get_root().add_child(macro)
|
| 94 |
+
|
| 95 |
+
return macro
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
branca==0.5.0
|
| 2 |
+
folium==0.12.1.post1
|
| 3 |
+
numpy==1.23.2
|
| 4 |
+
overpy==0.6
|
| 5 |
+
pandas==1.4.4
|
| 6 |
+
plotly==5.10.0
|
| 7 |
+
scikit_learn==1.1.2
|
| 8 |
+
streamlit==1.13.0
|
| 9 |
+
streamlit_folium==0.6.15
|