File size: 6,214 Bytes
fa6051a
 
 
 
0026eb1
32fdadc
fa6051a
32fdadc
ec901bf
 
32fdadc
 
 
 
 
29ef836
dec2e4d
32fdadc
 
 
 
 
 
0026eb1
32fdadc
 
 
 
 
 
 
 
fa6051a
32fdadc
da912f3
 
 
 
32fdadc
 
eae2075
 
 
dec2e4d
fa6051a
da912f3
 
ec901bf
 
 
 
 
 
 
 
 
 
 
 
da912f3
 
 
 
32fdadc
fa6051a
 
32fdadc
 
fa6051a
32fdadc
 
fa6051a
32fdadc
 
f57fc72
32fdadc
f57fc72
0026eb1
f57fc72
fa6051a
32fdadc
 
6e3a184
 
fa6051a
32fdadc
da912f3
 
 
 
fa6051a
6e3a184
32fdadc
 
 
 
 
 
eae2075
32fdadc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0026eb1
 
 
32fdadc
fa6051a
 
 
6e3a184
32fdadc
ec901bf
32fdadc
6e3a184
32fdadc
 
 
 
ec901bf
32fdadc
ec901bf
32fdadc
 
 
 
 
fa6051a
 
 
ec901bf
fa6051a
 
 
 
 
 
 
 
 
 
 
 
 
 
6e3a184
 
 
 
 
32fdadc
 
 
fa6051a
6e3a184
 
 
32fdadc
 
6e3a184
 
0026eb1
6e3a184
 
 
ec901bf
da912f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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

st.set_page_config(layout="wide")

st.sidebar.info(
    """
    - Web App URL: <https://streamlit.gishub.org>
    - GitHub repository: <https://github.com/giswqs/streamlit-geospatial>
    """
)

st.sidebar.title("Contact")
st.sidebar.info(
    """
    Qiusheng Wu at [wetlands.io](https://wetlands.io) | [GitHub](https://github.com/giswqs) | [Twitter](https://twitter.com/giswqs) | [YouTube](https://www.youtube.com/c/QiushengWu) | [LinkedIn](https://www.linkedin.com/in/qiushengwu)
    """
)

STREAMLIT_STATIC_PATH = pathlib.Path(st.__path__[0]) / "static"
DOWNLOADS_PATH = STREAMLIT_STATIC_PATH / "downloads"
if not DOWNLOADS_PATH.is_dir():
    DOWNLOADS_PATH.mkdir()

# Data source
data_links = {
    "Tokyo": "https://raw.githubusercontent.com/kunifujiwara/data/master/frac_veg/FRAC_VEG_Tokyo.geojson",
    "Kanagawa": "https://raw.githubusercontent.com/kunifujiwara/data/master/frac_veg/FRAC_VEG_Kanagawa.geojson",
    "Chiba": "https://raw.githubusercontent.com/kunifujiwara/data/master/frac_veg/FRAC_VEG_Chiba.geojson",
    "Saitama": "https://raw.githubusercontent.com/kunifujiwara/data/master/frac_veg/FRAC_VEG_Saitama.geojson",
}

# Predefined list of color palettes
color_palettes = ['viridis', 'plasma', 'inferno', 'magma', 'cividis', 'Blues', 'Greens', 'Reds', 'Oranges', 'Purples']

@st.cache_data
def get_geom_data(prefecture):
    response = requests.get(data_links[prefecture])
    if response.status_code == 200:
        geojson_data = json.loads(response.content)
        features = geojson_data['features']
        
        data = []
        for feature in features:
            properties = feature['properties']
            geometry = shape(feature['geometry'])
            properties['geometry'] = geometry
            data.append(properties)
        
        gdf = gpd.GeoDataFrame(data)
        gdf.set_geometry('geometry', inplace=True)
        return gdf
    else:
        st.error(f"Failed to fetch data for {prefecture}. Status code: {response.status_code}")
        return None

def get_data_columns(gdf):
    return gdf.select_dtypes(include=[float, int]).columns.tolist()

def select_non_null(gdf, col_name):
    return gdf[~gdf[col_name].isna()]

def select_null(gdf, col_name):
    return gdf[gdf[col_name].isna()]

def app():
    st.title("Japan Vegetation Cover Fraction")
    st.markdown(
        """**Introduction:** This interactive dashboard is designed for visualizing Japan Fractional Vegetation Cover at town block levels. 
        The data sources include [Vegetation Cover Fraction](https://zenodo.org/records/5553516) from a research project (https://doi.org/10.3130/aijt.28.521), 
        and [Cartographic Boundary Files](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) from Census of Japan 2015.
        """
    )

    prefecture = st.selectbox("Prefecture", ["Tokyo", "Kanagawa", "Chiba", "Saitama"])
    
    gdf = get_geom_data(prefecture)

    if gdf is None:
        st.error("Failed to load data. Please try again later.")
        return

    attributes = get_data_columns(gdf)
    selected_attribute = st.selectbox("Select attribute to visualize", attributes)

    row2_col1, row2_col2, row2_col3, row2_col4, row2_col5, row2_col6 = st.columns(
        [0.6, 0.68, 0.7, 0.7, 1.5, 0.8]
    )

    with row2_col1:
        palette = st.selectbox("Color palette", color_palettes, index=color_palettes.index('Blues'))
    with row2_col2:
        n_colors = st.slider("Number of colors", min_value=2, max_value=20, value=8)
    with row2_col3:
        show_nodata = st.checkbox("Show nodata areas", value=True)
    with row2_col4:
        show_3d = st.checkbox("Show 3D view", value=False)
    with row2_col5:
        if show_3d:
            elev_scale = st.slider(
                "Elevation scale", min_value=1, max_value=1000000, value=1, step=10
            )
            with row2_col6:
                st.info("Press Ctrl and move the left mouse button.")
        else:
            elev_scale = 1

    color_scale = cm.LinearColormap(colors=palette, vmin=gdf[selected_attribute].min(), vmax=gdf[selected_attribute].max(), n=n_colors)
    gdf['color'] = gdf[selected_attribute].apply(lambda x: color_scale(x))
    gdf['color'] = gdf['color'].apply(lambda x: [int(x[1:3], 16), int(x[3:5], 16), int(x[5:7], 16)])

    gdf_null = select_null(gdf, selected_attribute)
    gdf = select_non_null(gdf, selected_attribute)

    layer = pdk.Layer(
        "GeoJsonLayer",
        gdf.__geo_interface__,
        pickable=True,
        opacity=0.8,
        stroked=True,
        filled=True,
        extruded=show_3d,
        wireframe=True,
        get_elevation=f"properties.{selected_attribute}" if show_3d else None,
        elevation_scale=elev_scale,
        get_fill_color="properties.color",
        get_line_color=[0, 0, 0],
        get_line_width=2,
        line_width_min_pixels=1,
    )

    if show_nodata:
        nodata_layer = pdk.Layer(
            "GeoJsonLayer",
            gdf_null.__geo_interface__,
            pickable=True,
            opacity=0.2,
            stroked=True,
            filled=True,
            extruded=False,
            get_fill_color=[200, 200, 200],
            get_line_color=[0, 0, 0],
            get_line_width=2,
            line_width_min_pixels=1,
        )
        layers = [layer, nodata_layer]
    else:
        layers = [layer]

    view_state = pdk.ViewState(
        latitude=gdf.geometry.centroid.y.mean(),
        longitude=gdf.geometry.centroid.x.mean(),
        zoom=9,
        pitch=45 if show_3d else 0,
    )

    r = pdk.Deck(
        layers=layers,
        initial_view_state=view_state,
        map_style="mapbox://styles/mapbox/light-v9",
        tooltip={"text": "{NAME}\n{" + selected_attribute + "}"}
    )

    st.pydeck_chart(r)

    st.write(color_scale)

    if st.checkbox("Show raw data"):
        st.write(gdf[[selected_attribute, 'NAME']])

app()