import json
import folium
import pandas as pd
from folium import Marker
from folium.plugins import MarkerCluster
from jinja2 import Template
regions, countries, region_tree = json.load(
open("resources/country_regions.json", encoding="utf-8")
)
country_centers = json.load(
open("resources/country_center_coordinates.json", encoding="utf-8")
)
country_mappings = json.load(open("resources/country_mappings.json", encoding="utf-8"))
WORLD_GEO_URL = "https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/world-countries.json"
ICON_CREATE_FUNCTIOM = """
function(cluster) {
var markers = cluster.getAllChildMarkers();
var sum = 0;
for (var i = 0; i < markers.length; i++) {
sum += markers[i].options.props.resources;
}
return L.divIcon({
html: '' + sum + '',
className: 'marker-cluster marker-cluster-small',
iconSize: new L.Point(20, 20)
});
}
"""
class MarkerWithProps(Marker):
_template = Template(
"""
{% macro script(this, kwargs) %}
var {{this.get_name()}} = L.marker(
[{{this.location[0]}}, {{this.location[1]}}],
{
icon: new L.Icon.Default(),
{%- if this.draggable %}
draggable: true,
autoPan: true,
{%- endif %}
{%- if this.props %}
props : {{ this.props }}
{%- endif %}
}
)
.addTo({{this._parent.get_name()}});
{% endmacro %}
"""
)
def __init__(
self, location, popup=None, tooltip=None, icon=None, draggable=False, props=None
):
super(MarkerWithProps, self).__init__(
location=location,
popup=popup,
tooltip=tooltip,
icon=icon,
draggable=draggable,
)
self.props = json.loads(json.dumps(props))
def get_region_center(region_name):
latitudes = []
longitudes = []
for name in region_tree[region_name]:
if name in region_tree:
region_latitudes, region_longitudes = get_region_center(name)
latitudes += region_latitudes
longitudes += region_longitudes
elif name in country_centers or name in country_mappings["to_center"]:
country_center = country_centers[
country_mappings["to_center"].get(name, name)
]
latitudes += [float(country_center["latitude"])]
longitudes += [float(country_center["longitude"])]
return latitudes, longitudes
def get_region_countries(region_name):
countries = []
for name in region_tree[region_name]:
if name in region_tree:
countries += get_region_countries(name)
else:
countries += [name]
return countries
def make_choro_map(resource_counts, marker_thres=0):
world_map = folium.Map(tiles="cartodbpositron", location=[0, 0], zoom_start=1.5)
marker_cluster = MarkerCluster(icon_create_function=ICON_CREATE_FUNCTIOM)
marker_cluster.add_to(world_map)
for name, count in resource_counts.items():
if name in country_centers or name in country_mappings["to_center"]:
country_center = country_centers[
country_mappings["to_center"].get(name, name)
]
MarkerWithProps(
location=[country_center["latitude"], country_center["longitude"]],
popup=f"{'Region' if name in region_tree else 'Country'} : {name}
\n Resources : {count}
",
props={"name": name, "resources": count},
).add_to(marker_cluster)
# put a pin at the center of the region
elif name in region_tree:
latitudes, longitudes = get_region_center(name)
if len(latitudes) > 0:
lat = sum(latitudes) / len(latitudes)
lon = sum(longitudes) / len(longitudes)
MarkerWithProps(
location=[lat, lon],
popup=f"{'Region' if name in region_tree else 'Country'} : {name}
\n Resources : {count}
",
props={"name": name, "resources": count},
).add_to(marker_cluster)
# for choropleth, add counts to all countries in a region
choropleth_counts = {}
for loc_name in list(resource_counts.keys()):
if loc_name in region_tree:
for country_name in get_region_countries(loc_name):
choropleth_counts[country_name] = (
choropleth_counts.get(country_name, 0) + resource_counts[loc_name]
)
else:
choropleth_counts[loc_name] = (
choropleth_counts.get(loc_name, 0) + resource_counts[loc_name]
)
df_resource_counts = pd.DataFrame(
[
(country_mappings["to_outline"].get(n, n), c)
for n, c in choropleth_counts.items()
],
columns=["Name", "Resources"],
)
folium.Choropleth(
geo_data=WORLD_GEO_URL,
name="resource map",
data=df_resource_counts,
columns=["Name", "Resources"],
key_on="feature.properties.name",
fill_color="PuRd",
nan_fill_color="white",
).add_to(world_map)
return world_map