File size: 3,307 Bytes
923e8be
 
 
 
29d8459
f01decb
 
923e8be
f01decb
2da3667
 
 
 
 
 
f01decb
2da3667
 
 
 
f01decb
 
2da3667
f01decb
2da3667
f01decb
 
2da3667
923e8be
 
 
 
2da3667
de31e8b
923e8be
 
2da3667
f01decb
7acc783
923e8be
29d8459
7acc783
de31e8b
 
7acc783
2738fe4
de31e8b
 
7acc783
 
 
923e8be
de31e8b
29d8459
 
2da3667
29d8459
 
f01decb
29d8459
 
 
 
2da3667
29d8459
 
 
 
 
 
 
2da3667
29d8459
 
 
e5f5311
 
29d8459
e5f5311
29d8459
7acc783
923e8be
de31e8b
 
2da3667
 
de31e8b
 
2da3667
 
de31e8b
 
 
 
 
2da3667
de31e8b
 
923e8be
 
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
import gradio as gr
import rasterio
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objs as go
from gradio.exceptions import Error
from rasterio.errors import RasterioIOError

def process_dem(dem_file):
    # dem_file can be a tempfile-like object, or a str path (depending on gr.File type)
    if isinstance(dem_file, str):
        path = dem_file
    else:
        # Gradio hands a TemporaryFile object; use its .name
        path = getattr(dem_file, "name", None)

    if not path:
        raise Error("No file received. Please upload a GeoTIFF (.tif/.tiff).")

    # Robust validation by CONTENT (not extension)
    try:
        with rasterio.open(path) as src:
            # Accept common GeoTIFF drivers
            if src.driver not in ("GTiff", "COG"):
                raise Error(f"Unsupported raster driver: {src.driver}. Please upload a GeoTIFF.")
            dem = src.read(1).astype(float)
    except RasterioIOError as e:
        raise Error(f"Could not read the file as a GeoTIFF. Details: {e}")

    nrows, ncols = dem.shape
    Z = dem

    # --- SLOPE ---
    dy, dx = np.gradient(Z)
    slope = np.sqrt(dx**2 + dy**2)

    # --- RISK MASK (top 5% steepest) ---
    threshold = np.percentile(slope, 95)
    risk_mask = slope > threshold

    # --- 2D RISK MAP ---
    fig2d, ax = plt.subplots(figsize=(8, 6))
    c = ax.imshow(slope, cmap="hot", origin="upper")
    ax.contour(risk_mask, levels=[0.5], colors="blue", linewidths=0.8)
    plt.colorbar(c, ax=ax, label="Slope (steepness)")
    ax.set_title("Slope Risk Map (Hot = Steep, Blue = Risk zones)")
    ax.set_xlabel("Column Index (X)")
    ax.set_ylabel("Row Index (Y)")
    risk_map_path = "risk_map.png"
    plt.savefig(risk_map_path, dpi=150, bbox_inches="tight")
    plt.close(fig2d)

    # --- INTERACTIVE 3D DEM (Plotly) ---
    step = max(1, nrows // 200)
    fig3d = go.Figure()

    fig3d.add_trace(go.Surface(
        z=Z[::step, ::step],
        colorscale="Earth",
        showscale=True,
        opacity=0.9,
        contours=dict(z=dict(show=True, usecolormap=True, highlightcolor="black", project_z=True))
    ))

    fig3d.add_trace(go.Surface(
        z=np.where(risk_mask[::step, ::step], Z[::step, ::step], np.nan),
        surfacecolor=np.ones_like(Z[::step, ::step]),
        colorscale=[[0, "purple"], [1, "purple"]],
        showscale=False,
        opacity=0.6
    ))

    fig3d.update_layout(
        title="Interactive 3D DEM with Contours & Steep Slope Highlight",
        scene=dict(
            xaxis_title="X (grid cols)",
            yaxis_title="Y (grid rows)",
            zaxis_title="Elevation (m)",
            aspectmode="data"
        )
    )

    return risk_map_path, fig3d


# --- GRADIO APP ---
demo = gr.Interface(
    fn=process_dem,
    # 🔑 Remove file_types to bypass extension-based rejection entirely
    inputs=gr.File(label="Upload DEM (GeoTIFF .tif/.tiff)"),
    outputs=[
        gr.Image(type="filepath", label="2D Slope Risk Map"),
        gr.Plot(label="Interactive 3D DEM (with Contours & Risk Zones)")
    ],
    title="3D DEM & Landslide Risk Visualizer",
    description="Upload a GeoTIFF DEM file to see a 2D slope risk map and an interactive 3D DEM with contours & steep slope zones highlighted."
)

if __name__ == "__main__":
    demo.launch()