File size: 8,912 Bytes
9914376
73b9278
 
 
 
 
954111c
7eeceec
62dd0f3
 
 
7eeceec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73b9278
8f51ef1
 
 
 
 
73b9278
 
 
210106d
 
189935d
 
31f6634
 
 
 
 
 
 
 
 
 
189935d
 
 
31f6634
 
 
 
 
 
 
189935d
a633cb2
73b9278
b90f50d
 
189935d
45c1420
37f4e6a
7f9b2ce
37f4e6a
45c1420
b90f50d
7eeceec
b90f50d
 
 
 
62dd0f3
13c538e
 
 
 
 
 
 
b90f50d
62dd0f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2bb7e7
 
8f51ef1
 
 
 
 
 
 
 
 
b90f50d
 
 
 
 
 
 
 
 
7eeceec
b90f50d
 
 
 
73b9278
b90f50d
 
cc66d4e
b90f50d
566bbba
cc66d4e
 
 
 
 
566bbba
 
cc66d4e
566bbba
b90f50d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41103fd
73b9278
b90f50d
 
 
 
 
 
 
 
 
7480d60
73b9278
b90f50d
 
 
 
 
 
 
 
 
 
7be40d9
99b822c
b90f50d
 
 
7be40d9
 
b90f50d
7be40d9
 
 
 
 
 
 
 
b90f50d
 
 
 
 
 
 
7be40d9
b90f50d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2284563
566bbba
 
 
 
 
3375879
566bbba
761de2c
566bbba
 
 
 
 
 
 
 
 
 
 
 
 
4e55bc5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
import re
import json
import streamlit as st
import pandas as pd
import geopandas as gpd
import leafmap.foliumap as leafmap
from optree import tree_map
from shapely.ops import transform
import kml2geojson
from io import BytesIO, StringIO
import requests


def shape_3d_to_2d(shape):
    if shape.has_z:
        return transform(lambda x, y, z: (x, y), shape)
    else:
        return shape


def preprocess_gdf(gdf):
    gdf = gdf.to_crs(epsg=7761)  # epsg for Gujarat
    gdf["geometry"] = gdf["geometry"].apply(shape_3d_to_2d)
    gdf["geometry"] = gdf.buffer(0)  # Fixes some invalid geometries
    return gdf


def is_valid_polygon(geometry_gdf):
    geometry = geometry_gdf.geometry.item()
    return (geometry.type == "Polygon") and (not geometry.is_empty)


# wide streamlit display
st.set_page_config(layout="wide")

# Function


# Logo
# Logo
st.write(
    f"""
    <div style="display: flex; justify-content: space-between; align-items: center;">
        <img src="https://huggingface.co/spaces/SustainabilityLabIITGN/NDVI_PERG/resolve/main/Final_IITGN-Logo-symmetric-Color.png"  style="width: 10%; margin-right: auto;">
        <img src="https://huggingface.co/spaces/SustainabilityLabIITGN/NDVI_PERG/resolve/main/IFS.jpg" style="width: 10%; margin-left: auto;">
    </div>
    """,
    unsafe_allow_html=True,
)

# Title
# make title in center
# with cols[1]:
st.markdown(
    f"""
    <h1 style="text-align: center;">KML Viewer</h1>
    """,
    unsafe_allow_html=True,
)

file_url = st.query_params.get("file_url", None)

if not file_url:
    file_url = st.file_uploader("Upload KML/GeoJSON file", type=["geojson", "kml", "shp"])

if not file_url:
    st.warning(
        "Please provide a KML or GeoJSON URL as a query parameter, e.g., `https://sustainabilitylabiitgn-bhuvan.hf.space/?file_url=<your_file_url>` or upload a file."
    )
    st.stop()

if ("file_url" in st.session_state) and ("input_gdf" in st.session_state) and (st.session_state.file_url == file_url):
    # st.toast("Using cached data")
    input_gdf = st.session_state.input_gdf
else:
    st.session_state.file_url = file_url
    print(file_url, type(file_url))
    if isinstance(file_url, str):
        if file_url.startswith("https://drive.google.com/file/d/"):
            ID = file_url.replace("https://drive.google.com/file/d/", "").split("/")[0]
            file_url = f"https://drive.google.com/uc?id={ID}"
        elif file_url.startswith("https://drive.google.com/open?id="):
            ID = file_url.replace("https://drive.google.com/open?id=", "")
            file_url = f"https://drive.google.com/uc?id={ID}"

        response = requests.get(file_url)
        bytes_data = BytesIO(response.content)
        string_data = response.text
    else:
        bytes_data = BytesIO(file_url.getvalue())
        string_data = file_url.getvalue().decode("utf-8")

    if string_data.startswith("<?xml"):
        geojson = kml2geojson.convert(bytes_data)
        features = geojson[0]["features"]
        epsg = 4326
        input_gdf = gpd.GeoDataFrame.from_features(features, crs=f"EPSG:{epsg}")
    else:
        input_gdf = gpd.read_file(bytes_data)

    input_gdf = preprocess_gdf(input_gdf)
    if len(input_gdf) > 1:
        st.warning(f"Only the first polygon in the KML will be processed; all other geometries will be ignored.")

    for i in range(len(input_gdf)):
        geometry_gdf = input_gdf[input_gdf.index == i]
        if is_valid_polygon(geometry_gdf):
            break
    else:
        st.error(f"No polygon found inside KML. Please check the KML file.")
        st.stop()

    st.session_state.input_gdf = input_gdf
    # st.toast("Data loaded and cached")


def format_fn(x):
    return input_gdf.drop(columns=["geometry"]).loc[x].to_dict()


with st.expander("Advanced Controls", expanded=False):
    # input_geometry_idx = st.selectbox("Select the geometry", input_gdf.index, format_func=format_fn)
    map_type = st.radio(
        "",
        ["Esri Satellite Map", "Google Hybrid Map (displays place names)", "Google Satellite Map"],
        horizontal=True,
    )
    height = st.number_input("Map height (px)", 1, 10000, 600, 1)

# geometry_gdf = input_gdf[input_gdf.index == 0]


# def check_valid_geometry(geometry_gdf):
#     geometry = geometry_gdf.geometry.item()
#     if geometry.type != "Polygon":
#         st.error(f"Selected geometry is of type '{geometry.type}'. Please provide a 'Polygon' geometry.")
#         st.stop()


# check_valid_geometry(geometry_gdf)

m = leafmap.Map()

st.markdown(
    """
<style>
.stRadio [role=radiogroup]{
    align-items: center;
    justify-content: center;
}
</style>
""",
    unsafe_allow_html=True,
)

if map_type == "Google Hybrid Map (displays place names)":
    st.write(
        "<h4><div style='text-align: center;'>Google Hybrid (displays place names)</div></h4>",
        unsafe_allow_html=True,
    )
    m.add_basemap("HYBRID")
elif map_type == "Google Satellite Map":
    st.write("<h4><div style='text-align: center;'>Google Satellite</div></h4>", unsafe_allow_html=True)
    m.add_basemap("SATELLITE")
elif map_type == "Esri Satellite Map":
    st.write("<h4><div style='text-align: center;'>Esri - 2024/10/10</div></h4>", unsafe_allow_html=True)
    m.add_wms_layer(
        "https://wayback.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/WMTS/1.0.0/GoogleMapsCompatible/MapServer/tile/56450/{z}/{y}/{x}",
        layers="0",
    )
else:
    st.error("Invalid map type")
    st.stop()
m.add_gdf(
    geometry_gdf.to_crs(epsg=4326),
    layer_name="Geometry",
    zoom_to_layer=True,
    style_function=lambda x: {"color": "red", "fillOpacity": 0.0},
)

# Metrics
stats_df = pd.DataFrame(index=[0])
stats_df["Points"] = str(json.loads(geometry_gdf.to_crs(4326).to_json())["features"][0]["geometry"]["coordinates"])
stats_df["Centroid"] = geometry_gdf.centroid.to_crs(4326).item()
stats_df["Area (ha)"] = geometry_gdf.geometry.area.item() / 10000
stats_df["Perimeter (m)"] = geometry_gdf.geometry.length.item()
centroid_lon = stats_df["Centroid"].item().xy[0][0]
centroid_lat = stats_df["Centroid"].item().xy[1][0]

centroid_gdf = gpd.GeoDataFrame(geometry=[stats_df["Centroid"].item()], crs="EPSG:4326")
m.add_gdf(
    centroid_gdf,
    layer_name="Centroid",
    style_function=lambda x: {"color": "blue", "fillOpacity": 0.0},
    zoom_to_layer=False,
)
m.to_streamlit(height=height)
st.write("<h3><div style='text-align: center;'>Geometry Metrics</div></h3>", unsafe_allow_html=True)
#     st.markdown(
#         f"""| Metric | Value |
# | --- | --- |
# | Area (ha) | {stats_df['Area (ha)'].item():.2f} ha|
# | Perimeter (m) | {stats_df['Perimeter (m)'].item():.2f} m |"""
#     unsafe_allow_html=True)

centroid_url = f"http://maps.google.com/maps?q={centroid_lat},{centroid_lon}&layer=satellite"
st.markdown(
    f"""
<div style="display: flex; justify-content: center;">
    <table>
        <tr>
            <th>Metric</th>
            <th>Value</th>
        </tr>
<td>Centroid</td>
<td>
({centroid_lon:.5f}, {centroid_lat:.5f})
    <a href="{centroid_url}" target="_blank">
        <button>View on Google Maps</button>
    </a>
</td>
</tr>
            <td>Area (ha)</td>
            <td>{stats_df['Area (ha)'].item():.2f} ha</td>
        </tr>
        <tr>
            <td>Perimeter (m)</td>
            <td>{stats_df['Perimeter (m)'].item():.2f} m</td>
        </tr>
    </table>
</div>
""",
    unsafe_allow_html=True,
)
print(stats_df["Points"].item())
print(type(stats_df["Points"].item()))

csv = stats_df.T.to_csv(index=True)
st.download_button("Download Geometry Metrics", csv, f"{file_url}_metrics.csv", "text/csv", use_container_width=True)


if isinstance(file_url, str):
    st.markdown(
        f"""
        <div style="display: flex; justify-content: center;">
            <a href="https://sustainabilitylabiitgn-ndvi-perg.hf.space?file_url={file_url}" target="_blank">
                <button style="
                    background-color: #00EE00; /* Green background */
                    padding: 10px 20px;
                    font-size: 16px;
                    border: none;
                    border-radius: 5px;
                    cursor: pointer;
                ">
                    Click for NDVI Timeseries
                </button>
            </a>
        </div>
        """,
        unsafe_allow_html=True,
    )

# Add some space in the end
st.write(
    """
<style>
div.stButton > button {
    margin: 10px;
}
</style>
""",
    unsafe_allow_html=True,
)

# Add credits
st.write(
    """
    <div style="display: flex; justify-content: center; align-items: center;">
        <p style="text-align: left;">This tool is developed by <a href="https://sustainability-lab.github.io/">Sustainability Lab</a>, <a href="https://www.iitgn.ac.in/">IIT Gandhinagar</a> and supported by <a href="https://forests.gujarat.gov.in/">Gujarat Forest Department</a></p>""",
    unsafe_allow_html=True,
)