IllustratedMap / app.py
naohiro701's picture
Update app.py
6bae1b6 verified
raw
history blame
7.89 kB
import streamlit as st
import matplotlib.pyplot as plt
import osmnx as ox
import geopandas as gpd
import pandas as pd
import io
from PIL import Image
import numpy as np
def meters_to_degrees(meters, lat):
# 緯度1度あたりの距離は常に約111.32km
lat_deg = meters / 111320
# 経度1度あたりの距離は緯度によって変わる
lon_deg = meters / (111320 * np.cos(np.radians(lat)))
return lat_deg, lon_deg
def create_artistic_map(
lat, lon, distance, dpi, width, height, bg_color, greenery_color, water_color,
building_colors, major_road_color, medium_road_color, minor_road_color,
display_greenery, display_water, display_buildings,
display_major_roads, display_medium_roads, display_minor_roads
):
# 中心点の設定
point = (lat, lon)
# 距離をもとに緯度経度の幅と高さを計算
lat_deg, lon_deg = meters_to_degrees(distance, lat)
# 描画設定
fig, ax = plt.subplots(figsize=(width, height))
# 背景色を設定
fig.patch.set_facecolor(bg_color)
ax.set_facecolor(bg_color)
# 緑地の描画
if display_greenery:
# 緑地データ取得
greenery_list = []
greenery_tags = [
{'leisure': 'park'},
{'leisure': 'garden'},
{'landuse': 'grass'},
{'landuse': 'forest'},
{'landuse': 'meadow'},
{'natural': 'wood'},
{'natural': 'scrub'},
{'natural': 'grassland'},
{'natural': 'heath'}
]
for tags in greenery_tags:
greenery_part = ox.features_from_point(point, tags=tags, dist=distance)
if not greenery_part.empty:
greenery_list.append(greenery_part)
if greenery_list:
greenery = pd.concat(greenery_list)
greenery.plot(ax=ax, facecolor=greenery_color, edgecolor='none')
# 水域の描画
if display_water:
# 水域データ取得
water_list = []
water_tags = [
{'natural': 'water'},
{'waterway': 'riverbank'},
{'landuse': 'reservoir'},
{'landuse': 'basin'},
{'natural': 'wetland'}
]
for tags in water_tags:
water_part = ox.features_from_point(point, tags=tags, dist=distance)
if not water_part.empty:
water_list.append(water_part)
if water_list:
water = pd.concat(water_list)
water.plot(ax=ax, facecolor=water_color, edgecolor='none')
# 建物の描画
if display_buildings:
# 建物データ取得
buildings = ox.features_from_point(point, tags={'building': True}, dist=distance)
if not buildings.empty:
building_types = buildings['building'].unique()
for i, building_type in enumerate(building_types):
building_subset = buildings[buildings['building'] == building_type]
building_color = building_colors[i % len(building_colors)]
building_subset.plot(ax=ax, facecolor=building_color, edgecolor='none')
# 道路の描画
if display_major_roads or display_medium_roads or display_minor_roads:
# OSMデータ取得
G = ox.graph_from_point(point, dist=distance, network_type='all')
nodes, edges = ox.graph_to_gdfs(G)
if not edges.empty and 'highway' in edges.columns:
# 主要道路の描画
if display_major_roads:
major_roads = edges[edges['highway'].isin(['motorway', 'trunk', 'primary'])]
if not major_roads.empty:
major_roads.plot(ax=ax, linewidth=2, edgecolor=major_road_color)
# 中程度の道路の描画
if display_medium_roads:
medium_roads = edges[edges['highway'].isin(['secondary', 'tertiary'])]
if not medium_roads.empty:
medium_roads.plot(ax=ax, linewidth=1.5, edgecolor=medium_road_color)
# 小規模な道路の描画
if display_minor_roads:
minor_roads = edges[~edges['highway'].isin(['motorway', 'trunk', 'primary', 'secondary', 'tertiary'])]
if not minor_roads.empty:
minor_roads.plot(ax=ax, linewidth=1, edgecolor=minor_road_color)
# 軸をオフに設定
ax.axis('off')
# 表示範囲を設定
ax.set_xlim([lon - lon_deg, lon + lon_deg])
ax.set_ylim([lat - lat_deg, lat + lat_deg])
# バッファに保存して、PIL画像として返す
buf = io.BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0, dpi=dpi)
plt.close()
buf.seek(0)
return Image.open(buf)
# Streamlit UI
st.title("カラフルな地図が作成できます")
# デフォルト値を設定
default_lat = 35.533283
default_lon = 139.642000
default_distance = 1.0 # キロメートル
# ユーザー入力
lat = st.number_input("緯度を入力してください", value=default_lat, format="%.6f")
lon = st.number_input("経度を入力してください", value=default_lon, format="%.6f")
distance = st.number_input("距離を選択(km)", value=default_distance, format="%.2f")
# 画像設定
dpi = st.number_input("画像のDPI(解像度)を選択", value=72, format="%d")
width = st.slider("画像の幅(インチ)を選択", 5, 30, 20)
height = st.slider("画像の高さ(インチ)を選択", 5, 30, 20)
# 表示設定
st.header("表示設定")
# 背景色
bg_color = st.color_picker("背景色を選択", "#FAF3E0")
# 緑地の設定
st.subheader("緑地の設定")
display_greenery = st.checkbox("緑地を表示", value=True)
if display_greenery:
greenery_color = st.color_picker("緑地の色を選択", "#80C341")
else:
greenery_color = None
# 水域の設定
st.subheader("水域の設定")
display_water = st.checkbox("水域を表示", value=True)
if display_water:
water_color = st.color_picker("水域の色を選択", "#45A6FF")
else:
water_color = None
# 建物の設定
st.subheader("建物の設定")
display_buildings = st.checkbox("建物を表示", value=True)
if display_buildings:
default_building_colors = ['#FF6F61', '#FFAB73', '#FFA07A', '#FFD700', '#F08080']
building_colors = [
st.color_picker(f"建物の色 {i+1}", default_color)
for i, default_color in enumerate(default_building_colors)
]
else:
building_colors = []
# 道路の設定
st.subheader("道路の設定")
# 主要道路
display_major_roads = st.checkbox("主要道路を表示", value=True)
if display_major_roads:
major_road_color = st.color_picker("主要道路の色を選択", "#FF6347")
else:
major_road_color = None
# 中程度の道路
display_medium_roads = st.checkbox("中程度の道路を表示", value=True)
if display_medium_roads:
medium_road_color = st.color_picker("中程度の道路の色を選択", "#FF4500")
else:
medium_road_color = None
# 小規模な道路
display_minor_roads = st.checkbox("小規模な道路を表示", value=True)
if display_minor_roads:
minor_road_color = st.color_picker("小規模な道路の色を選択", "#D2691E")
else:
minor_road_color = None
# マップ生成ボタン
if st.button("マップを生成"):
with st.spinner('マップを生成中...'):
map_image = create_artistic_map(
lat, lon, distance*1000, dpi, width, height,
bg_color, greenery_color, water_color, building_colors,
major_road_color, medium_road_color, minor_road_color,
display_greenery, display_water, display_buildings,
display_major_roads, display_medium_roads, display_minor_roads
)
st.image(map_image, caption="生成されたアートマップ", use_column_width=True)