Spaces:
Sleeping
Sleeping
import streamlit as st | |
import geopandas as gpd | |
import pydeck as pdk | |
import pandas as pd | |
from branca import colormap as cm | |
import pathlib | |
import os | |
import requests | |
import json | |
from shapely.geometry import shape | |
import matplotlib.pyplot as plt | |
import matplotlib.colors as mcolors | |
import io | |
import base64 | |
import numpy as np | |
st.set_page_config(layout="wide") | |
# Data source | |
prefectures = { | |
"北海道": "Hokkaido", "青森県": "Aomori", "岩手県": "Iwate", "宮城県": "Miyagi", "秋田県": "Akita", | |
"山形県": "Yamagata", "福島県": "Fukushima", "茨城県": "Ibaraki", "栃木県": "Tochigi", "群馬県": "Gunma", | |
"埼玉県": "Saitama", "千葉県": "Chiba", "東京都": "Tokyo", "神奈川県": "Kanagawa", "新潟県": "Niigata", | |
"富山県": "Toyama", "石川県": "Ishikawa", "福井県": "Fukui", "山梨県": "Yamanashi", "長野県": "Nagano", | |
"岐阜県": "Gifu", "静岡県": "Shizuoka", "愛知県": "Aichi", "三重県": "Mie", "滋賀県": "Shiga", | |
"京都府": "Kyoto", "大阪府": "Osaka", "兵庫県": "Hyogo", "奈良県": "Nara", "和歌山県": "Wakayama", | |
"鳥取県": "Tottori", "島根県": "Shimane", "岡山県": "Okayama", "広島県": "Hiroshima", "山口県": "Yamaguchi", | |
"徳島県": "Tokushima", "香川県": "Kagawa", "愛媛県": "Ehime", "高知県": "Kochi", "福岡県": "Fukuoka", | |
"佐賀県": "Saga", "長崎県": "Nagasaki", "熊本県": "Kumamoto", "大分県": "Oita", "宮崎県": "Miyazaki", | |
"鹿児島県": "Kagoshima", "沖縄県": "Okinawa" | |
} | |
data_links = {pref_jp: f"https://raw.githubusercontent.com/kunifujiwara/data/master/frac_veg/FRAC_VEG_{pref_en}.geojson" for pref_jp, pref_en in prefectures.items()} | |
def get_geom_data(prefecture): | |
response = requests.get(data_links[prefecture]) | |
if response.status_code == 200: | |
geojson_data = json.loads(response.content) | |
gdf = gpd.GeoDataFrame.from_features(geojson_data['features']) | |
return gdf | |
else: | |
st.error(f"Failed to fetch data for {prefecture}. Status code: {response.status_code}") | |
return None | |
def create_discontinuous_colormap(n_colors): | |
cmap = plt.get_cmap('Greens') | |
colors = [cmap(i / (n_colors - 1)) for i in range(n_colors)] | |
return mcolors.ListedColormap(colors) | |
def create_colormap_legend(vmin, vmax, cmap, n_colors): | |
fig, ax = plt.subplots(figsize=(6, 0.8)) | |
fig.subplots_adjust(bottom=0.5) | |
bounds = np.linspace(vmin, vmax, n_colors + 1) | |
norm = mcolors.BoundaryNorm(bounds, cmap.N) | |
cbar = fig.colorbar(plt.cm.ScalarMappable(norm=norm, cmap=cmap), cax=ax, orientation='horizontal', | |
spacing='proportional', boundaries=bounds, format='%.0f') | |
cbar.set_ticks(bounds) | |
# ax.set_title("緑被率 %", fontsize=10, pad=10) | |
buf = io.BytesIO() | |
plt.savefig(buf, format='png', dpi=300, bbox_inches='tight') | |
plt.close(fig) | |
return base64.b64encode(buf.getvalue()).decode() | |
def calculate_zoom_level(bbox): | |
lon_range = bbox[2] - bbox[0] | |
lat_range = bbox[3] - bbox[1] | |
max_range = max(lon_range, lat_range) | |
zoom = int(np.log2(360 / max_range)) - 1 | |
return min(max(1, zoom), 20) # Clamp zoom between 1 and 20 | |
def app(): | |
st.title("日本全国緑被率マップ (町丁目別)") | |
prefecture = st.selectbox("都道府県", list(prefectures.keys())) | |
gdf = get_geom_data(prefecture) | |
if gdf is None: | |
st.error("Failed to load data. Please try again later.") | |
return | |
# Convert FRAC_VEG to percentage | |
gdf['FRAC_VEG_PERCENT'] = gdf['FRAC_VEG'] * 100 | |
# City filter | |
cities = sorted(gdf['CITY_NAME'].unique().tolist()) | |
selected_cities = st.multiselect("市区町村", cities, default=[]) | |
# Filter GeoDataFrame based on selected cities | |
if selected_cities: | |
gdf_filtered = gdf[gdf['CITY_NAME'].isin(selected_cities)] | |
else: | |
gdf_filtered = gdf | |
selected_attribute = "FRAC_VEG_PERCENT" | |
# Custom CSS to create a box around the color scale controls | |
st.markdown(""" | |
<style> | |
.color-scale-box { | |
border: 2px solid #4CAF50; | |
border-radius: 10px; | |
padding: 10px; | |
margin-bottom: 10px; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Color scale controls in an expander | |
with st.expander("カラースケール設定", expanded=True): | |
# st.markdown('<div class="color-scale-box">', unsafe_allow_html=True) | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
n_colors = st.slider("分割数", min_value=2, max_value=20, value=5) | |
with col2: | |
alpha = st.slider("透過率", min_value=0.0, max_value=1.0, value=0.8, step=0.1) | |
with col3: | |
vmin = st.number_input("最小値 (%)", value=0.0, step=1.0) | |
with col4: | |
vmax = st.number_input("最大値 (%)", value=100.0, step=1.0) | |
st.markdown('</div>', unsafe_allow_html=True) | |
# Other controls | |
col5, col6 = st.columns(2) | |
with col5: | |
show_3d = st.checkbox("3Dビュー", value=False) | |
with col6: | |
if show_3d: | |
elev_scale = st.slider("スケール", min_value=1, max_value=100, value=1, step=10) | |
else: | |
elev_scale = 1 | |
# Create discontinuous color map | |
cmap = create_discontinuous_colormap(n_colors) | |
bounds = np.linspace(vmin, vmax, n_colors + 1) | |
norm = mcolors.BoundaryNorm(bounds, cmap.N) | |
def get_color(value): | |
rgba = cmap(norm(value)) | |
return [int(rgba[0]*255), int(rgba[1]*255), int(rgba[2]*255), int(alpha*255)] | |
gdf_filtered['color'] = gdf_filtered[selected_attribute].apply(get_color) | |
layer = pdk.Layer( | |
"GeoJsonLayer", | |
gdf_filtered.__geo_interface__, | |
pickable=True, | |
opacity=alpha, | |
stroked=True, | |
filled=True, | |
extruded=show_3d, | |
wireframe=True, | |
get_elevation=f"properties.{selected_attribute}" if show_3d else None, | |
elevation_scale=elev_scale if show_3d else 1, | |
get_fill_color="properties.color", | |
get_line_color=[0, 0, 0], | |
get_line_width=2, | |
line_width_min_pixels=1, | |
) | |
# Calculate bounding box and zoom level | |
bbox = gdf_filtered.total_bounds | |
zoom = calculate_zoom_level(bbox) | |
view_state = pdk.ViewState( | |
latitude=(bbox[1] + bbox[3]) / 2, | |
longitude=(bbox[0] + bbox[2]) / 2, | |
zoom=zoom, | |
pitch=45 if show_3d else 0, | |
) | |
r = pdk.Deck( | |
layers=[layer], | |
initial_view_state=view_state, | |
map_style="mapbox://styles/mapbox/light-v9", | |
tooltip={ | |
"html": "<b>{CITY_NAME} {S_NAME}</b><br/>緑被率: {FRAC_VEG_PERCENT}%", | |
"style": { | |
"backgroundColor": "steelblue", | |
"color": "white" | |
} | |
} | |
) | |
st.pydeck_chart(r) | |
# Create and display color scale legend | |
legend_img = create_colormap_legend(vmin, vmax, cmap, n_colors) | |
# Create three columns, with the middle one being 30% width | |
left_spacer, center_col, right_spacer = st.columns([1, 1, 1]) | |
# Display the legend in the middle column | |
with right_spacer: | |
st.image(f"data:image/png;base64,{legend_img}", use_column_width=True) | |
st.markdown(""" | |
<h3 style='text-align: center; font-weight: bold; font-size: 16px;'> 緑被率 %</h3> | |
""", unsafe_allow_html=True) | |
if st.checkbox("Show raw data"): | |
st.write(gdf_filtered[['CITY_NAME', 'S_NAME', selected_attribute]]) | |
st.markdown( | |
""" | |
**データソース** | |
・Vegetation cover fraction in each town block across Japan https://zenodo.org/records/5553516 | |
・平成27年国勢調査小地域境界データ https://www.e-stat.go.jp/gis/statmap-search?page=1&type=2&aggregateUnitForBoundary=A&toukeiCode=00200521&toukeiYear=2015&serveyId=A002005212015&coordsys=1&format=shape&datum=2000 | |
""" | |
) | |
st.markdown(" ") | |
st.markdown( | |
""" | |
**引用方法** | |
・マップの利用のみの場合: | |
Kiyono Tomoki, Fujiwara Kunihiko, & Tsurumi Ryuta. (2021). Vegetation cover fraction in each town block across Japan (1.0.1) [Data set]. Zenodo. https://doi.org/10.5281/zenodo.5553516 | |
・上記以外の場合(この取り組みそのものや,アルゴリズム等への言及): | |
清野友規, 藤原邦彦, 鶴見隆太. Google Earth Engineを用いた町丁目別緑被率オープンデータ(全国版)の作成と評価, 日本建築学会技術報告集, 2022, 28 巻, 68 号, p. 521-526, https://doi.org/10.3130/aijt.28.521 | |
""" | |
) | |
app() |