Skip to content
Snippets Groups Projects
Select Git revision
  • main
1 result

app.py

Blame
  • app.py 7.23 KiB
    import dash
    from dash import dcc, html, Input, Output
    def trigger_event(event):
        pass
    import plotly.express as px
    import plotly.graph_objects as go
    import pandas as pd
    import os
    import zipfile
    import numpy as np
    
    
    
    
    # Load and prepare data
    zip_path = "Glacier.zip"
    gtn_report_path = "GTN Report.xlsx"
    extract_dir = "glacier_data"
    
    # Extract glacier zip contents
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(extract_dir)
    
    # Load regional glacier CSV files and assign approximate coordinates for visualization
    region_coordinates = {
        '1_alaska': (64.2008, -149.4937),
        '2_western_canada_us': (52.9399, -106.4509),
        '3_arctic_canada_north': (75.0000, -100.0000),
        '4_arctic_canada_south': (65.0000, -100.0000),
        '5_greenland_periphery': (72.0000, -40.0000),
        '6_iceland': (64.9631, -19.0208),
        '7_svalbard': (78.0000, 16.0000),
        '8_scandinavia': (60.0000, 15.0000),
        '9_russian_arctic': (70.0000, 100.0000),
        '10_north_asia': (60.0000, 90.0000),
        '11_central_europe': (47.0000, 10.0000),
        '12_caucasus_middle_east': (42.0000, 45.0000),
        '13_central_asia': (43.0000, 75.0000),
        '14_south_asia_west': (35.0000, 70.0000),
        '15_south_asia_east': (27.0000, 85.0000),
        '16_low_latitudes': (0.0000, -60.0000),
        '17_southern_andes': (-40.0000, -70.0000),
        '18_new_zealand': (-41.2865, 174.7762),
        '19_antarctic_and_subantarctic': (-75.0000, 0.0000)
    }
    
    # Load glacier data into a combined DataFrame
    glacier_data = []
    region_files = [file for file in os.listdir(extract_dir) if file.endswith('.csv') and file != '0_global.csv']
    for file in region_files:
        region_name = file.replace('.csv', '')
        df_region = pd.read_csv(os.path.join(extract_dir, file))
        df_region['region'] = region_name
        df_region['latitude'], df_region['longitude'] = region_coordinates.get(region_name, (0, 0))
        glacier_data.append(df_region)
    
    df_glacier_regions = pd.concat(glacier_data, ignore_index=True)
    
    # Simulate river discharge based on glacier melt trends
    def simulate_discharge(glacier_mass_loss):
        max_discharge = 500  # Max discharge in cubic meters per second
        normalized_loss = (glacier_mass_loss - glacier_mass_loss.min()) / (glacier_mass_loss.max() - glacier_mass_loss.min())
        simulated_discharge = max_discharge * (1 - normalized_loss) + np.random.normal(0, 10, size=len(glacier_mass_loss))
        return np.clip(simulated_discharge, 50, max_discharge)
    
    # Enhance DataFrame with simulated discharge
    df_glacier_regions['simulated_discharge'] = simulate_discharge(df_glacier_regions['combined_gt'])
    
    # Initialize Dash app
    app = dash.Dash(__name__)
    app.title = "Glacier Melt Impact on Water Resources - Interactive Visualization"
    
    # Layout with 3D globe map and a side chart for selected region details
    app.layout = html.Div([
        html.H1("🌍 Glacier Melt and Water Resources Visualization",
                style={'textAlign': 'center', 'fontSize': '28px', 'marginBottom': '20px', 'color': '#2C3E50'}),
    
        html.Div([
            dcc.Graph(id='world-map-visualization', style={'height': '75vh', 'width': '65%', 'display': 'inline-block'}),
            dcc.Graph(id='region-detail-chart', style={'height': '75vh', 'width': '33%', 'display': 'inline-block', 'paddingLeft': '1%'})
        ], style={'display': 'flex', 'justifyContent': 'center'}),
    
        html.Div([
            html.Label("⏩ Animation Speed:", style={'fontWeight': 'bold', 'fontSize': '16px'}),
            dcc.Slider(
                id='animation-speed',
                min=50,
                max=1000,
                step=50,
                value=300,
                marks={50: 'Fast', 500: 'Medium', 1000: 'Slow'},
                tooltip={"placement": "bottom", "always_visible": True}
            )
        ], style={'width': '60%', 'margin': 'auto', 'paddingTop': '20px'})
    ])
    
    # Callback to update the 3D world map for smoother panning and zooming
    @app.callback(
        Output('world-map-visualization', 'figure'),
        [Input('animation-speed', 'value')]
    )
    def update_world_map(animation_speed):
        min_size, max_size = 4, 15
        normalized_sizes = (
            (df_glacier_regions['glacier_area'] - df_glacier_regions['glacier_area'].min()) /
            (df_glacier_regions['glacier_area'].max() - df_glacier_regions['glacier_area'].min())
        )
        bubble_sizes = normalized_sizes * (max_size - min_size) + min_size
    
        fig = go.Figure()
        fig.add_trace(go.Scattergeo(
            lat=df_glacier_regions['latitude'],
            lon=df_glacier_regions['longitude'],
            mode='markers',
            marker=dict(
                size=bubble_sizes,
                color=df_glacier_regions['simulated_discharge'],
                colorscale='Viridis',
                cmin=df_glacier_regions['simulated_discharge'].min(),
                cmax=df_glacier_regions['simulated_discharge'].max(),
                showscale=True,
                colorbar=dict(title="Discharge (m³/s)", thickness=15, len=0.5),
                line=dict(width=0.4, color='DarkSlateGrey')
            ),
            text=df_glacier_regions.apply(lambda row: (
                f"Region: {row['region']}<br>Year: {row['end_dates']}<br>Glacier Area: {row['glacier_area']:.2f} km²<Br>Discharge: {row['simulated_discharge']:.2f} m³/s"
            ), axis=1),
            hoverinfo='text',
            customdata=df_glacier_regions[['region']]
        ))
    
        fig.update_geos(
            projection_type="orthographic",
            showland=True,
            landcolor="rgb(230, 230, 230)",
            showocean=True,
            oceancolor="rgb(180, 210, 255)",
            showcountries=True,
            countrycolor="rgb(150, 150, 150)",
            center=dict(lat=20, lon=0),
            resolution=50,
        )
    
        fig.update_layout(
            title="🌍 3D Globe - Glacier Melt and Water Resources",
            margin=dict(l=0, r=0, t=40, b=0),
            dragmode='pan',
            geo=dict(
                projection_rotation=dict(lon=0, lat=0),
                projection_scale=0.9,
                lataxis=dict(range=[-90, 90]),
                lonaxis=dict(range=[-180, 180]),
            ),
            font=dict(family="Arial", size=13, color="#2C3E50"),
        )
    
        return fig
    
    # Callback to update the region detail chart when a bubble is clicked
    @app.callback(
        Output('region-detail-chart', 'figure'),
        [Input('world-map-visualization', 'clickData')]
    )
    def update_region_chart(clickData):
        if not clickData:
            return go.Figure(layout=dict(title="Select a region to see details", font=dict(size=14)))
    
        selected_region = clickData['points'][0]['customdata'][0]
        region_data = df_glacier_regions[df_glacier_regions['region'] == selected_region].sort_values(by='end_dates')
    
        fig = go.Figure()
        fig.add_trace(go.Scatter(
            x=region_data['end_dates'],
            y=region_data['combined_gt'],
            mode='lines+markers',
            name='Glacier Mass Change (Gt)',
            line=dict(color='blue')
        ))
        fig.add_trace(go.Scatter(
            x=region_data['end_dates'],
            y=region_data['simulated_discharge'],
            mode='lines+markers',
            name='Simulated River Discharge (m³/s)',
            line=dict(color='green')
        ))
    
        fig.update_layout(
            title=f"Glacier Mass vs Water Discharge - {selected_region.replace('_', ' ').title()}",
            xaxis_title="Year",
            yaxis_title="Value",
            legend=dict(x=0.02, y=0.98),
            margin=dict(l=30, r=20, t=40, b=40),
            font=dict(family="Arial", size=12)
        )
    
        return fig
    
    if __name__ == '__main__':
        app.run_server(debug=True, port=8060)