pymdurs 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymdurs-0.1.1/PKG-INFO +898 -0
- pymdurs-0.1.1/README.md +873 -0
- pymdurs-0.1.1/pymdurs/Cargo.lock +4656 -0
- pymdurs-0.1.1/pymdurs/Cargo.toml +34 -0
- pymdurs-0.1.1/pymdurs/MATURIN_ANALYSIS.md +86 -0
- pymdurs-0.1.1/pymdurs/README.md +680 -0
- pymdurs-0.1.1/pymdurs/__init__.py +2 -0
- pymdurs-0.1.1/pymdurs/src/bindings/bounding_box.rs +51 -0
- pymdurs-0.1.1/pymdurs/src/bindings/building.rs +201 -0
- pymdurs-0.1.1/pymdurs/src/bindings/cadastre.rs +82 -0
- pymdurs-0.1.1/pymdurs/src/bindings/cosia.rs +65 -0
- pymdurs-0.1.1/pymdurs/src/bindings/dem.rs +70 -0
- pymdurs-0.1.1/pymdurs/src/bindings/geo_core.rs +53 -0
- pymdurs-0.1.1/pymdurs/src/bindings/iris.rs +73 -0
- pymdurs-0.1.1/pymdurs/src/bindings/lcz.rs +93 -0
- pymdurs-0.1.1/pymdurs/src/bindings/lidar.rs +119 -0
- pymdurs-0.1.1/pymdurs/src/bindings/mod.rs +31 -0
- pymdurs-0.1.1/pymdurs/src/bindings/rnb.rs +82 -0
- pymdurs-0.1.1/pymdurs/src/bindings/road.rs +83 -0
- pymdurs-0.1.1/pymdurs/src/bindings/vegetation.rs +94 -0
- pymdurs-0.1.1/pymdurs/src/bindings/water.rs +86 -0
- pymdurs-0.1.1/pymdurs/src/lib.rs +74 -0
- pymdurs-0.1.1/pymdurs/tests/test_basic.py +51 -0
- pymdurs-0.1.1/pyproject.toml +67 -0
- pymdurs-0.1.1/rsmdu/Cargo.toml +68 -0
- pymdurs-0.1.1/rsmdu/examples/README.md +144 -0
- pymdurs-0.1.1/rsmdu/examples/building_complete.rs +214 -0
- pymdurs-0.1.1/rsmdu/examples/building_from_geojson.rs +116 -0
- pymdurs-0.1.1/rsmdu/examples/building_from_ign.rs +62 -0
- pymdurs-0.1.1/rsmdu/examples/building_from_ign_api.rs +191 -0
- pymdurs-0.1.1/rsmdu/examples/building_manual.rs +40 -0
- pymdurs-0.1.1/rsmdu/examples/cadastre_from_ign.rs +52 -0
- pymdurs-0.1.1/rsmdu/examples/cosia_from_ign.rs +32 -0
- pymdurs-0.1.1/rsmdu/examples/dem_from_ign.rs +36 -0
- pymdurs-0.1.1/rsmdu/examples/iris_from_ign.rs +51 -0
- pymdurs-0.1.1/rsmdu/examples/lcz_from_url.rs +40 -0
- pymdurs-0.1.1/rsmdu/examples/rnb_from_api.rs +51 -0
- pymdurs-0.1.1/rsmdu/examples/road_from_ign.rs +51 -0
- pymdurs-0.1.1/rsmdu/examples/vegetation_from_ign.rs +63 -0
- pymdurs-0.1.1/rsmdu/examples/water_from_ign.rs +54 -0
- pymdurs-0.1.1/rsmdu/src/collect/global_variables.rs +8 -0
- pymdurs-0.1.1/rsmdu/src/collect/ign/data/Tableau-suivi-services-web-01-08-2025.csv +1349 -0
- pymdurs-0.1.1/rsmdu/src/collect/ign/data/Tableau-suivi-services-web-06-02-2026.csv +1387 -0
- pymdurs-0.1.1/rsmdu/src/collect/ign/data/Tableau-suivi-services-web-23-05-2025.csv +1265 -0
- pymdurs-0.1.1/rsmdu/src/collect/ign/ign_collect.rs +596 -0
- pymdurs-0.1.1/rsmdu/src/collect/ign/mod.rs +2 -0
- pymdurs-0.1.1/rsmdu/src/collect/mod.rs +3 -0
- pymdurs-0.1.1/rsmdu/src/commons/basic_functions.rs +21 -0
- pymdurs-0.1.1/rsmdu/src/commons/mod.rs +2 -0
- pymdurs-0.1.1/rsmdu/src/geo_core.rs +187 -0
- pymdurs-0.1.1/rsmdu/src/geometric/building.rs +1162 -0
- pymdurs-0.1.1/rsmdu/src/geometric/cadastre.rs +167 -0
- pymdurs-0.1.1/rsmdu/src/geometric/cosia.rs +177 -0
- pymdurs-0.1.1/rsmdu/src/geometric/dem.rs +465 -0
- pymdurs-0.1.1/rsmdu/src/geometric/iris.rs +167 -0
- pymdurs-0.1.1/rsmdu/src/geometric/lcz.rs +591 -0
- pymdurs-0.1.1/rsmdu/src/geometric/lidar.rs +2396 -0
- pymdurs-0.1.1/rsmdu/src/geometric/mod.rs +11 -0
- pymdurs-0.1.1/rsmdu/src/geometric/rnb.rs +288 -0
- pymdurs-0.1.1/rsmdu/src/geometric/road.rs +167 -0
- pymdurs-0.1.1/rsmdu/src/geometric/vegetation.rs +518 -0
- pymdurs-0.1.1/rsmdu/src/geometric/water.rs +272 -0
- pymdurs-0.1.1/rsmdu/src/lib.rs +12 -0
- pymdurs-0.1.1/rsmdu/src/main.rs +7 -0
pymdurs-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pymdurs
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
10
|
+
Requires-Dist: pandas>=2.0.0
|
|
11
|
+
Requires-Dist: numpy>=2.0.2
|
|
12
|
+
Requires-Dist: geopandas>=1.0.1
|
|
13
|
+
Requires-Dist: rasterio>=1.4.3
|
|
14
|
+
Requires-Dist: umep>=0.0.1a18
|
|
15
|
+
Requires-Dist: solweig>=0.1.0b55
|
|
16
|
+
Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Summary: Rust transpilation of pymdu (Python Urban Data Model) - Python bindings
|
|
19
|
+
Home-Page: https://github.com/rupeelab17/rsmdu
|
|
20
|
+
Author-email: Boris Brangeon <boris.brangeon@plateforme-tipee.com>
|
|
21
|
+
License: GPL-3.0
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
24
|
+
|
|
25
|
+
# pymdurs
|
|
26
|
+
|
|
27
|
+
Rust transpilation of [pymdu](https://github.com/rupeelab17/pymdu) (Python Urban Data Model).
|
|
28
|
+
|
|
29
|
+
This project transpiles the Python pymdu library to Rust, using the [GeoRust](https://georust.org/) ecosystem for geospatial operations. It provides three deployment targets: native Rust library, Python bindings (PyO3), and WebAssembly (WASM) for browser-based applications.
|
|
30
|
+
|
|
31
|
+
## 🎯 Project Overview
|
|
32
|
+
|
|
33
|
+
**pymdurs** is a comprehensive geospatial data processing library for urban data analysis, providing:
|
|
34
|
+
|
|
35
|
+
- **Native Rust library** (`rsmdu`) - High-performance geospatial operations
|
|
36
|
+
- **Python bindings** (`pymdurs`) - Pythonic API with PyO3
|
|
37
|
+
- **WebAssembly bindings** (`rsmdu-wasm`) - Browser-based geospatial processing
|
|
38
|
+
|
|
39
|
+
## 📦 Project Structure
|
|
40
|
+
|
|
41
|
+
This project is organized as a **Cargo workspace** with three main crates:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
pymdurs/
|
|
45
|
+
├── rsmdu/ # Core Rust library
|
|
46
|
+
│ ├── src/
|
|
47
|
+
│ │ ├── geometric/ # Geometric data structures
|
|
48
|
+
│ │ │ ├── building.rs # Building and BuildingCollection
|
|
49
|
+
│ │ │ ├── dem.rs # Digital Elevation Model
|
|
50
|
+
│ │ │ ├── cadastre.rs # Cadastral parcel data
|
|
51
|
+
│ │ │ ├── iris.rs # IRIS statistical units
|
|
52
|
+
│ │ │ ├── lcz.rs # Local Climate Zone
|
|
53
|
+
│ │ │ ├── road.rs # Road segments
|
|
54
|
+
│ │ │ ├── water.rs # Water bodies
|
|
55
|
+
│ │ │ └── vegetation.rs # Vegetation zones
|
|
56
|
+
│ │ ├── collect/ # Data collection modules
|
|
57
|
+
│ │ │ ├── ign/ # IGN API integration
|
|
58
|
+
│ │ │ │ └── ign_collect.rs
|
|
59
|
+
│ │ │ └── global_variables.rs
|
|
60
|
+
│ │ ├── geo_core.rs # Base GeoCore class
|
|
61
|
+
│ │ ├── commons/ # Common utilities
|
|
62
|
+
│ │ ├── lib.rs # Library root
|
|
63
|
+
│ │ └── main.rs # Binary entry point
|
|
64
|
+
│ └── examples/ # Rust usage examples
|
|
65
|
+
│
|
|
66
|
+
├── pymdurs/ # Python bindings (PyO3)
|
|
67
|
+
│ ├── src/
|
|
68
|
+
│ │ └── bindings/ # PyO3 bindings
|
|
69
|
+
│ ├── examples/ # Python usage examples
|
|
70
|
+
│ └── tests/ # Python tests
|
|
71
|
+
│
|
|
72
|
+
└── rsmdu-wasm/ # WebAssembly bindings
|
|
73
|
+
├── src/
|
|
74
|
+
│ └── lib.rs # WASM bindings
|
|
75
|
+
└── examples/ # Browser examples
|
|
76
|
+
├── index.html # Building visualization
|
|
77
|
+
└── dem.html # DEM visualization
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## ✨ Features
|
|
81
|
+
|
|
82
|
+
### 🏢 Building Data Management
|
|
83
|
+
|
|
84
|
+
- **Building data collection**: Load buildings from Shapefiles, GeoJSON, or IGN API
|
|
85
|
+
- **Geometric operations**: Area, centroid, and height calculations
|
|
86
|
+
- **Data processing**: Height processing with fallback to storeys or mean district height
|
|
87
|
+
- **Height calculation**: Automatic height filling using:
|
|
88
|
+
- Number of storeys × default storey height
|
|
89
|
+
- Alternative height field (HAUTEUR_2)
|
|
90
|
+
- Mean district height (weighted by area)
|
|
91
|
+
- **DataFrame support**: Integration with Polars for tabular operations (equivalent to GeoDataFrame)
|
|
92
|
+
- **WebAssembly support**: Process building data directly in the browser
|
|
93
|
+
|
|
94
|
+
### 🗻 Digital Elevation Model (DEM)
|
|
95
|
+
|
|
96
|
+
- **DEM collection**: Download DEM data from IGN API via WMS-R
|
|
97
|
+
- **Raster processing**: GeoTIFF handling and validation
|
|
98
|
+
- **Mask generation**: Automatic mask creation for DEM boundaries
|
|
99
|
+
- **Browser visualization**: Interactive DEM viewer with Leaflet.js integration
|
|
100
|
+
- **IGN API integration**: Direct loading from IGN WMS service in browser
|
|
101
|
+
|
|
102
|
+
### 🗺️ Cadastral Data
|
|
103
|
+
|
|
104
|
+
- **Cadastre collection**: Download cadastral parcel data from IGN API via WFS
|
|
105
|
+
- **GeoJSON parsing**: Automatic parsing of IGN API responses
|
|
106
|
+
- **GeoJSON export**: Save cadastral data to GeoPackage format
|
|
107
|
+
|
|
108
|
+
### 📊 IRIS Statistical Units
|
|
109
|
+
|
|
110
|
+
- **IRIS collection**: Download IRIS (statistical units) data from IGN API via WFS
|
|
111
|
+
- **GeoJSON parsing**: Automatic parsing of IGN API responses
|
|
112
|
+
- **GeoJSON export**: Save IRIS data to GeoPackage format
|
|
113
|
+
|
|
114
|
+
### 🌡️ LCZ (Local Climate Zone)
|
|
115
|
+
|
|
116
|
+
- **LCZ collection**: Load Local Climate Zone data from external sources
|
|
117
|
+
- **Color mapping**: Built-in LCZ color table (17 LCZ types)
|
|
118
|
+
- **Spatial filtering**: Filter LCZ data by bounding box
|
|
119
|
+
- **Shapefile support**: Load from zip URLs (requires GDAL)
|
|
120
|
+
|
|
121
|
+
### 🛣️ Road and Infrastructure
|
|
122
|
+
|
|
123
|
+
- **Road collection**: Download road segments from IGN API
|
|
124
|
+
- **Water bodies**: Download water body data from IGN API
|
|
125
|
+
- **Vegetation zones**: Download vegetation data from IGN API
|
|
126
|
+
|
|
127
|
+
### 🛰️ LiDAR Processing
|
|
128
|
+
|
|
129
|
+
- **LiDAR data collection**: Download LAZ files from IGN WFS service
|
|
130
|
+
- **Point cloud processing**: Load and process LiDAR point clouds
|
|
131
|
+
- **Raster generation**: Create DSM (Digital Surface Model), DTM (Digital Terrain Model), and CHM (Canopy Height Model)
|
|
132
|
+
- **Classification filtering**: Filter points by classification (ground, vegetation, buildings, water, etc.)
|
|
133
|
+
- **Multi-band GeoTIFF export**: Export processed rasters as multi-band GeoTIFF files
|
|
134
|
+
- **Automatic workflow**: Points are automatically fetched and loaded when bounding box is set
|
|
135
|
+
|
|
136
|
+
### 🌐 IGN API Integration
|
|
137
|
+
|
|
138
|
+
- **WFS (Web Feature Service)**: Vector data retrieval (buildings, roads, water, etc.)
|
|
139
|
+
- **WMS (Web Map Service)**: Raster data retrieval (DEM, orthoimagery, etc.)
|
|
140
|
+
- **WMS-R endpoint**: Optimized raster service endpoint
|
|
141
|
+
- **OGC compliance**: Follows OGC WFS 2.0.0 and WMS 1.3.0 standards
|
|
142
|
+
- **Browser support**: Direct API calls from WebAssembly
|
|
143
|
+
|
|
144
|
+
### 🎨 WebAssembly Features
|
|
145
|
+
|
|
146
|
+
- **Building processing**: Load and process building GeoJSON in the browser
|
|
147
|
+
- **DEM visualization**: Interactive DEM viewer with color scales
|
|
148
|
+
- **IGN API integration**: Fetch data directly from IGN API in browser
|
|
149
|
+
- **Leaflet.js integration**: Seamless integration with Leaflet maps
|
|
150
|
+
- **Dynamic bbox**: Automatic bounding box updates based on map view
|
|
151
|
+
|
|
152
|
+
## 🚀 Installation
|
|
153
|
+
|
|
154
|
+
### Python Package
|
|
155
|
+
|
|
156
|
+
**Requirements:** Python >= 3.10, pandas >= 2.0.0, numpy >= 2.0.2
|
|
157
|
+
|
|
158
|
+
**Important:** Run maturin from the **project root** (where `pyproject.toml` is). Maturin uses `manifest-path = "pymdurs/Cargo.toml"`.
|
|
159
|
+
|
|
160
|
+
#### 1. Clone and setup
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git clone https://github.com/rupeelab17/rsmdu.git
|
|
164
|
+
cd rsmdu
|
|
165
|
+
uv venv .venv --python 3.13
|
|
166
|
+
source .venv/bin/activate
|
|
167
|
+
uv pip install maturin
|
|
168
|
+
uv sync
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### 2. Install GDAL (prerequisite)
|
|
172
|
+
|
|
173
|
+
| Platform | Command |
|
|
174
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
175
|
+
| **macOS** | `brew install gdal` or `ARCHFLAGS="-arch arm64" uv pip install --no-cache-dir gdal` |
|
|
176
|
+
| **Linux** | `sudo apt-get update && sudo apt-get install -y libgdal-dev gdal-bin libclang-dev` |
|
|
177
|
+
| **Windows** | [OSGeo4W](https://trac.osgeo.org/osgeo4w/) (GDAL, GEOS, PROJ, SQLite3) + `choco install llvm pkgconfiglite sqlite -y` + set `GDAL_HOME`, `PKG_CONFIG_PATH`, `PATH` |
|
|
178
|
+
|
|
179
|
+
#### 3. Build (maturin develop)
|
|
180
|
+
|
|
181
|
+
| Platform | Target | Command |
|
|
182
|
+
| ------------------------- | --------------------------- | ------------------------------------------------------------------------ |
|
|
183
|
+
| **macOS** (Apple Silicon) | `aarch64-apple-darwin` | `maturin develop --target aarch64-apple-darwin` |
|
|
184
|
+
| **macOS** (Intel) | `x86_64-apple-darwin` | `maturin develop --target x86_64-apple-darwin` |
|
|
185
|
+
| **Linux** (x86_64) | `x86_64-unknown-linux-gnu` | `maturin develop --target x86_64-unknown-linux-gnu` or `maturin develop` |
|
|
186
|
+
| **Linux** (ARM64) | `aarch64-unknown-linux-gnu` | `maturin develop --target aarch64-unknown-linux-gnu` |
|
|
187
|
+
| **Windows** | `x86_64-pc-windows-msvc` | `maturin develop --target x86_64-pc-windows-msvc` |
|
|
188
|
+
|
|
189
|
+
With uv: `uv run maturin develop --target <target>`
|
|
190
|
+
|
|
191
|
+
**macOS: linker can't find GDAL / "library 'gdal' not found"**
|
|
192
|
+
If you upgraded GDAL or PROJ with Homebrew, the linker may still use old paths. Clean and point pkg-config at the current install, then rebuild:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
cd pymdurs && cargo clean && cd ..
|
|
196
|
+
export PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig"
|
|
197
|
+
maturin develop --target aarch64-apple-darwin
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### 4. Verify and release
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
python -c "import pymdurs; print('OK')"
|
|
204
|
+
# Release wheel
|
|
205
|
+
maturin build --target <target> --release
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Installing Rust
|
|
209
|
+
|
|
210
|
+
Maturin compiles the Rust extension. If Rust is not installed, follow the instructions for your operating system:
|
|
211
|
+
|
|
212
|
+
#### Windows
|
|
213
|
+
|
|
214
|
+
1. **Download and run rustup-init.exe:**
|
|
215
|
+
- Visit https://rustup.rs/
|
|
216
|
+
- Download `rustup-init.exe`
|
|
217
|
+
- Run the installer and follow the prompts
|
|
218
|
+
|
|
219
|
+
2. **Or use PowerShell:**
|
|
220
|
+
|
|
221
|
+
```powershell
|
|
222
|
+
Invoke-WebRequest -Uri https://win.rustup.rs/x86_64 -OutFile rustup-init.exe
|
|
223
|
+
.\rustup-init.exe
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
3. **Verify installation:**
|
|
227
|
+
|
|
228
|
+
```powershell
|
|
229
|
+
rustc --version
|
|
230
|
+
cargo --version
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
4. **Install Visual Studio Build Tools (required for Windows):**
|
|
234
|
+
- Download from: https://visualstudio.microsoft.com/downloads/
|
|
235
|
+
- Install "Desktop development with C++" workload
|
|
236
|
+
- Or install the lighter "C++ build tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
|
|
237
|
+
|
|
238
|
+
**Note**: On Windows, you may need to restart your terminal after installation for PATH changes to take effect.
|
|
239
|
+
|
|
240
|
+
#### macOS
|
|
241
|
+
|
|
242
|
+
1. **Install using Homebrew (recommended):**
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
brew install rust
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
2. **Or use rustup (official installer):**
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
3. **Follow the prompts** and restart your terminal
|
|
255
|
+
|
|
256
|
+
4. **Verify installation:**
|
|
257
|
+
```bash
|
|
258
|
+
rustc --version
|
|
259
|
+
cargo --version
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Note**: On macOS, you may need to install Xcode Command Line Tools:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
xcode-select --install
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Linux
|
|
269
|
+
|
|
270
|
+
1. **Install using your package manager:**
|
|
271
|
+
|
|
272
|
+
**Ubuntu/Debian:**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
sudo apt update
|
|
276
|
+
sudo apt install rustc cargo
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Fedora:**
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
sudo dnf install rust cargo
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Arch Linux:**
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
sudo pacman -S rust
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
2. **Or use rustup (recommended for latest version):**
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
295
|
+
source $HOME/.cargo/env
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
3. **Install build dependencies (required):**
|
|
299
|
+
|
|
300
|
+
**Ubuntu/Debian:**
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
sudo apt install build-essential pkg-config libssl-dev
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Fedora:**
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
sudo dnf install gcc pkg-config openssl-devel
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Arch Linux:**
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
sudo pacman -S base-devel
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
4. **Verify installation:**
|
|
319
|
+
```bash
|
|
320
|
+
rustc --version
|
|
321
|
+
cargo --version
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Docker
|
|
325
|
+
|
|
326
|
+
When building an image that installs pymdurs with `uv pip install https://github.com/rupeelab17/rsmdu.git`, **Rust must be installed in a previous layer**. If Rust is not present, maturin installs it on the fly and can hit "Text file busy (os error 26)" when running `cargo metadata` immediately after.
|
|
327
|
+
|
|
328
|
+
**Fix:** install Rust (e.g. with rustup) **before** the step that runs `uv pip install`:
|
|
329
|
+
|
|
330
|
+
```dockerfile
|
|
331
|
+
# Install Rust so maturin does not auto-install it (avoids "Text file busy")
|
|
332
|
+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable \
|
|
333
|
+
&& . "$HOME/.cargo/env" \
|
|
334
|
+
&& rustc --version
|
|
335
|
+
ENV PATH="/root/.cargo/bin:${PATH}"
|
|
336
|
+
|
|
337
|
+
# Then install pymdurs (maturin will find cargo and build the wheel)
|
|
338
|
+
RUN micromamba run -n umep_pymdu python -m uv pip install https://github.com/rupeelab17/rsmdu.git
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Adjust the environment (e.g. `micromamba run -n umep_pymdu`, or `ENV PATH` if Rust is installed for another user) to match your image.
|
|
342
|
+
|
|
343
|
+
### WebAssembly
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
# Install wasm-pack
|
|
347
|
+
cargo install wasm-pack
|
|
348
|
+
|
|
349
|
+
# Add WASM target
|
|
350
|
+
rustup target add wasm32-unknown-unknown
|
|
351
|
+
|
|
352
|
+
# Navigate to WASM package directory
|
|
353
|
+
cd rsmdu-wasm
|
|
354
|
+
|
|
355
|
+
# Fix WASM target (if needed)
|
|
356
|
+
./fix-wasm-target.sh
|
|
357
|
+
|
|
358
|
+
# Build WASM package
|
|
359
|
+
./build.sh
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
See `rsmdu-wasm/README.md` for detailed WebAssembly setup instructions.
|
|
363
|
+
|
|
364
|
+
## 📖 Usage
|
|
365
|
+
|
|
366
|
+
### Rust Library
|
|
367
|
+
|
|
368
|
+
#### Building Collection
|
|
369
|
+
|
|
370
|
+
```rust
|
|
371
|
+
use rsmdu::geometric::building::BuildingCollection;
|
|
372
|
+
|
|
373
|
+
// Create BuildingCollection (Python: Building(output_path='./'))
|
|
374
|
+
let mut buildings = BuildingCollection::new(
|
|
375
|
+
None, // filepath_shp (None = use IGN API)
|
|
376
|
+
Some("./output".to_string()), // output_path
|
|
377
|
+
3.0, // defaultStoreyHeight
|
|
378
|
+
None, // set_crs (None = default EPSG:2154)
|
|
379
|
+
)?;
|
|
380
|
+
|
|
381
|
+
// Set bounding box (Python: buildings.bbox = [...])
|
|
382
|
+
buildings.set_bbox(-1.152704, 46.181627, -1.139893, 46.18699)?;
|
|
383
|
+
|
|
384
|
+
// Run processing (Python: buildings = buildings.run())
|
|
385
|
+
let buildings = buildings.run()?;
|
|
386
|
+
|
|
387
|
+
// Convert to Polars DataFrame (Python: buildings.to_gdf())
|
|
388
|
+
let df = buildings.to_polars_df()?;
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### DEM (Digital Elevation Model)
|
|
392
|
+
|
|
393
|
+
```rust
|
|
394
|
+
use rsmdu::geometric::dem::Dem;
|
|
395
|
+
|
|
396
|
+
// Create Dem instance
|
|
397
|
+
let mut dem = Dem::new(Some("./output".to_string()))?;
|
|
398
|
+
|
|
399
|
+
// Set bounding box
|
|
400
|
+
dem.set_bbox(-1.152704, 46.181627, -1.139893, 46.18699);
|
|
401
|
+
|
|
402
|
+
// Run DEM processing
|
|
403
|
+
let dem_result = dem.run(None)?;
|
|
404
|
+
|
|
405
|
+
// Get output paths
|
|
406
|
+
println!("DEM saved to: {:?}", dem_result.get_path_save_tiff());
|
|
407
|
+
println!("Mask: {:?}", dem_result.get_path_save_mask());
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Python Bindings
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
import pymdurs
|
|
414
|
+
|
|
415
|
+
# Create BuildingCollection
|
|
416
|
+
buildings = pymdurs.geometric.Building(
|
|
417
|
+
output_path="./output",
|
|
418
|
+
defaultStoreyHeight=3.0
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Set bounding box
|
|
422
|
+
buildings.set_bbox(-1.152704, 46.181627, -1.139893, 46.18699)
|
|
423
|
+
|
|
424
|
+
# Run processing (downloads from IGN API)
|
|
425
|
+
buildings = buildings.run()
|
|
426
|
+
|
|
427
|
+
# Convert to pandas DataFrame
|
|
428
|
+
df = buildings.to_pandas()
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### LiDAR Processing
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
import pymdurs
|
|
435
|
+
|
|
436
|
+
# Create Lidar instance
|
|
437
|
+
lidar = pymdurs.geometric.Lidar(output_path="./output")
|
|
438
|
+
|
|
439
|
+
# Set bounding box (automatically fetches LAZ file URLs and loads points)
|
|
440
|
+
# Format: min_x, min_y, max_x, max_y (WGS84, EPSG:4326)
|
|
441
|
+
lidar.set_bbox(-1.154894, 46.182639, -1.148361, 46.186820)
|
|
442
|
+
|
|
443
|
+
# Set CRS (optional, defaults to EPSG:2154)
|
|
444
|
+
lidar.set_crs(2154)
|
|
445
|
+
|
|
446
|
+
# Process points to create rasters (DSM, DTM, CHM)
|
|
447
|
+
# Classification filter: 2=Ground, 3=Low Vegetation, 4=Medium, 5=High, 6=Building, 9=Water
|
|
448
|
+
classification_list = [3, 4, 5, 9] # Vegetation and water
|
|
449
|
+
output_path = lidar.run(file_name="CDSM.tif", classification_list=classification_list)
|
|
450
|
+
|
|
451
|
+
print(f"GeoTIFF saved to: {output_path}")
|
|
452
|
+
# File contains 3 bands: DSM, DTM, CHM
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### WebAssembly (Browser)
|
|
456
|
+
|
|
457
|
+
#### Building Processing
|
|
458
|
+
|
|
459
|
+
```javascript
|
|
460
|
+
import init, { WasmBuildingCollection } from "./pkg/rsmdu_wasm.js";
|
|
461
|
+
|
|
462
|
+
// Initialize WASM
|
|
463
|
+
await init("./pkg/rsmdu_wasm_bg.wasm");
|
|
464
|
+
|
|
465
|
+
// Load buildings from GeoJSON
|
|
466
|
+
const geojson = {
|
|
467
|
+
type: "FeatureCollection",
|
|
468
|
+
features: [
|
|
469
|
+
/* ... */
|
|
470
|
+
],
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const collection = WasmBuildingCollection.from_geojson(
|
|
474
|
+
JSON.stringify(geojson),
|
|
475
|
+
3.0, // default storey height in meters
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
// Process heights
|
|
479
|
+
collection.process_heights();
|
|
480
|
+
|
|
481
|
+
// Get processed GeoJSON
|
|
482
|
+
const processedGeojson = collection.to_geojson();
|
|
483
|
+
|
|
484
|
+
// Get statistics
|
|
485
|
+
const stats = collection.get_stats();
|
|
486
|
+
console.log("Building count:", stats.count);
|
|
487
|
+
console.log("Total area:", stats.total_area);
|
|
488
|
+
console.log("Mean height:", stats.mean_height);
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
#### DEM Loading
|
|
492
|
+
|
|
493
|
+
```javascript
|
|
494
|
+
import init, { WasmDem } from "./pkg/rsmdu_wasm.js";
|
|
495
|
+
|
|
496
|
+
// Initialize WASM
|
|
497
|
+
await init("./pkg/rsmdu_wasm_bg.wasm");
|
|
498
|
+
|
|
499
|
+
// Load DEM from IGN API
|
|
500
|
+
const dem = await WasmDem.from_ign_api(
|
|
501
|
+
-1.152704, // min_x
|
|
502
|
+
46.181627, // min_y
|
|
503
|
+
-1.139893, // max_x
|
|
504
|
+
46.18699, // max_y
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
// Get DEM dimensions
|
|
508
|
+
console.log("Width:", dem.width());
|
|
509
|
+
console.log("Height:", dem.height());
|
|
510
|
+
|
|
511
|
+
// Get extent
|
|
512
|
+
const extent = dem.get_extent();
|
|
513
|
+
console.log("Extent:", extent);
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
See `rsmdu-wasm/examples/index.html` and `rsmdu-wasm/examples/dem.html` for complete browser examples.
|
|
517
|
+
|
|
518
|
+
## 📚 Examples
|
|
519
|
+
|
|
520
|
+
Comprehensive examples are available for all three deployment targets:
|
|
521
|
+
|
|
522
|
+
### Rust Examples
|
|
523
|
+
|
|
524
|
+
Located in `rsmdu/examples/`:
|
|
525
|
+
|
|
526
|
+
**Building Examples:**
|
|
527
|
+
|
|
528
|
+
- **`building_manual.rs`**: Minimal example of manually creating buildings
|
|
529
|
+
- **`building_from_geojson.rs`**: Complete example loading from GeoJSON
|
|
530
|
+
- **`building_from_ign.rs`**: Example using `run()` method (Python-style)
|
|
531
|
+
- **`building_from_ign_api.rs`**: Detailed example loading buildings from IGN API
|
|
532
|
+
- **`building_complete.rs`**: Comprehensive example covering all use cases
|
|
533
|
+
|
|
534
|
+
**Other Examples:**
|
|
535
|
+
|
|
536
|
+
- **`dem_from_ign.rs`**: Downloading and processing DEM from IGN API
|
|
537
|
+
- **`cadastre_from_ign.rs`**: Downloading and processing cadastral data from IGN API
|
|
538
|
+
- **`iris_from_ign.rs`**: Downloading and processing IRIS statistical units from IGN API
|
|
539
|
+
- **`lcz_from_url.rs`**: Loading and processing LCZ data from URL
|
|
540
|
+
- **`cosia_from_ign.rs`**: Downloading COSIA landcover raster from IGN API
|
|
541
|
+
- **`rnb_from_api.rs`**: Downloading RNB (French National Building Reference) data from RNB API
|
|
542
|
+
- **`road_from_ign.rs`**: Downloading road segments from IGN API
|
|
543
|
+
- **`water_from_ign.rs`**: Downloading water bodies from IGN API
|
|
544
|
+
- **`vegetation_from_ign.rs`**: Downloading vegetation zones from IGN API
|
|
545
|
+
|
|
546
|
+
**Run Rust examples:**
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
cd rsmdu
|
|
550
|
+
cargo run --example building_manual
|
|
551
|
+
cargo run --example building_from_geojson
|
|
552
|
+
cargo run --example building_from_ign
|
|
553
|
+
cargo run --example dem_from_ign
|
|
554
|
+
cargo run --example cadastre_from_ign
|
|
555
|
+
cargo run --example iris_from_ign
|
|
556
|
+
cargo run --example lcz_from_url
|
|
557
|
+
cargo run --example cosia_from_ign
|
|
558
|
+
cargo run --example rnb_from_api
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Python Examples
|
|
562
|
+
|
|
563
|
+
Located in `examples/` (see [examples/README.md](examples/README.md) for full documentation):
|
|
564
|
+
|
|
565
|
+
- **`building_basic.py`**: Basic Building usage
|
|
566
|
+
- **`building_from_ign.py`**: Load buildings from IGN API
|
|
567
|
+
- **`dem_from_ign.py`**: Download DEM from IGN API
|
|
568
|
+
- **`cadastre_from_ign.py`**: Download cadastral data from IGN API
|
|
569
|
+
- **`iris_from_ign.py`**: Download IRIS statistical units from IGN API
|
|
570
|
+
- **`lcz_from_url.py`**: Load LCZ data from URL
|
|
571
|
+
- **`cosia_from_ign.py`**: Complete COSIA workflow - download, vectorize and convert to UMEP format
|
|
572
|
+
- **`rnb_from_api.py`**: Download RNB (French National Building Reference) data from RNB API
|
|
573
|
+
- **`lidar_from_wfs.py`**: Download and process LiDAR data from IGN WFS service
|
|
574
|
+
- **`road_from_ign.py`**: Download road segments from IGN API
|
|
575
|
+
- **`water_from_ign.py`**: Download water bodies from IGN API
|
|
576
|
+
- **`vegetation_from_ign.py`**: Download vegetation zones from IGN API
|
|
577
|
+
- **`umep_workflow.py`**: Complete UMEP workflow for urban climate modeling (SOLWEIG, SVF, etc.)
|
|
578
|
+
- **`umep_workflow_new.py`**: Alternative UMEP workflow using solweig (SOLWEIG from UMEP-dev), with GIF export from preview PNGs
|
|
579
|
+
|
|
580
|
+
**Run Python examples** (from project root):
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
python examples/building_basic.py
|
|
584
|
+
python examples/building_from_ign.py
|
|
585
|
+
python examples/dem_from_ign.py
|
|
586
|
+
python examples/cosia_from_ign.py
|
|
587
|
+
python examples/umep_workflow.py
|
|
588
|
+
python examples/umep_workflow_new.py
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### WebAssembly Examples
|
|
592
|
+
|
|
593
|
+
Located in `rsmdu-wasm/examples/`:
|
|
594
|
+
|
|
595
|
+
- **`index.html`**: Interactive building visualization with Leaflet.js
|
|
596
|
+
- Load buildings from GeoJSON or IGN API
|
|
597
|
+
- Dynamic bounding box based on map view
|
|
598
|
+
- Building statistics and visualization
|
|
599
|
+
- **`dem.html`**: Interactive DEM visualization with Leaflet.js
|
|
600
|
+
- Load DEM from local TIFF files or IGN API
|
|
601
|
+
- Multiple color scales (terrain, elevation, grayscale)
|
|
602
|
+
- Elevation query on click
|
|
603
|
+
- Automatic OSM layer opacity adjustment
|
|
604
|
+
|
|
605
|
+
**Run WebAssembly examples:**
|
|
606
|
+
|
|
607
|
+
```bash
|
|
608
|
+
cd rsmdu-wasm
|
|
609
|
+
./build.sh
|
|
610
|
+
cd examples
|
|
611
|
+
python3 -m http.server 8000
|
|
612
|
+
# Open http://localhost:8000/index.html or http://localhost:8000/dem.html
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
## 🌐 IGN API
|
|
616
|
+
|
|
617
|
+
The library integrates with the French IGN (Institut Géographique National) Géoplateforme API.
|
|
618
|
+
|
|
619
|
+
### Available Services
|
|
620
|
+
|
|
621
|
+
**Vector Data (WFS)**:
|
|
622
|
+
|
|
623
|
+
- `buildings`: Building footprints (BDTOPO_V3:batiment)
|
|
624
|
+
- `road`: Road segments (BDTOPO_V3:troncon_de_route)
|
|
625
|
+
- `water`: Water bodies (BDTOPO_V3:plan_d_eau)
|
|
626
|
+
- `cadastre`: Cadastral parcels (CADASTRALPARCELS.PARCELLAIRE_EXPRESS:parcelle)
|
|
627
|
+
- `iris`: IRIS statistical units (STATISTICALUNITS.IRIS:contours_iris)
|
|
628
|
+
- `lidar`: LiDAR point cloud data (IGNF_LIDAR-HD_TA:nuage-dalle)
|
|
629
|
+
- `vegetation`: Vegetation zones
|
|
630
|
+
- `hydrographique`: Hydrographic details
|
|
631
|
+
|
|
632
|
+
**Raster Data (WMS-R)**:
|
|
633
|
+
|
|
634
|
+
- `dem`: Digital Elevation Model (ELEVATION.ELEVATIONGRIDCOVERAGE.HIGHRES)
|
|
635
|
+
- `dsm`: Digital Surface Model
|
|
636
|
+
- `irc`: IRC orthoimagery
|
|
637
|
+
- `ortho`: High-resolution orthoimagery
|
|
638
|
+
- `cosia`: COSIA imagery
|
|
639
|
+
|
|
640
|
+
### API Requirements
|
|
641
|
+
|
|
642
|
+
- **Internet connection**: Required for API requests
|
|
643
|
+
- **Rate limiting**: The API may have rate limits
|
|
644
|
+
- **Coordinate system**: Bounding boxes must be in WGS84 (EPSG:4326)
|
|
645
|
+
- **API keys**: For production use, you may need to register at https://geoservices.ign.fr/
|
|
646
|
+
- **CORS**: Browser-based requests require CORS support (available for IGN API)
|
|
647
|
+
|
|
648
|
+
## 🏗️ Architecture
|
|
649
|
+
|
|
650
|
+
### Workspace Structure
|
|
651
|
+
|
|
652
|
+
The project uses a Cargo workspace with three crates:
|
|
653
|
+
|
|
654
|
+
1. **`rsmdu`**: Core Rust library with all geospatial functionality
|
|
655
|
+
- Uses feature flags (`wasm`) to conditionally compile WASM-incompatible dependencies
|
|
656
|
+
- Optional dependencies: `gdal`, `proj`, `geos`, `polars`, `reqwest`, etc.
|
|
657
|
+
|
|
658
|
+
2. **`pymdurs`**: Python bindings using PyO3
|
|
659
|
+
- Depends on `rsmdu` crate
|
|
660
|
+
- Provides Pythonic API with aliases
|
|
661
|
+
|
|
662
|
+
3. **`rsmdu-wasm`**: WebAssembly bindings
|
|
663
|
+
- Uses `rsmdu` with `wasm` feature flag
|
|
664
|
+
- Only WASM-compatible dependencies (geo, geojson, geotiff, tiff)
|
|
665
|
+
- Browser-specific APIs (web-sys, wasm-bindgen)
|
|
666
|
+
|
|
667
|
+
### Inheritance Pattern (Python → Rust)
|
|
668
|
+
|
|
669
|
+
In Python, `Building` inherits from `IgnCollect`, which inherits from `GeoCore`. In Rust, we use composition:
|
|
670
|
+
|
|
671
|
+
```rust
|
|
672
|
+
// Python: class Building(IgnCollect)
|
|
673
|
+
// Rust: BuildingCollection contains GeoCore and IgnCollect
|
|
674
|
+
pub struct BuildingCollection {
|
|
675
|
+
pub geo_core: GeoCore, // Inherits from GeoCore
|
|
676
|
+
ign_collect: Option<IgnCollect>, // Inherits from IgnCollect
|
|
677
|
+
// ...
|
|
678
|
+
}
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### GeoCore Properties
|
|
682
|
+
|
|
683
|
+
`GeoCore` provides base functionality:
|
|
684
|
+
|
|
685
|
+
- `epsg`: Coordinate Reference System (default: 2154)
|
|
686
|
+
- `bbox`: Bounding box
|
|
687
|
+
- `output_path`: Output directory
|
|
688
|
+
- `output_path_shp`: Shapefile output path
|
|
689
|
+
- `filename_shp`: Shapefile filename
|
|
690
|
+
|
|
691
|
+
## 📊 Status
|
|
692
|
+
|
|
693
|
+
### ✅ Fully Implemented
|
|
694
|
+
|
|
695
|
+
**Core Features:**
|
|
696
|
+
|
|
697
|
+
- ✅ GeoJSON parsing and building collection
|
|
698
|
+
- ✅ IGN API integration (WFS and WMS)
|
|
699
|
+
- ✅ Building height processing with multiple fallback strategies
|
|
700
|
+
- ✅ DEM collection via WMS-R
|
|
701
|
+
- ✅ Cadastral data collection via WFS
|
|
702
|
+
- ✅ IRIS statistical units collection via WFS
|
|
703
|
+
- ✅ LCZ data structure with color mapping (17 LCZ types)
|
|
704
|
+
- ✅ Road, water, and vegetation data collection
|
|
705
|
+
- ✅ Polars DataFrame conversion
|
|
706
|
+
- ✅ GeoCore base class with all properties
|
|
707
|
+
- ✅ BuildingCollection with `run()` method (Python-style)
|
|
708
|
+
- ✅ Coordinate Reference System (CRS) transformations using Proj
|
|
709
|
+
|
|
710
|
+
**Python Bindings (PyO3):**
|
|
711
|
+
|
|
712
|
+
- ✅ Complete Python bindings installable via `pip install pymdurs`
|
|
713
|
+
- ✅ All geometric classes available: `Building`, `Dem`, `Cadastre`, `Iris`, `Lcz`, `Lidar`, `Road`, `Water`, `Vegetation`
|
|
714
|
+
- ✅ Geometric submodule (`pymdurs.geometric`) for organized class access
|
|
715
|
+
- ✅ Pythonic API with aliases (e.g., `pymdurs.geometric.Building` instead of `PyBuilding`)
|
|
716
|
+
- ✅ Pandas DataFrame conversion for Building data
|
|
717
|
+
- ✅ GeoJSON export/import
|
|
718
|
+
- ✅ LiDAR processing with automatic point loading
|
|
719
|
+
- ✅ Comprehensive Python examples and tests
|
|
720
|
+
|
|
721
|
+
**WebAssembly Bindings:**
|
|
722
|
+
|
|
723
|
+
- ✅ Building collection processing in browser
|
|
724
|
+
- ✅ DEM loading from IGN API in browser
|
|
725
|
+
- ✅ DEM loading from local TIFF files
|
|
726
|
+
- ✅ Interactive Leaflet.js visualization
|
|
727
|
+
- ✅ Dynamic bounding box updates
|
|
728
|
+
- ✅ Building statistics
|
|
729
|
+
- ✅ DEM visualization with color scales
|
|
730
|
+
- ✅ Elevation query on map click
|
|
731
|
+
- ✅ OSM layer opacity control
|
|
732
|
+
|
|
733
|
+
**Data Formats:**
|
|
734
|
+
|
|
735
|
+
- ✅ GeoJSON parsing and generation
|
|
736
|
+
- ✅ GeoTIFF reading and validation
|
|
737
|
+
- ✅ GeoJSON export (simplified, saves as GeoJSON temporarily)
|
|
738
|
+
|
|
739
|
+
### 🚧 In Progress / Limitations
|
|
740
|
+
|
|
741
|
+
**Current Limitations:**
|
|
742
|
+
|
|
743
|
+
- ⚠️ GDAL Shapefile I/O temporarily disabled (API compatibility issues)
|
|
744
|
+
- ⚠️ Full GeoJSON export not yet implemented (saves as GeoJSON as workaround)
|
|
745
|
+
- ⚠️ LCZ shapefile loading from zip URLs requires full GDAL implementation
|
|
746
|
+
- ⚠️ Raster reprojection simplified (full resampling not yet implemented)
|
|
747
|
+
- ⚠️ Shapefile export for masks not yet available
|
|
748
|
+
- ⚠️ DEM pixel data access in WASM (currently metadata only)
|
|
749
|
+
|
|
750
|
+
**Workarounds:**
|
|
751
|
+
|
|
752
|
+
- Use GeoJSON format instead of Shapefiles for input/output
|
|
753
|
+
- GeoJSON export currently saves as GeoJSON (full GeoJSON support planned)
|
|
754
|
+
- LCZ processing structure ready but full shapefile loading pending GDAL fixes
|
|
755
|
+
- DEM visualization uses JavaScript libraries (GeoRasterLayer) for pixel access
|
|
756
|
+
|
|
757
|
+
### 📋 Planned Features
|
|
758
|
+
|
|
759
|
+
**Short-term:**
|
|
760
|
+
|
|
761
|
+
- Complete GDAL integration for Shapefile I/O
|
|
762
|
+
- Full GeoJSON export with proper layer management
|
|
763
|
+
- Full raster reprojection with resampling options
|
|
764
|
+
- LCZ shapefile loading from zip URLs
|
|
765
|
+
- Full DEM pixel data access in WASM
|
|
766
|
+
|
|
767
|
+
**Long-term:**
|
|
768
|
+
|
|
769
|
+
- Additional geometric operations
|
|
770
|
+
- More IGN API services
|
|
771
|
+
- Performance optimizations
|
|
772
|
+
- Additional output formats
|
|
773
|
+
- Enhanced error handling and validation
|
|
774
|
+
- 3D visualization capabilities
|
|
775
|
+
|
|
776
|
+
## 🧪 Testing
|
|
777
|
+
|
|
778
|
+
### Rust Tests
|
|
779
|
+
|
|
780
|
+
Run Rust unit tests:
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
cd rsmdu
|
|
784
|
+
cargo test
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Python Tests
|
|
788
|
+
|
|
789
|
+
Run Python tests:
|
|
790
|
+
|
|
791
|
+
```bash
|
|
792
|
+
cd pymdurs
|
|
793
|
+
pytest tests/
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Or manually test the Python bindings:
|
|
797
|
+
|
|
798
|
+
```bash
|
|
799
|
+
cd pymdurs
|
|
800
|
+
python -c "import pymdurs; print('✅ pymdurs imported successfully')"
|
|
801
|
+
python -c "import pymdurs; print('Available classes:', [x for x in dir(pymdurs.geometric) if not x.startswith('_')])"
|
|
802
|
+
python -c "import pymdurs; lidar = pymdurs.geometric.Lidar(output_path='./test'); print('✅ Lidar class works')"
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### WebAssembly Tests
|
|
806
|
+
|
|
807
|
+
```bash
|
|
808
|
+
cd rsmdu-wasm
|
|
809
|
+
wasm-pack test --headless --firefox
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
## 🤝 Contributing
|
|
813
|
+
|
|
814
|
+
Contributions are welcome! This project follows standard Rust and Python best practices.
|
|
815
|
+
|
|
816
|
+
### Development Setup
|
|
817
|
+
|
|
818
|
+
1. **Clone the repository:**
|
|
819
|
+
|
|
820
|
+
```bash
|
|
821
|
+
git clone https://github.com/rupeelab17/pymdurs.git
|
|
822
|
+
cd pymdurs
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
2. **Set up Rust development:**
|
|
826
|
+
|
|
827
|
+
**Note**: If you haven't installed Rust yet, see the [Installing Rust](#installing-rust) section above.
|
|
828
|
+
|
|
829
|
+
```bash
|
|
830
|
+
cd rsmdu
|
|
831
|
+
cargo build
|
|
832
|
+
cargo test
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
3. **Set up Python development:** See [Python Package](#python-package) for platform-specific build instructions.
|
|
836
|
+
|
|
837
|
+
4. **Set up WebAssembly development:**
|
|
838
|
+
```bash
|
|
839
|
+
cd rsmdu-wasm
|
|
840
|
+
rustup target add wasm32-unknown-unknown
|
|
841
|
+
cargo install wasm-pack
|
|
842
|
+
./build.sh
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### Contribution Guidelines
|
|
846
|
+
|
|
847
|
+
- Follow Rust naming conventions and style
|
|
848
|
+
- Add tests for new features
|
|
849
|
+
- Update documentation (README.md) for significant changes
|
|
850
|
+
- Ensure all examples still work after changes
|
|
851
|
+
- Test all three deployment targets (Rust, Python, WASM)
|
|
852
|
+
- Use feature flags for WASM-incompatible code
|
|
853
|
+
|
|
854
|
+
### Reporting Issues
|
|
855
|
+
|
|
856
|
+
Please use GitHub Issues to report bugs or request features. Include:
|
|
857
|
+
|
|
858
|
+
- Description of the issue
|
|
859
|
+
- Steps to reproduce
|
|
860
|
+
- Expected vs actual behavior
|
|
861
|
+
- Environment details (OS, Rust version, Python version, browser for WASM)
|
|
862
|
+
|
|
863
|
+
## 📄 License
|
|
864
|
+
|
|
865
|
+
GPL-3.0 (same as [pymdu](https://github.com/rupeelab17/pymdu))
|
|
866
|
+
|
|
867
|
+
## ⚡ Performance
|
|
868
|
+
|
|
869
|
+
This Rust implementation provides significant performance improvements over the original Python version:
|
|
870
|
+
|
|
871
|
+
- **Memory efficiency**: Rust's ownership system prevents memory leaks
|
|
872
|
+
- **Concurrency**: Safe parallel processing capabilities
|
|
873
|
+
- **Speed**: Native compilation provides faster execution
|
|
874
|
+
- **Type safety**: Compile-time error checking reduces runtime errors
|
|
875
|
+
- **WebAssembly**: Near-native performance in the browser
|
|
876
|
+
|
|
877
|
+
## 🔗 Related Projects
|
|
878
|
+
|
|
879
|
+
- [pymdu](https://github.com/rupeelab17/pymdu): Original Python implementation
|
|
880
|
+
- [GeoRust](https://georust.org/): Rust geospatial ecosystem
|
|
881
|
+
- [IGN Géoplateforme](https://geoservices.ign.fr/): French geospatial data services
|
|
882
|
+
- [PyO3](https://pyo3.rs/): Rust bindings for Python
|
|
883
|
+
- [Maturin](https://github.com/PyO3/maturin): Build tool for Python packages with Rust extensions
|
|
884
|
+
- [wasm-pack](https://rustwasm.github.io/wasm-pack/): Build tool for WebAssembly packages
|
|
885
|
+
|
|
886
|
+
## 📝 Version
|
|
887
|
+
|
|
888
|
+
Current version: **0.1.1** (Alpha)
|
|
889
|
+
|
|
890
|
+
This is an early release. The API may change in future versions. See the [Status](#-status) section for implementation details.
|
|
891
|
+
|
|
892
|
+
## 📚 Documentation
|
|
893
|
+
|
|
894
|
+
- **Rust API**: Run `cargo doc --open` in the `rsmdu` directory
|
|
895
|
+
- **Python API**: See `pymdurs/README.md`
|
|
896
|
+
- **WebAssembly API**: See `rsmdu-wasm/README.md`
|
|
897
|
+
- **Examples**: See [examples/README.md](examples/README.md), `rsmdu/examples/`, and `rsmdu-wasm/examples/`
|
|
898
|
+
|