Metadata-Version: 2.4
Name: dem-shadows
Version: 0.1.0
Summary: Generate terrain shadow rasters and animations from DEM GeoTIFFs
Author: Marco Pizzolato
License: GPLv3
Project-URL: Homepage, https://github.com/marcop11/dem-shadows
Project-URL: Source, https://github.com/marcop11/dem-shadows
Project-URL: Issues, https://github.com/marcop11/dem-shadows/issues
Project-URL: Streamlit, https://dem-shadows.streamlit.app/
Keywords: dem,dtm,terrain,shadow,solar,gis,geospatial
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Other Audience
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: astral>=3.0
Requires-Dist: rasterio>=1.3
Requires-Dist: numpy>=1.24
Requires-Dist: tqdm>=4.60
Requires-Dist: Pillow>=10.0
Requires-Dist: insolation>=0.1.9
Requires-Dist: pandas
Requires-Dist: tzfpy>=1.1.0
Requires-Dist: numba>=0.59
Requires-Dist: scipy>=1.10
Provides-Extra: demo
Requires-Dist: streamlit>=1.37; extra == "demo"
Dynamic: license-file

[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)
![Python Versions](https://img.shields.io/badge/python-3.12-brightgreen)
![Platform](https://img.shields.io/badge/platform-windows%20|%20linux%20|%20macos-lightgrey)
[![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://dem-shadows.streamlit.app/)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/marcop11/dem-shadows/blob/main/notebooks/try_it_yourself.ipynb)

![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/logo.svg)

# DEM-Shadows  
Generate high-resolution terrain shadow rasters and animations from DEM GeoTIFFs.

![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/zh_example_sh.png)
![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/zh_example_ortho.png)

---

## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Installation](#installation)
- [Project Structure](#project-structure)
- [Quick Start](#quick-start)
  - [1. Generate Shadows](#1-generate-shadows)
  - [2. Animate Shadows](#2-animate-shadows)
  - [3. Cumulative Shadow Map](#3-cumulative-shadow-map)
- [CLI Reference](#cli-reference)
  - [`dem-shadows-generate`](#dem-shadows-generate)
  - [`dem-shadows-animate`](#dem-shadows-animate)
  - [`dem-shadows-cumulate`](#dem-shadows-cumulate)
- [Examples](#examples)
- [Notebooks](#notebooks)
- [Testing](#testing)
- [Streamlit App](#streamlit-app)
- [Author](#author)
- [License](#license)

---

## Overview
`dem-shadows` is a lightweight Python toolchain for:

- Merging DEM tiles  
- Converting DEMs to metric projected CRS  
- Computing *per-timestamp* shadow rasters using the `insolation` package  
- Auto-detecting latitude, longitude, and timezone from DEM extent  
- Rendering animated GIFs of shadow progression  
- Rendering cumulative “shadow exposure maps"  

The goal is to provide **easy**, **reproducible**, and **open-source** shadow modelling for any DEM.

---

## Features

✔ Merge DEM tiles automatically  
✔ Auto lat/lon from DEM center  
✔ Auto timezone inference via `tzfpy`  
✔ Robust shadow modeling (`insolation` `sunvector + doshade`)  
✔ Zero-dependency GIF animation (Pillow only)  
✔ Transparent or white-background output modes  
✔ Cumulative exposure computation  
✔ Optional Streamlit GUI  
✔ Fully cross-platform (Windows / Linux / macOS)

---

## Installation

### Clone & install locally
```bash
git clone https://github.com/marcop11/dem-shadows.git
cd dem-shadows
pip install -e .
```

### Requirements
Automatically installed:
- rasterio
- numpy
- pandas
- Pillow
- tqdm
- astral
- insolation>=0.1.9
- tzfpy

Optional:
```bash
pip install streamlit
```

---

## Project Structure

```
dem-shadows/
│
├── app/
│   └── streamlit_app.py
│
├── examples/
│   └── dem.tif              # Example DEM for README & demos
│
├── img/
│   ├── app.png
│   ├── icon.svg
│   ├── logo.svg
│   ├── zh_example_sh.png
│   ├── zh_example_dtm.png
│   ├── zh_example_ortho.png
│   ├── zh_example_sh_cum.png
│   └── zh_example_animate.gif
│
├── notebooks/
│   └── try_it_yourself.ipynb
│
├── src/
│   └── dem_shadows/
│       ├── shadows.py       # DEM → shadows
│       ├── animate.py       # GIF generation
│       ├── analysis.py      # Cumulative shadows
│       ├── preprocess.py
│       ├── utils.py
│       ├── schedule.py
│       ├── config.py
│       └── __init__.py
│
├── tests/
│   └── test_basic.py
│
├── README.md
├── pyproject.toml
└── requirements.txt
```

---

## Quick Start

### 1. Generate Shadows

Generate a single shadow at 11:55 on March 8th 2025 — automatically detects lat/lon + timezone:

```bash
dem-shadows-generate ^
  --dem-dir "examples" ^
  --out-dir "out" ^
  --auto-latlon ^
  --auto-timezone ^
  --start 2025-03-08 ^
  --end 2025-03-08 ^
  --only-time 11:55
```

Minimal example for a whole day shadow generation hourly on March 8th 2025 — automatically detects lat/lon + timezone:

```bash
dem-shadows-generate ^
  --dem-dir "examples" ^
  --out-dir "shadows_day" ^
  --auto-latlon ^
  --auto-timezone ^
  --start 2025-03-08 ^
  --end 2025-03-08
```

Resulting output (example):

```
shadows_day/
│
├── shadow_20250308T070000_EuropeZurich.tif
├── shadow_20250308T080000_EuropeZurich.tif
├── shadow_20250308T090000_EuropeZurich.tif
├── ...
└── schedule.csv
```

Each GeoTIFF contains:
- `0 = shadow`  
- `1 = sunlit`  
- `255 = nodata`  

![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/zh_example_dtm.png)

---

### 2. Animate Shadows

Create a GIF from the folder:

```bash
dem-shadows-animate ^
  --shadow-folder "shadows_day" ^
  --out-gif "shadows_day.gif"
```

Example GIF (included in repo):

Shadows of Zürich on 8th March 2025.

![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/zh_example_animate.gif)

---

### 3. Cumulative Shadow Map

```bash
dem-shadows-cumulate ^
  --shadow-folder "shadows_day" ^
  --out "shadow_cumulative.tif"
```

This produces a raster where the value of each pixel equals:

```
Number of hours of sun per pixel
```

Example Cumulative Shadows (included in repo):

Cumulative shadows of Zürich on 8th March 2025.

![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/zh_example_sh_cum.png)

---

## CLI Reference

---

### `dem-shadows-generate`

```
usage: dem-shadows-generate [OPTIONS]
```

**Required**
```
--dem PATH                # or --dem-dir PATH (automerge)
--out-dir PATH
--start YYYY-MM-DD
--end   YYYY-MM-DD
```

**Optional**
```
--auto-latlon
--auto-timezone
--lat FLOAT
--lon FLOAT
--timezone "Europe/Zurich"
--step-minutes 60
--only-time HH:MM
--dem-pattern "*.tif"
```

---

### `dem-shadows-animate`

```
usage: dem-shadows-animate [OPTIONS]
```

**Required**
```
--shadow-folder PATH
--out-gif PATH
```

**Optional**
```
--start YYYY-MM-DD
--end YYYY-MM-DD
--hour INT
--minute INT
--duration-ms 250
--sample-stride 1
--target-width 900
--target-height 0
--scale-factor FLOAT
--no-timestamp
--font-path PATH
```

---

### `dem-shadows-cumulate`

```
usage: dem-shadows-cumulate [OPTIONS]
```

```
--shadow-folder PATH
--out PATH
```

## Python API example

You can also call the core functionality from Python:

```python
from pathlib import Path
from datetime import date
from dem_shadows import LocationConfig, ShadowConfig, run_shadow_batch

dem_path = Path("examples/dem.tif")
out_dir = Path("out")

loc = LocationConfig(latitude=47.38, longitude=8.53, timezone="Europe/Zurich")
cfg = ShadowConfig(
    dem_path=dem_path,
    out_dir=out_dir,
    location=loc,
    start_date=date(2025, 3, 8),
    end_date=date(2025, 3, 8),
    step_minutes=60,
)

run_shadow_batch(cfg)
```

---

## Examples

### Example DEM
Located at:

```
examples/
├── dem.tif
├── dem_is.tif
└── dem_fr.tif
```

### Example imagery and shadow screenshots
Located at:

```
img/
├── app.png
├── icon.svg
├── logo.svg
├── zh_example_sh.png
├── zh_example_dtm.png
├── zh_example_ortho.png
├── zh_example_sh_cum.png
└── zh_example_animate.gif
```

These are referenced inside this README.

---

## Notebooks

Google Colab notebook:

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/marcop11/dem-shadows/blob/main/notebooks/try_it_yourself.ipynb)

---

## Testing

Run all tests:

```bash
pytest -q
```

The `tests/` folder contains:

```
tests/
└── test_basic.py
```

`test_basic.py` ensures basic imports and config structures work.

---

## Streamlit App
Try the live web interface:

[![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://dem-shadows.streamlit.app/)

![](https://raw.githubusercontent.com/marcop11/dem-shadows/refs/heads/main/img/app.png)

You can run the GUI with:

```bash
streamlit run app/streamlit_app.py
```

Features:
- Upload DEM
- Choose start/end time
- Generate shadows
- Preview results
- Download outputs and GIF


Find more digital elevation or terrain models to try:
- [Switzerland](https://www.swisstopo.admin.ch/en/height-model-swisssurface3d-raster)
- [Iceland](https://ftp.lmi.is/gisdata/raster/)
- [France](https://cartes.gouv.fr/telechargement/IGNF_MNH-LIDAR-HD)

---
## Author

Marco Pizzolato – [marcop11](https://github.com/marcop11)

Date: 24.11.2025

Version: 0.1.0

---

## License
This project is licensed under [**GPLv3**](LICENSE).

[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)
