Sowmya15 commited on
Commit
dc2b6ec
·
1 Parent(s): fa9d07e

Upload 6 files

Browse files
Files changed (6) hide show
  1. app.py +110 -0
  2. cluster_model.py +295 -0
  3. config.py +4 -0
  4. convex_hull.py +99 -0
  5. maplegend.py +95 -0
  6. 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