demetrius 0.1.0a1__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.
Files changed (37) hide show
  1. demetrius-0.1.0a1/LICENSE +21 -0
  2. demetrius-0.1.0a1/PKG-INFO +550 -0
  3. demetrius-0.1.0a1/README.md +511 -0
  4. demetrius-0.1.0a1/pyproject.toml +55 -0
  5. demetrius-0.1.0a1/setup.cfg +4 -0
  6. demetrius-0.1.0a1/src/demetrius/__init__.py +3 -0
  7. demetrius-0.1.0a1/src/demetrius/cli.py +471 -0
  8. demetrius-0.1.0a1/src/demetrius/clipper.py +129 -0
  9. demetrius-0.1.0a1/src/demetrius/cog.py +80 -0
  10. demetrius-0.1.0a1/src/demetrius/coverage.py +118 -0
  11. demetrius-0.1.0a1/src/demetrius/crs.py +112 -0
  12. demetrius-0.1.0a1/src/demetrius/data/__init__.py +1 -0
  13. demetrius-0.1.0a1/src/demetrius/data/projections/__init__.py +1 -0
  14. demetrius-0.1.0a1/src/demetrius/downloader.py +180 -0
  15. demetrius-0.1.0a1/src/demetrius/elevation_converter.py +196 -0
  16. demetrius-0.1.0a1/src/demetrius/filtering.py +113 -0
  17. demetrius-0.1.0a1/src/demetrius/inspector.py +142 -0
  18. demetrius-0.1.0a1/src/demetrius/manifest.py +175 -0
  19. demetrius-0.1.0a1/src/demetrius/merger.py +213 -0
  20. demetrius-0.1.0a1/src/demetrius/models.py +325 -0
  21. demetrius-0.1.0a1/src/demetrius/mosaicker.py +145 -0
  22. demetrius-0.1.0a1/src/demetrius/parser.py +90 -0
  23. demetrius-0.1.0a1/src/demetrius/priority.py +101 -0
  24. demetrius-0.1.0a1/src/demetrius/project_boundaries.py +246 -0
  25. demetrius-0.1.0a1/src/demetrius/projections.py +44 -0
  26. demetrius-0.1.0a1/src/demetrius/reprojector.py +104 -0
  27. demetrius-0.1.0a1/src/demetrius/snapper.py +289 -0
  28. demetrius-0.1.0a1/src/demetrius/sources.py +35 -0
  29. demetrius-0.1.0a1/src/demetrius/tnm.py +231 -0
  30. demetrius-0.1.0a1/src/demetrius.egg-info/PKG-INFO +550 -0
  31. demetrius-0.1.0a1/src/demetrius.egg-info/SOURCES.txt +35 -0
  32. demetrius-0.1.0a1/src/demetrius.egg-info/dependency_links.txt +1 -0
  33. demetrius-0.1.0a1/src/demetrius.egg-info/entry_points.txt +2 -0
  34. demetrius-0.1.0a1/src/demetrius.egg-info/requires.txt +13 -0
  35. demetrius-0.1.0a1/src/demetrius.egg-info/top_level.txt +1 -0
  36. demetrius-0.1.0a1/tests/test_core.py +373 -0
  37. demetrius-0.1.0a1/tests/test_integration.py +315 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WSP-Internal-Repos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,550 @@
1
+ Metadata-Version: 2.4
2
+ Name: demetrius
3
+ Version: 0.1.0a1
4
+ Summary: High-resolution DEM assembly from USGS 3DEP data for engineering applications
5
+ Author-email: Thomas Williams <thomas.williams@wsp.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/wsp-watertech/demetrius
8
+ Project-URL: Repository, https://github.com/wsp-watertech/demetrius
9
+ Project-URL: Issues, https://github.com/wsp-watertech/demetrius/issues
10
+ Keywords: dem,usgs,3dep,elevation,hydraulic-modeling,gis
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Scientific/Engineering :: GIS
23
+ Requires-Python: >=3.12
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: click>=8.0
27
+ Requires-Dist: geopandas>=1.1.4
28
+ Requires-Dist: httpx>=0.28.1
29
+ Requires-Dist: pydantic>=2.0
30
+ Requires-Dist: pyarrow>=15.0
31
+ Requires-Dist: pyproj>=3.6
32
+ Requires-Dist: shapely>=2.0
33
+ Requires-Dist: gdal>=3.8
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
37
+ Requires-Dist: ruff>=0.1; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # demetrius
41
+
42
+ High-resolution DEM assembly from USGS 3DEP data for engineering applications.
43
+
44
+ ```bash
45
+ demetrius process --aoi watershed.shp --project-bounds boundaries.gpkg --output dem.tif
46
+ ```
47
+
48
+ **demetrius** is a Python library and CLI tool that:
49
+ - Accepts a polygon Area of Interest (AOI)
50
+ - Queries the USGS TNM Access API for 3DEP 1m DEM tiles
51
+ - Selects the most recent available data
52
+ - Downloads only required tiles
53
+ - Mosaics them into a single Cloud-Optimized GeoTIFF (COG)
54
+
55
+ Perfect for hydraulic modeling workflows where data integrity and reproducibility are critical.
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install demetrius
61
+ ```
62
+
63
+ Or install from source for development:
64
+
65
+ ```bash
66
+ git clone https://github.com/wsp-watertech/demetrius.git
67
+ cd demetrius
68
+ pip install -e ".[dev]"
69
+ ```
70
+
71
+ **Installation with Conda (recommended):**
72
+ ```bash
73
+ conda create -n demetrius python=3.12 gdal
74
+ conda activate demetrius
75
+ pip install demetrius
76
+ ```
77
+
78
+ ### System Requirements
79
+
80
+ - **Python**: 3.12 or higher
81
+ - **GDAL**: Command-line tools (`gdalbuildvrt`, `gdalwarp`, `gdal_translate`)
82
+
83
+ ## Quick Start
84
+
85
+ ### Basic Usage
86
+
87
+ Process an AOI polygon and generate a DEM:
88
+
89
+ ```bash
90
+ demetrius process --aoi site.shp --output dem.tif --project-bounds boundaries.gpkg
91
+ ```
92
+
93
+ Required arguments:
94
+ - `--aoi`: Path to AOI geometry (shapefile, GeoJSON, or GeoPackage)
95
+ - `--output`: Output raster path
96
+ - `--project-bounds`: Path to project boundaries (shapefile, GeoPackage, etc.)
97
+
98
+ ### Preview Tiles (No Download)
99
+
100
+ Inspect what tiles would be used without downloading:
101
+
102
+ ```bash
103
+ demetrius inspect --aoi site.shp --project-bounds boundaries.gpkg
104
+ ```
105
+
106
+ Output:
107
+ ```
108
+ ======================================================================
109
+ demetrius Inspection Report
110
+ ======================================================================
111
+
112
+ Area of Interest:
113
+ Bounds: (-74.4500, 40.0500) → (-74.4100, 40.0900)
114
+ Buffer distance: 0 m
115
+
116
+ Tiles Discovered:
117
+ Total: 3 tile(s)
118
+ Datasets: 1
119
+
120
+ Dataset Priority Order (newest first):
121
+ [0] PA_3_County_South_Central_2018_D18
122
+ Tiles: 3
123
+ Publication dates: 2018-06-15 → 2018-06-15
124
+
125
+ Coverage Estimate:
126
+ AOI area: 0.00 sq degrees
127
+ Tile coverage: 0.01 sq degrees
128
+ Coverage: 100.0%
129
+ ======================================================================
130
+ ```
131
+
132
+ ## Using demetrius as a Python Library
133
+
134
+ Beyond the CLI, you can use demetrius directly in Python scripts:
135
+
136
+ ### Basic Example
137
+
138
+ ```python
139
+ from pathlib import Path
140
+ from demetrius.models import AOI
141
+ from demetrius.project_boundaries import ProjectBoundaries
142
+ from demetrius.tnm import TNMTileSource
143
+ from demetrius.filtering import filter_tiles_by_aoi
144
+ from demetrius.priority import prioritize_datasets
145
+ from demetrius.downloader import TileDownloader
146
+ from demetrius.mosaicker import VRTMosaicker
147
+ from demetrius.merger import DatasetMerger
148
+ from demetrius.reprojector import Reprojector
149
+ from demetrius.snapper import Snapper
150
+ from demetrius.clipper import Clipper
151
+ from demetrius.elevation_converter import ElevationConverter
152
+ from demetrius.cog import COGGenerator
153
+
154
+ # Load AOI and project boundaries
155
+ aoi = AOI.from_file("site.shp")
156
+ proj_bounds = ProjectBoundaries.from_file("boundaries.gpkg")
157
+
158
+ # Query TNM for tiles
159
+ source = TNMTileSource()
160
+ tiles = source.search(aoi.bounds())
161
+
162
+ # Filter and prioritize
163
+ tiles = filter_tiles_by_aoi(tiles, aoi, proj_bounds)
164
+ tiles = prioritize_datasets(tiles)
165
+
166
+ # Download
167
+ downloader = TileDownloader()
168
+ for tile in tiles:
169
+ downloader.download_tile(tile)
170
+
171
+ # Mosaic & merge datasets
172
+ mosaicker = VRTMosaicker()
173
+ mosaic = mosaicker.create_mosaic_vrt(tiles)
174
+
175
+ merger = DatasetMerger()
176
+ merged = merger.merge(mosaic)
177
+
178
+ # Reproject & snap
179
+ reprojector = Reprojector()
180
+ snapper = Snapper()
181
+ output_crs = "EPSG:32111"
182
+ cellsize = 1.0
183
+
184
+ reprojected = reprojector.reproject(merged, output_crs, cellsize=cellsize)
185
+ snapped = snapper.snap(reprojected, cellsize=cellsize)
186
+
187
+ # Clip to AOI
188
+ clipper = Clipper()
189
+ clipped = clipper.clip(snapped, aoi)
190
+
191
+ # Convert units & generate COG
192
+ converter = ElevationConverter()
193
+ converted = converter.convert(clipped, "converted.tif", output_crs)
194
+
195
+ cog_gen = COGGenerator()
196
+ cog_gen.generate(converted, "dem.tif")
197
+ ```
198
+
199
+ ### Accessing Cached Data
200
+
201
+ Downloaded tiles are cached in `~/.demetrius/`:
202
+
203
+ ```python
204
+ from demetrius.downloader import TileDownloader
205
+
206
+ # Get cache directory
207
+ cache_dir = TileDownloader.get_default_data_dir()
208
+ print(f"Tiles cached in: {cache_dir}")
209
+
210
+ # List downloaded tiles
211
+ for tile_file in cache_dir.glob("**/*.tif"):
212
+ print(tile_file)
213
+ ```
214
+
215
+ ### Inspect Workflow Without Downloading
216
+
217
+ ```python
218
+ from demetrius.inspector import InspectionReport
219
+
220
+ # Preview tiles that would be downloaded
221
+ report = InspectionReport.from_aoi_and_bounds("site.shp", "boundaries.gpkg")
222
+ print(report)
223
+ # Shows tiles, datasets, coverage %
224
+ ```
225
+
226
+ ### Working with Manifests
227
+
228
+ Manifests track tile provenance for reproducibility:
229
+
230
+ ```python
231
+ from demetrius.manifest import Manifest
232
+
233
+ # Load manifest from previous run
234
+ manifest = Manifest.load("dem.tif.manifest.json")
235
+
236
+ # Access tile information
237
+ print(f"Tiles used: {len(manifest.tiles)}")
238
+ print(f"Buffer distance: {manifest.buffer} m")
239
+ print(f"Output CRS: {manifest.output_crs}")
240
+
241
+ # Inspect original AOI geometry
242
+ print(manifest.aoi.bounds())
243
+ ```
244
+
245
+ ## Processing Modes
246
+
247
+ ### Full Pipeline (default)
248
+ Runs complete workflow: discover → download → mosaic → reproject → clip → COG
249
+
250
+ ```bash
251
+ demetrius process --aoi site.shp --output dem.tif
252
+ ```
253
+
254
+ ### Download Only
255
+ Stop after downloading tiles; saves manifest for later processing
256
+
257
+ ```bash
258
+ demetrius process --aoi site.shp --mode download-only
259
+ ```
260
+
261
+ Outputs: `dem.tif.manifest.json` and tiles in `~/.demetrius/`
262
+
263
+ ### Process Only
264
+ Use previously downloaded tiles; skip TNM query and download
265
+
266
+ ```bash
267
+ demetrius process --aoi site.shp --mode process-only
268
+ ```
269
+
270
+ Requires manifest from previous `download-only` run (named `{output}.manifest.json`).
271
+
272
+ ### Batch Processing Multiple Areas
273
+ Process multiple polygons from a vector file by looping through features. No new functionality needed—just iterate and process each polygon separately:
274
+
275
+ ```python
276
+ import geopandas as gpd
277
+ from src.demetrius.cli import process
278
+ from pathlib import Path
279
+
280
+ # Load polygons from GeoPackage, Shapefile, GeoJSON, etc.
281
+ polygons = gpd.read_file("study_areas.gpkg")
282
+
283
+ output_dir = Path("./output_dems")
284
+ output_dir.mkdir(exist_ok=True)
285
+
286
+ for idx, row in polygons.iterrows():
287
+ aoi_name = row.get("name", f"area_{idx}")
288
+
289
+ # Create temporary GeoJSON for this feature
290
+ feature_path = output_dir / f"{aoi_name}_aoi.geojson"
291
+ gpd.GeoDataFrame([row], crs=polygons.crs).to_file(feature_path, driver="GeoJSON")
292
+
293
+ # Process this feature
294
+ output_dem = output_dir / f"{aoi_name}_dem.tif"
295
+ process(
296
+ aoi=str(feature_path),
297
+ output=str(output_dem),
298
+ output_crs="EPSG:32111",
299
+ buffer=500,
300
+ cellsize=1.0
301
+ )
302
+
303
+ # Manifest saved as {output}.manifest.json for reproducibility
304
+ print(f"✓ Generated {output_dem} with metadata in {output_dem}.manifest.json")
305
+
306
+ # Clean up temporary feature file
307
+ feature_path.unlink()
308
+ ```
309
+
310
+ Each DEM gets its own manifest file (`{name}_dem.tif.manifest.json`) recording tiles, buffer, cellsize, and other parameters for reproducibility.
311
+
312
+ ## Options
313
+
314
+ ```
315
+ --aoi PATH Path to AOI (shapefile, GeoJSON, GeoPackage)
316
+ --output PATH Output file path [default: dem.tif]
317
+ --output-crs EPSG:CODE Target CRS (e.g., EPSG:32618) [default: auto-detect]
318
+ --buffer METERS Buffer for tile discovery and clipping [default: 0]
319
+ --cellsize FLOAT Output cellsize in target CRS units [default: 1m converted to CRS units]
320
+ --no-snap Disable grid snapping [default: enabled]
321
+ --require-full-coverage Fail if AOI not fully covered [default: True]
322
+ --mode {full,download-only,process-only}
323
+ Processing mode [default: full]
324
+ ```
325
+
326
+ ### Default Cellsize and Grid Snapping
327
+
328
+ By default, DEMs are generated with:
329
+ - **Cellsize**: 1 meter, automatically converted to output CRS units
330
+ - UTM zones (meters): 1.0 meter
331
+ - State Plane (feet): 3.28083... feet
332
+ - Other units: automatic conversion
333
+ - **Grid snapping**: Enabled, aligning to multiples of the cellsize
334
+
335
+ This means all DEMs (even with different CRS selections) have equivalent 1-meter resolution in their native units. The actual pixel size appears different in different CRS (3.28... feet vs 1m) but represents the same geographic resolution.
336
+
337
+ To override:
338
+ - `--cellsize VALUE`: Use explicit cellsize instead of 1m default
339
+ - `--no-snap`: Disable grid snapping (rarely needed)
340
+
341
+ Both cellsize resampling and grid snapping use the same computed value for consistent alignment.
342
+
343
+ ## AOI Geometry Formats
344
+
345
+ - **Shapefiles**: `.shp` + `.shx` + `.dbf`
346
+ - **GeoJSON**: `.geojson`
347
+ - **GeoPackage**: `.gpkg`
348
+ - **GIS formats**: Any format readable by geopandas (WKT, etc.)
349
+
350
+ Multi-feature files automatically unionize to single geometry.
351
+
352
+ ## Output
353
+
354
+ ### Cloud-Optimized GeoTIFF (COG)
355
+
356
+ Final output is COG-compliant GeoTIFF with:
357
+ - Internal tiling (512x512)
358
+ - Overviews for efficient zoom
359
+ - DEFLATE compression
360
+ - Valid NODATA values
361
+ - Georeferencing in output CRS
362
+
363
+ ### Manifest
364
+
365
+ JSON file recording all inputs and selected tiles for reproducibility:
366
+
367
+ ```json
368
+ {
369
+ "aoi": {
370
+ "bounds": {"min_x": -74.45, "min_y": 40.05, "max_x": -74.41, "max_y": 40.09},
371
+ "crs": "EPSG:4326"
372
+ },
373
+ "buffer": 1000,
374
+ "cellsize": 10.5,
375
+ "tile_count": 3,
376
+ "tiles": [
377
+ {
378
+ "dataset_id": "PA_3_County_South_Central_2018_D18",
379
+ "tile_id": "x38y448",
380
+ "priority": 0,
381
+ "url": "https://...",
382
+ "bounds": {...},
383
+ "local_path": "/home/user/.demetrius/dataset_PA_3/tile_x38y448.tif"
384
+ },
385
+ ...
386
+ ]
387
+ }
388
+ ```
389
+
390
+ Note: `cellsize` is only included if specified; otherwise the field is omitted.
391
+
392
+ ## How It Works
393
+
394
+ ### 1. AOI Preparation
395
+
396
+ Original geometry is buffered by `--buffer` distance for tile discovery AND for final clipping.
397
+
398
+ The buffer is applied in the output CRS coordinate space to ensure accurate meter-based (or relevant units) buffering. This means:
399
+ - If no `--output-crs` is specified, it's auto-detected based on initial unbuffered tile discovery
400
+ - The buffered geometry is then used for both tile filtering and final clip operations
401
+
402
+ ### 1b. Output Resolution (Cellsize)
403
+
404
+ If `--cellsize` is specified, the final DEM will be resampled to that resolution during reprojection. The cellsize units are in the target CRS units (e.g., meters for UTM zones, feet for State Plane feet zones).
405
+
406
+ If `--cellsize` is not specified, native tile resolution is preserved (typically 1 meter for USGS 3DEP 1m DEM).
407
+
408
+ ### 2. TNM Query
409
+
410
+ Tiles are discovered using [USGS TNM Access API](https://tnmaccess.nationalmap.gov/):
411
+
412
+ ```
413
+ https://tnmaccess.nationalmap.gov/api/v1/products
414
+ ?bbox=minx,miny,maxx,maxy
415
+ &datasets=Digital Elevation Model (DEM) 1 meter
416
+ &prodFormats=GeoTIFF
417
+ ```
418
+
419
+ ### 3. Tile Selection
420
+
421
+ **Filtering**: Tiles are filtered to those intersecting the buffered AOI.
422
+
423
+ **Prioritization**: Datasets are ranked by `publication_date` (newest first). When the same tile appears in multiple datasets, only the newest version is kept.
424
+
425
+ **Coverage Validation**: Original (unbuffered) AOI must be fully covered. Fails with `--require-full-coverage=true`.
426
+
427
+ ### 4. Parallel Download
428
+
429
+ Tiles are downloaded in parallel (configurable worker pool) with automatic retry on failure.
430
+
431
+ ### 5. Mosaicking
432
+
433
+ Tiles are merged using GDAL Virtual Raster (VRT) for efficiency:
434
+
435
+ ```bash
436
+ gdalbuildvrt dataset_priority_0.vrt tile1.tif tile2.tif ...
437
+ ```
438
+
439
+ No in-memory raster loading; efficient for thousands of tiles.
440
+
441
+ ### 6. Multi-CRS Handling
442
+
443
+ If tiles span multiple UTM zones, each zone is mosaicked separately, then reprojected to common zone.
444
+
445
+ ### 7. Reprojection
446
+
447
+ All tiles reprojected to target CRS (auto-detected or user-specified), with optional resampling to specified cellsize:
448
+
449
+ ```bash
450
+ gdalwarp -t_srs EPSG:32618 -r bilinear -multi ...
451
+ # With cellsize:
452
+ gdalwarp -t_srs EPSG:32618 -tr 10.0 10.0 -r bilinear -multi ...
453
+ ```
454
+
455
+ - **Resampling**: Minimum bilinear (nearest neighbor forbidden)
456
+ - **Multi-threaded**: Uses all available CPU cores
457
+ - **Cellsize**: Optional target resolution (applied via `-tr` flag)
458
+
459
+ ### 8. Clipping
460
+
461
+ Final raster clipped to original AOI using shapely geometry:
462
+
463
+ ```bash
464
+ gdalwarp -cutline aoi.geojson -crop_to_cutline ...
465
+ ```
466
+
467
+ ### 9. Grid Snapping (Optional, Default Enabled)
468
+
469
+ If snapping is enabled (default), the raster is snapped to a regular grid where all pixel boundaries are exact multiples of the snap distance:
470
+
471
+ ```bash
472
+ # Snapping distance = 1m converted to output CRS units
473
+ gdalwarp -te <snapped_minx> <snapped_miny> <snapped_maxx> <snapped_maxy> ...
474
+ ```
475
+
476
+ **CRS-Aware Snapping:**
477
+ - **Meters (UTM/projected)**: Snap distance is 1.0 meter
478
+ - **US Survey Feet (State Plane)**: Snap distance is 3.28083... feet
479
+ - **Other units**: Automatically converted from 1 meter
480
+
481
+ This ensures:
482
+ - Pixel boundaries align to multiples of snap distance in the output CRS
483
+ - Consistent reproducible output across runs
484
+ - Compatibility with downstream grid-based processing
485
+ - CRS-native coordinate precision
486
+
487
+ Snapping can be disabled with `--no-snap` or overridden with explicit `--cellsize`.
488
+
489
+ ### 10. COG Output
490
+
491
+ Result converted to Cloud-Optimized GeoTIFF:
492
+
493
+ ```bash
494
+ gdal_translate -of COG -co COMPRESS=DEFLATE ...
495
+ ```
496
+
497
+ ## Examples
498
+
499
+ ### Multi-county with Specific CRS
500
+
501
+ ```bash
502
+ demetrius process \
503
+ --aoi counties.geojson \
504
+ --output counties_dem.tif \
505
+ --output-crs EPSG:2272 \
506
+ --buffer 5000
507
+ ```
508
+
509
+ ### Resampled DEM with Specific Resolution
510
+
511
+ ```bash
512
+ demetrius process \
513
+ --aoi site.shp \
514
+ --output dem_10m.tif \
515
+ --cellsize 10.0
516
+ ```
517
+
518
+ With this command, the output DEM will be resampled to 10-meter resolution in the target CRS units, and snapped to multiples of 10.0.
519
+
520
+ ### Snapping with CRS-Specific Units
521
+
522
+ ```bash
523
+ # EPSG:32111 (meters) - snaps to multiples of 1.0 meter
524
+ demetrius process \
525
+ --aoi site.shp \
526
+ --output dem_utm.tif \
527
+ --output-crs EPSG:32111
528
+
529
+ # EPSG:2286 (US survey feet) - snaps to multiples of 3.28... feet
530
+ demetrius process \
531
+ --aoi site.shp \
532
+ --output dem_feet.tif \
533
+ --output-crs EPSG:2286
534
+ ```
535
+
536
+ Both commands snap to the equivalent of 1 meter, but in their respective CRS units. This ensures consistent, reproducible grid alignment regardless of CRS.
537
+
538
+ ### Two-stage processing (separate network/compute)
539
+
540
+ **Stage 1: Discovery & Download (on server with network)**
541
+ ```bash
542
+ demetrius process --aoi site.shp --mode download-only --output dem.tif --project-bounds boundaries.gpkg
543
+ # Output: dem.tif.manifest.json, ~/.demetrius/
544
+ ```
545
+
546
+ **Stage 2: Processing (offline or different machine)**
547
+ ```bash
548
+ # Copy dem.tif.manifest.json and tiles to processing machine
549
+ demetrius process --aoi site.shp --mode process-only --output dem.tif --project-bounds boundaries.gpkg
550
+ ```