|
|
import folium |
|
|
import json |
|
|
import random |
|
|
|
|
|
|
|
|
import folium |
|
|
import json |
|
|
import os |
|
|
import random |
|
|
|
|
|
def generate_distinct_colors(n): |
|
|
"""Generate n distinct bright colors for each district""" |
|
|
|
|
|
colors = [ |
|
|
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', |
|
|
'#F7DC6F', '#BB8FCE', '#85C1E2', '#F8B739', '#52B788', |
|
|
'#E63946', '#06FFA5', '#FFD93D', '#6BCF7F', '#C77DFF', |
|
|
'#FF9F1C', '#2EC4B6', '#E71D36', '#FF499E', '#00B4D8', |
|
|
'#90E0EF', '#F72585', '#7209B7', '#3A0CA3', '#F4A261', |
|
|
'#2A9D8F', '#E76F51', '#264653', '#E9C46A', '#F77F00', |
|
|
'#D62828', '#023047', '#8338EC', '#3DDC84', '#FF006E', |
|
|
'#FFBE0B', '#FB5607', '#8AC926' |
|
|
] |
|
|
|
|
|
|
|
|
random.shuffle(colors) |
|
|
|
|
|
return colors[:n] |
|
|
|
|
|
def create_map(): |
|
|
"""Create a Folium map with distinct colors for each kabupaten/kota in Jawa Timur. |
|
|
|
|
|
Returns: |
|
|
str: HTML string for embedding the Folium map (safe to render). |
|
|
""" |
|
|
|
|
|
geojson_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'geojson', 'jatim_kabkota.geojson') |
|
|
|
|
|
with open(geojson_path, encoding='utf-8') as f: |
|
|
geojson_data = json.load(f) |
|
|
|
|
|
|
|
|
m = folium.Map( |
|
|
location=[-7.5, 112.5], |
|
|
zoom_start=8, |
|
|
tiles='CartoDB positron', |
|
|
prefer_canvas=True, |
|
|
zoom_control=True, |
|
|
attributionControl=False |
|
|
) |
|
|
|
|
|
|
|
|
num_features = len(geojson_data.get('features', [])) |
|
|
colors = generate_distinct_colors(num_features) |
|
|
|
|
|
|
|
|
color_map = {} |
|
|
city_list = [] |
|
|
for idx, feature in enumerate(geojson_data.get('features', [])): |
|
|
feature_name = feature.get('properties', {}).get('name', f'Feature_{idx}') |
|
|
feature_type = feature.get('properties', {}).get('type', 'Kabupaten') |
|
|
color_map[feature_name] = colors[idx] |
|
|
city_list.append({ |
|
|
'name': feature_name, |
|
|
'type': feature_type, |
|
|
'color': colors[idx] |
|
|
}) |
|
|
|
|
|
|
|
|
def style_function(feature): |
|
|
feature_name = feature['properties'].get('name', 'Unknown') |
|
|
return { |
|
|
'fillColor': color_map.get(feature_name, '#CCCCCC'), |
|
|
'color': '#333333', |
|
|
'weight': 2, |
|
|
'fillOpacity': 1.0, |
|
|
'opacity': 1, |
|
|
'dashArray': None, |
|
|
'lineJoin': 'miter', |
|
|
'lineCap': 'butt' |
|
|
} |
|
|
|
|
|
|
|
|
def highlight_function(feature): |
|
|
return { |
|
|
'fillColor': '#FFFF00', |
|
|
'color': '#FF0000', |
|
|
'weight': 3, |
|
|
'fillOpacity': 0.9, |
|
|
} |
|
|
|
|
|
|
|
|
folium.GeoJson( |
|
|
geojson_data, |
|
|
name='Jawa Timur', |
|
|
style_function=style_function, |
|
|
highlight_function=highlight_function, |
|
|
tooltip=folium.GeoJsonTooltip( |
|
|
fields=['name'], |
|
|
aliases=['Wilayah:'], |
|
|
localize=True, |
|
|
sticky=False, |
|
|
labels=True, |
|
|
style=""" |
|
|
background-color: white; |
|
|
border: 2px solid black; |
|
|
border-radius: 5px; |
|
|
font-family: Arial, sans-serif; |
|
|
font-size: 12px; |
|
|
padding: 8px; |
|
|
box-shadow: 3px 3px 5px rgba(0,0,0,0.5); |
|
|
""", |
|
|
) |
|
|
).add_to(m) |
|
|
|
|
|
|
|
|
for feature in geojson_data.get('features', []): |
|
|
props = feature.get('properties', {}) |
|
|
name = props.get('name', 'Unknown') |
|
|
center_lat = props.get('centroid_lat') |
|
|
center_lon = props.get('centroid_lon') |
|
|
|
|
|
if center_lat and center_lon: |
|
|
|
|
|
folium.Marker( |
|
|
location=[center_lat, center_lon], |
|
|
icon=folium.DivIcon(html=f''' |
|
|
<div style=" |
|
|
font-family: Arial, sans-serif; |
|
|
font-size: 10px; |
|
|
color: #FFFFFF; |
|
|
font-weight: bold; |
|
|
text-shadow: 1px 1px 3px rgba(0,0,0,0.9), |
|
|
-1px -1px 3px rgba(0,0,0,0.9), |
|
|
1px -1px 3px rgba(0,0,0,0.9), |
|
|
-1px 1px 3px rgba(0,0,0,0.9); |
|
|
text-align: center; |
|
|
white-space: nowrap; |
|
|
">{name}</div> |
|
|
''') |
|
|
).add_to(m) |
|
|
|
|
|
|
|
|
custom_css = ''' |
|
|
<style> |
|
|
.leaflet-container { |
|
|
background-color: #F5F5F5 !important; /* Light gray background */ |
|
|
} |
|
|
/* STRONG 3D shadow effect - multiple layers for depth */ |
|
|
.leaflet-interactive { |
|
|
filter: |
|
|
drop-shadow(3px 3px 2px rgba(0,0,0,0.3)) |
|
|
drop-shadow(6px 6px 4px rgba(0,0,0,0.25)) |
|
|
drop-shadow(9px 9px 8px rgba(0,0,0,0.2)) |
|
|
drop-shadow(12px 12px 12px rgba(0,0,0,0.15)); |
|
|
} |
|
|
/* Clean polygon edges */ |
|
|
path.leaflet-interactive { |
|
|
stroke-linejoin: miter !important; |
|
|
stroke-linecap: butt !important; |
|
|
} |
|
|
</style> |
|
|
''' |
|
|
m.get_root().html.add_child(folium.Element(custom_css)) |
|
|
|
|
|
|
|
|
legend_html = ''' |
|
|
<div style="position: fixed; |
|
|
bottom: 20px; left: 20px; width: 280px; max-height: 520px; |
|
|
background-color: white; z-index:9999; font-size:11px; |
|
|
border:2px solid #333; border-radius: 8px; |
|
|
overflow-y: auto; |
|
|
box-shadow: 5px 5px 15px rgba(0,0,0,0.5); |
|
|
font-family: Arial, sans-serif; |
|
|
"> |
|
|
<div style="background-color: #1976D2; color: white; padding: 12px; |
|
|
font-weight: bold; text-align: center; font-size: 13px; |
|
|
border-radius: 6px 6px 0 0;"> |
|
|
📍 38 Kabupaten/Kota Jawa Timur |
|
|
</div> |
|
|
<div style="padding: 10px; max-height: 450px; overflow-y: auto;"> |
|
|
''' |
|
|
|
|
|
|
|
|
sorted_cities = sorted(city_list, key=lambda x: x['name']) |
|
|
for idx, city in enumerate(sorted_cities, 1): |
|
|
legend_html += f''' |
|
|
<div style="margin: 3px 0; display: flex; align-items: center;"> |
|
|
<div style="width: 20px; height: 20px; background-color: {city['color']}; |
|
|
border: 1.5px solid #333; margin-right: 8px; flex-shrink: 0; |
|
|
border-radius: 3px; |
|
|
box-shadow: 2px 2px 4px rgba(0,0,0,0.3);"></div> |
|
|
<span style="font-size: 10px; color: #333;"> |
|
|
<b>{idx}.</b> {city['name']} ({city['type']}) |
|
|
</span> |
|
|
</div> |
|
|
''' |
|
|
|
|
|
legend_html += ''' |
|
|
</div> |
|
|
</div> |
|
|
''' |
|
|
|
|
|
m.get_root().html.add_child(folium.Element(legend_html)) |
|
|
|
|
|
|
|
|
title_html = ''' |
|
|
<div style="position: fixed; |
|
|
top: 10px; left: 50%; transform: translateX(-50%); |
|
|
background-color: white; z-index:9999; |
|
|
padding: 10px 30px; |
|
|
border: 2px solid black; |
|
|
border-radius: 5px; |
|
|
font-family: Arial, sans-serif; |
|
|
font-size: 16px; |
|
|
font-weight: bold; |
|
|
box-shadow: 3px 3px 10px rgba(0,0,0,0.5); |
|
|
"> |
|
|
Peta Jawa Timur - Kabupaten/Kota |
|
|
</div> |
|
|
''' |
|
|
m.get_root().html.add_child(folium.Element(title_html)) |
|
|
|
|
|
|
|
|
return m._repr_html_() |