Skip to content

Instantly share code, notes, and snippets.

@vikas-git
Created August 13, 2019 10:30
Show Gist options
  • Save vikas-git/27b9731a4fc3ef940b7e1501f83e8dcb to your computer and use it in GitHub Desktop.
Save vikas-git/27b9731a4fc3ef940b7e1501f83e8dcb to your computer and use it in GitHub Desktop.
Implement folium library
# -*- coding: utf-8 -*-
"""
Created on Tuesday 23-July-2019
@author: Vikas shrivastava
For more info go to official documentation of folium https://python-visualization.github.io/folium/modules.html
"""
import random
import folium
import pandas as pd
import numpy as np
from collections import namedtuple
MIN_RADIUS = 2.0
MAX_RADIUS = 9.0
def convert_float(value, places=2):
return float(round(float(value), places))
class GeoVis:
def __init__(self, center_lat=23, center_lon=77, zoom=5, width='100%', height='100%'):
'''
This is constructor function for set center position of map
Parameters:
center_lat: default(23),
center_lon: default(77),
zoom: int default(5),
width: int default(100%),
height: int default(100%),
'''
self.m = folium.Map(
location=[center_lat, center_lon],
zoom_start=zoom,
width=width,
height=height
)
def make_html_for_tooltip(self, row, fields):
html = ''
for field in fields:
html += '<div><span><b>'+field.title()+'</b>: <label>'+str(row[field])+'</label></br></span></div>'
return html
def cal_radius(self, volume, max_volume):
try:
volume = float(volume)
max_volume = float(max_volume)
radius = float(volume/max_volume) * MAX_RADIUS
if radius < MIN_RADIUS:
radius = MIN_RADIUS
except Exception as e:
print(e)
radius = MIN_RADIUS
return float(round(float(radius), 2))
# def mark_points(self, df, lat_col = 'latitude', lon_col = 'longitude', color = 'green'):
# df.apply(lambda row: self.mark_point(row[lat_col], row[lon_col], row['display_name'], color=color), axis=1)
# return self
def mark_points(self, df, lat_col = 'latitude', lon_col = 'longitude', color = 'green', label_fields=[]):
if not label_fields:
label_fields = list(df.columns)
df.apply(lambda row: self.mark_point(row[lat_col], row[lon_col], self.make_html_for_tooltip(row, label_fields), radius= 4.5, color=color), axis=1)
return self
def mark_point(self, lat, lon, label='DC', radius=1.5, color='blue', weight=0, opacity=0.7, point_type='circle_marker'):
if pd.isna(lat) or pd.isna(lon):
return
if point_type == 'circle':
folium.Circle(
radius=radius,
location=[lat, lon],
popup=label,
color=False,
fill=True,
fill_color=color,
tooltip=label
).add_to(self.m)
elif point_type == 'circle_marker':
folium.CircleMarker(
location=[lat, lon],
radius=radius,
popup=label,
tooltip=label,
color=color,
line_color=False,
fill=True,
fill_color=color,
fill_opacity=0.3,
weight=weight
).add_to(self.m)
elif point_type == 'marker':
folium.Marker(
location=[lat, lon],
radius=radius,
popup=label,
tooltip=label,
color=color,
weight = weight,
opacity = opacity,
fill=True,
fill_color=color
).add_to(self.m)
return self
def connection_establish(self, points, color="#FF0000", weight=5, opacity=0.5):
'''
This function connect one lat, long to other lat, long
Parameters :
points : [(lat, long), (lat2, long2)]
color : hexadecimal code(example: #CD5C5C), default: #FF0000
'''
folium.PolyLine(
points,
color=color,
weight=weight,
opacity=opacity
).add_to(self.m)
return self
def draw_connection(self, lat1, lon1, lat2, lon2, color='blue', weight=1.5, opacity=0.5,
bin_col_params={}, bin_col_val=None, tooltip=[]):
if pd.isna(lat1) or pd.isna(lon1) or pd.isna(lat2) or pd.isna(lon2):
return
p1 = [lat1, lon1]
p2 = [lat2, lon2]
if bin_col_params:
bin_col_params['col_val'] = float(bin_col_val)
color = self.manage_color_for_connection(**bin_col_params)
folium.PolyLine(
[p1, p2],
color=color,
weight=weight,
opacity=opacity,
tooltip=tooltip
).add_to(self.m)
return self
def connections(self, df, lat_col1 = 'from_latitude', lon_col1 = 'from_longitude', lat_col2 = 'to_latitude',
lon_col2 = 'to_longitude', color = 'red', weight=2, opacity=0.3, bin_filters=[], bin_col=None,label_fields=[]):
col_max_val = 0
bin_col_params = {}
if bin_col:
des_col = dict(df[bin_col].describe())
col_max_val = des_col.get('max', '100')
bin_col_params = {
'col_max_val': col_max_val,
'bin_color_filter': bin_filters
}
if not label_fields:
label_fields = list(df.columns)
df.apply(
lambda row: self.draw_connection(
row[lat_col1], row[lon_col1],
row[lat_col2], row[lon_col2],
color = color,
weight=weight,
opacity=opacity,
bin_col_params=bin_col_params,
bin_col_val=row[bin_col],
tooltip=self.make_html_for_tooltip(row, label_fields)
),
axis = 1)
return self
def manage_color_for_connection(self, col_max_val, bin_color_filter, col_val):
color_code = '#a94442'
try:
x = convert_float(col_val*100/col_max_val)
for _filter in bin_color_filter:
if float(_filter.get('min_val', 0.0)) < x and float(_filter.get('max_val', 100.0)) >= x:
color_code = _filter.get('color_code')
break
except Exception as e:
print(e)
return color_code
def get_bearing(self, p1, p2):
'''
Returns compass bearing from p1 to p2
Parameters
p1 : namedtuple with lat lon
p2 : namedtuple with lat lon
Return
compass bearing of type float
'''
long_diff = np.radians(p2.lon - p1.lon)
lat1 = np.radians(p1.lat)
lat2 = np.radians(p2.lat)
x = np.sin(long_diff) * np.cos(lat2)
y = (np.cos(lat1) * np.sin(lat2) - (np.sin(lat1) * np.cos(lat2) * np.cos(long_diff)))
bearing = np.degrees(np.arctan2(x, y))
# adjusting for compass bearing
if bearing < 0:
return bearing + 360
return bearing
def get_arrows(self, locations, color='blue', size=3, n_arrows=3):
'''
Get a list of correctly placed and rotated
arrows/markers to be plotted
Parameters
locations : list of lists of lat lons that represent the
start and end of the line.
eg [[41.1132, -96.1993],[41.3810, -95.8021]]
arrow_color : default is 'blue'
size : default is 6
n_arrows : number of arrows to create. default is 3
Return
list of arrows/markers
'''
Point = namedtuple('Point', field_names=['lat', 'lon'])
# creating point from our Point named tuple
p1 = Point(locations[0][0], locations[0][1])
p2 = Point(locations[1][0], locations[1][1])
# getting the rotation needed for our marker.
# Subtracting 90 to account for the marker's orientation
# of due East(get_bearing returns North)
# rotation = self.get_bearing(p1, p2) - 90
# get an evenly space list of lats and lons for our arrows
# note that I'm discarding the first and last for aesthetics
# as I'm using markers to denote the start and end
arrow_lats = np.linspace(p1.lat, p2.lat, n_arrows + 2)[1:n_arrows+1]
arrow_lons = np.linspace(p1.lon, p2.lon, n_arrows + 2)[1:n_arrows+1]
arrows = []
#creating each "arrow" and appending them to our arrows list
for points in zip(arrow_lats, arrow_lons):
arrows.append(folium.RegularPolygonMarker(location=points,
fill_color=color, number_of_sides=1,
radius=size, rotation=0, weight=1))
return arrows
def manage_arrows(self, lat1, lon1, lat2, lon2):
if pd.isna(lat1) or pd.isna(lon1) or pd.isna(lat2) or pd.isna(lon2):
return
p1 = [lat1, lon1]
p2 = [lat2, lon2]
arrows = self.get_arrows([p1,p2])
for arrow in arrows:
arrow.add_to(self.m)
return self
def connection_flow_arrow(self, df, lat_col1 = 'from_latitude', lon_col1 = 'from_longitude', lat_col2 = 'to_latitude',
lon_col2 = 'to_longitude'):
df.apply(
lambda row: self.manage_arrows(
row[lat_col1], row[lon_col1],
row[lat_col2], row[lon_col2]
),
axis = 1)
return self
def manage_arrows_without_df(self, points):
for i in range(len(points)-1):
arrows = self.get_arrows([points[i], points[i+1]])
for arrow in arrows:
arrow.add_to(self.m)
return self
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment