Skip to content

Commit 65693c9

Browse files
authored
Merge pull request #49 from quantifyearth/mwd-filename-format-update
Change output filename to match other IUCN projects
2 parents 4889a15 + 3201f48 commit 65693c9

File tree

6 files changed

+74
-60
lines changed

6 files changed

+74
-60
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v2.1.0 (09/03/2026)
2+
3+
### Changed
4+
5+
* Change the output filename format to match those used in other IUCN processes.
6+
17
## v2.0.5 (18/02/2026)
28

39
### Added

aoh/_internal/speciesinfo.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,21 @@ def __init__(self, species_data_path: Path, crosswalk_path: Path) -> None:
4545

4646
@property
4747
def species_id(self) -> int:
48-
return self.species_info.id_no.values[0]
48+
return int(self.species_info.id_no.values[0])
4949

5050
@property
51-
def season(self) -> str:
52-
return self.species_info.season.values[0]
51+
def assessment_id(self) -> int | None:
52+
try:
53+
return int(self.species_info.assessment_id.values[0])
54+
except AttributeError:
55+
return None
56+
57+
@property
58+
def season(self) -> str | None:
59+
try:
60+
return self.species_info.season.values[0]
61+
except AttributeError:
62+
return None
5363

5464
@property
5565
def elevation_lower(self) -> int:
@@ -75,27 +85,23 @@ def raw_habitats(self) -> set[str]:
7585
self.manifest["error"] = "Species data missing or corrupt habitat data"
7686
raise ValueError(self.manifest["error"]) from exc
7787

78-
def filenames(self, output_directory_path:Path) -> tuple[Path,Path]:
79-
species_id = self.species_id
80-
try:
81-
seasonality = self.season
82-
result_filename = output_directory_path / f"{species_id}_{seasonality}.tif"
83-
manifest_filename = output_directory_path / f"{species_id}_{seasonality}.json"
84-
except AttributeError:
85-
seasonality = None
86-
result_filename = output_directory_path / f"{species_id}.tif"
87-
manifest_filename = output_directory_path / f"{species_id}.json"
88+
def filenames(self, output_directory_path: Path) -> tuple[Path,Path]:
89+
species_id_part = f"T{self.species_id}"
90+
assessment_id = self.assessment_id
91+
assessment_id_part = f"A{assessment_id}" if assessment_id is not None else ""
92+
93+
season = self.season
94+
seasonality_part = f"_{season}" if season is not None else ""
95+
96+
stem = f"aoh_{species_id_part}{assessment_id_part}{seasonality_part}"
97+
98+
result_filename = output_directory_path / f"{stem}.tif"
99+
manifest_filename = output_directory_path / f"{stem}.json"
88100

89101
return result_filename, manifest_filename
90102

91103
def save_manifest(self, output_directory_path:Path, error_message: str | None = None) -> None:
92-
species_id = self.species_id
93-
try:
94-
seasonality = self.season
95-
manifest_filename = output_directory_path / f"{species_id}_{seasonality}.json"
96-
except AttributeError:
97-
manifest_filename = output_directory_path / f"{species_id}.json"
98-
104+
_, manifest_filename = self.filenames(output_directory_path)
99105
if error_message is not None:
100106
self.manifest["error"] = error_message
101107

aoh/aohcalc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def main() -> None:
9494
parser.add_argument(
9595
'--output',
9696
type=Path,
97-
help='Directory where area geotiffs should be stored.',
97+
help='Directory where area geotiff and metadata should be stored.',
9898
required=True,
9999
dest='output_path',
100100
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "aoh"
7-
version = "2.0.5"
7+
version = "2.1.0"
88
description = "A library for calculating Area of Habitat for species distribution mapping"
99
authors = [
1010
{name = "Michael Dales", email = "mwd24@cam.ac.uk"}

tests/test_aoh_binary.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def generate_species_info(
9696
) -> None:
9797
properties = {
9898
"id_no": "1234",
99+
"assessment_id": "789",
99100
"season": "1",
100101
"elevation_lower": float(elevation_range[0]),
101102
"elevation_upper": float(elevation_range[1]),
@@ -153,9 +154,9 @@ def test_simple_aoh(force_habitat) -> None:
153154
force_habitat=force_habitat,
154155
)
155156

156-
expected_geotiff_path = output_dir / "1234_1.tif"
157+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
157158
assert expected_geotiff_path.exists()
158-
expected_manifest_path = output_dir / "1234_1.json"
159+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
159160
assert expected_manifest_path.exists()
160161

161162
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -224,9 +225,9 @@ def test_no_habitat_aoh(force_habitat) -> None:
224225
force_habitat=force_habitat,
225226
)
226227

227-
expected_geotiff_path = output_dir / "1234_1.tif"
228+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
228229
assert expected_geotiff_path.exists() == (not force_habitat)
229-
expected_manifest_path = output_dir / "1234_1.json"
230+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
230231
assert expected_manifest_path.exists()
231232

232233
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -293,9 +294,9 @@ def test_simple_aoh_weight(force_habitat) -> None:
293294
weight_layer_paths=[area_path],
294295
)
295296

296-
expected_geotiff_path = output_dir / "1234_1.tif"
297+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
297298
assert expected_geotiff_path.exists()
298-
expected_manifest_path = output_dir / "1234_1.json"
299+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
299300
assert expected_manifest_path.exists()
300301

301302
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -367,9 +368,9 @@ def test_simple_aoh_multiple_habitats(force_habitat) -> None:
367368
force_habitat=force_habitat,
368369
)
369370

370-
expected_geotiff_path = output_dir / "1234_1.tif"
371+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
371372
assert expected_geotiff_path.exists()
372-
expected_manifest_path = output_dir / "1234_1.json"
373+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
373374
assert expected_manifest_path.exists()
374375

375376
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -439,9 +440,9 @@ def test_no_overlapping_habitats(force_habitat) -> None:
439440
force_habitat=force_habitat,
440441
)
441442

442-
expected_geotiff_path = output_dir / "1234_1.tif"
443+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
443444
assert expected_geotiff_path.exists() == (not force_habitat)
444-
expected_manifest_path = output_dir / "1234_1.json"
445+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
445446
assert expected_manifest_path.exists()
446447

447448
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -502,9 +503,9 @@ def test_no_elevation_aoh(force_habitat) -> None:
502503
force_habitat=force_habitat,
503504
)
504505

505-
expected_geotiff_path = output_dir / "1234_1.tif"
506+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
506507
assert expected_geotiff_path.exists()
507-
expected_manifest_path = output_dir / "1234_1.json"
508+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
508509
assert expected_manifest_path.exists()
509510

510511
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -579,12 +580,12 @@ def test_simple_aoh_area() -> None:
579580
expected_area = yg.area_raster(("WGS84", (360/200, -180/200)))
580581

581582
with (
582-
yg.read_raster(output_dir_without_area / "1234_1.tif") as aoh_sans_area,
583-
yg.read_raster(output_dir_with_area / "1234_1.tif") as aoh_with_area,
583+
yg.read_raster(output_dir_without_area / "aoh_T1234A789_1.tif") as aoh_sans_area,
584+
yg.read_raster(output_dir_with_area / "aoh_T1234A789_1.tif") as aoh_with_area,
584585
):
585-
with open(output_dir_without_area / "1234_1.json", "r", encoding="UTF-8") as f:
586+
with open(output_dir_without_area / "aoh_T1234A789_1.json", "r", encoding="UTF-8") as f:
586587
sans_area_manifest = json.load(f)
587-
with open(output_dir_with_area / "1234_1.json", "r", encoding="UTF-8") as f:
588+
with open(output_dir_with_area / "aoh_T1234A789_1.json", "r", encoding="UTF-8") as f:
588589
with_area_manifest = json.load(f)
589590

590591
# Check calculated values. All habitat layers for this
@@ -661,10 +662,10 @@ def test_simple_aoh_area_and_weights() -> None:
661662
expected_area = yg.area_raster(("WGS84", (360/200, -180/200)))
662663

663664
with (
664-
yg.read_raster(output_dir_without_area / "1234_1.tif") as aoh_sans_area,
665-
yg.read_raster(output_dir_with_area / "1234_1.tif") as aoh_with_area,
665+
yg.read_raster(output_dir_without_area / "aoh_T1234A789_1.tif") as aoh_sans_area,
666+
yg.read_raster(output_dir_with_area / "aoh_T1234A789_1.tif") as aoh_with_area,
666667
):
667-
with open(output_dir_with_area / "1234_1.json", "r", encoding="UTF-8") as f:
668+
with open(output_dir_with_area / "aoh_T1234A789_1.json", "r", encoding="UTF-8") as f:
668669
with_area_manifest = json.load(f)
669670

670671
manual_version_total = (aoh_sans_area * expected_area * 2).sum()

tests/test_aoh_fractional.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def generate_species_info(
9696
) -> None:
9797
properties = {
9898
"id_no": "1234",
99+
"assessment_id": "789",
99100
"season": "1",
100101
"elevation_lower": float(elevation_range[0]),
101102
"elevation_upper": float(elevation_range[1]),
@@ -156,9 +157,9 @@ def test_simple_aoh(force_habitat) -> None:
156157
force_habitat=force_habitat,
157158
)
158159

159-
expected_geotiff_path = output_dir / "1234_1.tif"
160+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
160161
assert expected_geotiff_path.exists()
161-
expected_manifest_path = output_dir / "1234_1.json"
162+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
162163
assert expected_manifest_path.exists()
163164

164165
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -227,9 +228,9 @@ def test_no_habitat_aoh(force_habitat) -> None:
227228
force_habitat=force_habitat,
228229
)
229230

230-
expected_geotiff_path = output_dir / "1234_1.tif"
231+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
231232
assert expected_geotiff_path.exists() == (not force_habitat)
232-
expected_manifest_path = output_dir / "1234_1.json"
233+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
233234
assert expected_manifest_path.exists()
234235

235236
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -299,9 +300,9 @@ def test_simple_aoh_weights(force_habitat) -> None:
299300
force_habitat=force_habitat,
300301
)
301302

302-
expected_geotiff_path = output_dir / "1234_1.tif"
303+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
303304
assert expected_geotiff_path.exists()
304-
expected_manifest_path = output_dir / "1234_1.json"
305+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
305306
assert expected_manifest_path.exists()
306307

307308
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -372,9 +373,9 @@ def test_simple_aoh_multiple_habitats(force_habitat) -> None:
372373
force_habitat=force_habitat,
373374
)
374375

375-
expected_geotiff_path = output_dir / "1234_1.tif"
376+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
376377
assert expected_geotiff_path.exists()
377-
expected_manifest_path = output_dir / "1234_1.json"
378+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
378379
assert expected_manifest_path.exists()
379380

380381
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -445,9 +446,9 @@ def test_no_overlapping_habitats(force_habitat) -> None:
445446
force_habitat=force_habitat,
446447
)
447448

448-
expected_geotiff_path = output_dir / "1234_1.tif"
449+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
449450
assert expected_geotiff_path.exists() == (not force_habitat)
450-
expected_manifest_path = output_dir / "1234_1.json"
451+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
451452
assert expected_manifest_path.exists()
452453

453454
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -511,9 +512,9 @@ def test_no_elevation_aoh(force_habitat) -> None:
511512
force_habitat=force_habitat,
512513
)
513514

514-
expected_geotiff_path = output_dir / "1234_1.tif"
515+
expected_geotiff_path = output_dir / "aoh_T1234A789_1.tif"
515516
assert expected_geotiff_path.exists()
516-
expected_manifest_path = output_dir / "1234_1.json"
517+
expected_manifest_path = output_dir / "aoh_T1234A789_1.json"
517518
assert expected_manifest_path.exists()
518519

519520
with open(expected_manifest_path, "r", encoding="UTF-8") as f:
@@ -588,12 +589,12 @@ def test_simple_aoh_area() -> None:
588589
expected_area = yg.area_raster(("WGS84", (360/200, -180/200)))
589590

590591
with (
591-
yg.read_raster(output_dir_without_area / "1234_1.tif") as aoh_sans_area,
592-
yg.read_raster(output_dir_with_area / "1234_1.tif") as aoh_with_area,
592+
yg.read_raster(output_dir_without_area / "aoh_T1234A789_1.tif") as aoh_sans_area,
593+
yg.read_raster(output_dir_with_area / "aoh_T1234A789_1.tif") as aoh_with_area,
593594
):
594-
with open(output_dir_without_area / "1234_1.json", "r", encoding="UTF-8") as f:
595+
with open(output_dir_without_area / "aoh_T1234A789_1.json", "r", encoding="UTF-8") as f:
595596
sans_area_manifest = json.load(f)
596-
with open(output_dir_with_area / "1234_1.json", "r", encoding="UTF-8") as f:
597+
with open(output_dir_with_area / "aoh_T1234A789_1.json", "r", encoding="UTF-8") as f:
597598
with_area_manifest = json.load(f)
598599

599600
# Check calculated values. All habitat layers for this
@@ -671,10 +672,10 @@ def test_simple_aoh_area_and_weights() -> None:
671672
expected_area = yg.area_raster(("WGS84", (360/200, -180/200)))
672673

673674
with (
674-
yg.read_raster(output_dir_without_area / "1234_1.tif") as aoh_sans_area,
675-
yg.read_raster(output_dir_with_area / "1234_1.tif") as aoh_with_area,
675+
yg.read_raster(output_dir_without_area / "aoh_T1234A789_1.tif") as aoh_sans_area,
676+
yg.read_raster(output_dir_with_area / "aoh_T1234A789_1.tif") as aoh_with_area,
676677
):
677-
with open(output_dir_with_area / "1234_1.json", "r", encoding="UTF-8") as f:
678+
with open(output_dir_with_area / "aoh_T1234A789_1.json", "r", encoding="UTF-8") as f:
678679
with_area_manifest = json.load(f)
679680

680681
manual_version_total = (aoh_sans_area * expected_area * 2).sum()

0 commit comments

Comments
 (0)