Skip to content

Commit a027434

Browse files
authored
Add Annual NLCD dataset (#2387)
* Add Annual NLCD dataset * Add fake test data * Removed unused import * Run ruff check * Improve _verrify function * Run ruff check * Update citation
1 parent 8f71d5e commit a027434

7 files changed

+67
-71
lines changed

tests/data/nlcd/data.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import hashlib
77
import os
8-
import shutil
98

109
import numpy as np
1110
import rasterio
@@ -16,8 +15,6 @@
1615

1716
np.random.seed(0)
1817

19-
dir = 'nlcd_{}_land_cover_l48_20210604'
20-
2118
years = [2011, 2019]
2219

2320
wkt = """
@@ -67,21 +64,12 @@ def create_file(path: str, dtype: str) -> None:
6764

6865
if __name__ == '__main__':
6966
for year in years:
70-
year_dir = dir.format(year)
71-
# Remove old data
72-
if os.path.isdir(year_dir):
73-
shutil.rmtree(year_dir)
74-
75-
os.makedirs(os.path.join(os.getcwd(), year_dir))
76-
77-
zip_filename = year_dir + '.zip'
78-
filename = year_dir + '.img'
79-
create_file(os.path.join(year_dir, filename), dtype='int8')
80-
81-
# Compress data
82-
shutil.make_archive(year_dir, 'zip', '.', year_dir)
67+
filename = os.path.join(
68+
'tests', 'data', 'nlcd', 'Annual_NLCD_LndCov_{}_CU_C1V0.tif'
69+
).format(year)
70+
create_file(filename, dtype='int8')
8371

8472
# Compute checksums
85-
with open(zip_filename, 'rb') as f:
73+
with open(filename, 'rb') as f:
8674
md5 = hashlib.md5(f.read()).hexdigest()
87-
print(f'{zip_filename}: {md5}')
75+
print(f'{filename}: {md5}')
Binary file not shown.
Binary file not shown.

tests/datasets/test_nlcd.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,12 @@ class TestNLCD:
2525
@pytest.fixture
2626
def dataset(self, monkeypatch: MonkeyPatch, tmp_path: Path) -> NLCD:
2727
md5s = {
28-
2011: '99546a3b89a0dddbe4e28e661c79984e',
29-
2019: 'a4008746f15720b8908ddd357a75fded',
28+
2011: '3346297a3cb53c9bd1c7e03b2e6e2d74',
29+
2019: 'a307cdaa1add9dae05efe02fec4c33bb',
3030
}
3131
monkeypatch.setattr(NLCD, 'md5s', md5s)
3232

33-
url = os.path.join(
34-
'tests', 'data', 'nlcd', 'nlcd_{}_land_cover_l48_20210604.zip'
35-
)
33+
url = os.path.join('tests', 'data', 'nlcd', 'Annual_NLCD_LndCov_{}_CU_C1V0.tif')
3634
monkeypatch.setattr(NLCD, 'url', url)
3735
monkeypatch.setattr(plt, 'show', lambda *args: None)
3836
root = tmp_path
@@ -75,7 +73,7 @@ def test_already_extracted(self, dataset: NLCD) -> None:
7573

7674
def test_already_downloaded(self, tmp_path: Path) -> None:
7775
pathname = os.path.join(
78-
'tests', 'data', 'nlcd', 'nlcd_2019_land_cover_l48_20210604.zip'
76+
'tests', 'data', 'nlcd', 'Annual_NLCD_LndCov_2019_CU_C1V0.tif'
7977
)
8078
root = tmp_path
8179
shutil.copy(pathname, root)
@@ -86,7 +84,7 @@ def test_invalid_year(self, tmp_path: Path) -> None:
8684
AssertionError,
8785
match='NLCD data product only exists for the following years:',
8886
):
89-
NLCD(tmp_path, years=[1996])
87+
NLCD(tmp_path, years=[1984])
9088

9189
def test_invalid_classes(self) -> None:
9290
with pytest.raises(AssertionError):

torchgeo/datasets/nlcd.py

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
"""NLCD dataset."""
55

6-
import glob
76
import os
87
from collections.abc import Callable, Iterable
98
from typing import Any, ClassVar
@@ -15,20 +14,18 @@
1514

1615
from .errors import DatasetNotFoundError
1716
from .geo import RasterDataset
18-
from .utils import BoundingBox, Path, download_url, extract_archive
17+
from .utils import BoundingBox, Path, download_url
1918

2019

2120
class NLCD(RasterDataset):
22-
"""National Land Cover Database (NLCD) dataset.
21+
"""Annual National Land Cover Database (NLCD) dataset.
2322
24-
The `NLCD dataset
25-
<https://www.usgs.gov/centers/eros/science/national-land-cover-database>`_
26-
is a land cover product that covers the United States and Puerto Rico. The current
27-
implementation supports maps for the continental United States only. The product is
28-
a joint effort between the United States Geological Survey
23+
The `Annual NLCD products
24+
<https://www.usgs.gov/centers/eros/science/annual-national-land-cover-database>`_
25+
is an annual land cover product for the conterminous U.S. initially covering the period
26+
from 1985 to 2023. The product is a joint effort between the United States Geological Survey
2927
(`USGS <https://www.usgs.gov/>`_) and the Multi-Resolution Land Characteristics
30-
Consortium (`MRLC <https://www.mrlc.gov/>`_) which released the first product
31-
in 2001 with new updates every five years since then.
28+
Consortium (`MRLC <https://www.mrlc.gov/>`_).
3229
3330
The dataset contains the following 17 classes:
3431
@@ -57,33 +54,60 @@ class NLCD(RasterDataset):
5754
5855
* single channel .img file with integer class labels
5956
60-
If you use this dataset in your research, please use the corresponding citation:
57+
If you use this dataset in your research, please cite the following paper:
6158
62-
* 2001: https://doi.org/10.5066/P9MZGHLF
63-
* 2006: https://doi.org/10.5066/P9HBR9V3
64-
* 2011: https://doi.org/10.5066/P97S2IID
65-
* 2016: https://doi.org/10.5066/P96HHBIE
66-
* 2019: https://doi.org/10.5066/P9KZCM54
59+
* https://doi.org/10.5066/P94UXNTS
6760
6861
.. versionadded:: 0.5
6962
"""
7063

71-
filename_glob = 'nlcd_*_land_cover_l48_*.img'
72-
filename_regex = (
73-
r'nlcd_(?P<date>\d{4})_land_cover_l48_(?P<publication_date>\d{8})\.img'
74-
)
75-
zipfile_glob = 'nlcd_*_land_cover_l48_*.zip'
64+
filename_glob = 'Annual_NLCD_LndCov_*_CU_C1V0.tif'
65+
filename_regex = r'Annual_NLCD_LndCov_(?P<date>\d{4})_CU_C1V0\.tif'
7666
date_format = '%Y'
7767
is_image = False
7868

79-
url = 'https://s3-us-west-2.amazonaws.com/mrlc/nlcd_{}_land_cover_l48_20210604.zip'
69+
url = 'https://s3-us-west-2.amazonaws.com/mrlc/Annual_NLCD_LndCov_{}_CU_C1V0.tif'
8070

8171
md5s: ClassVar[dict[int, str]] = {
82-
2001: '538166a4d783204764e3df3b221fc4cd',
83-
2006: '67454e7874a00294adb9442374d0c309',
84-
2011: 'ea524c835d173658eeb6fa3c8e6b917b',
85-
2016: '452726f6e3bd3f70d8ca2476723d238a',
86-
2019: '82851c3f8105763b01c83b4a9e6f3961',
72+
1985: 'a2e1c5f0b34e9b15a63a9dc10e8d3ec2',
73+
1986: 'da1d08ca51ac43abc14711c8d6139f1d',
74+
1987: '2cb85e8f077c227605cd7bac62a72a75',
75+
1988: 'b20fb987cc30926d2d125d045e02626d',
76+
1989: 'dbe851cbea34d0a57c2a94eb745a1267',
77+
1990: '1927e0e040b9ff513ff039749b64919b',
78+
1991: 'eca73474843d6c58693eba62d70e507c',
79+
1992: '8beda41ba79000f55a8e9358ba3fa5a4',
80+
1993: '1a023552967cdac1111e9968ea62c879',
81+
1994: 'acc30ce4f6cdd78af5f7887d17ac4de3',
82+
1995: 'f728e8fc231b2e8e74a14201f500543a',
83+
1996: 'd2580904244f89b20d6258150fbf4161',
84+
1997: 'fec4e08032e162f2cc7dbe019d042609',
85+
1998: '87ea19434de96ea99cd5d7991042816c',
86+
1999: 'd4133737f20e75f3bd3a5baa32a668da',
87+
2000: 'e20b61bb2e7f4034a33c9fd536798a01',
88+
2001: 'b1f46ace9aedd17a89efab489cb67bc3',
89+
2002: '57bf60d7cd473096af3bb125391bde63',
90+
2003: '5e346854da9abf739152e85fee4c7aff',
91+
2004: '13136f271f53a454358eb7ec12bda686',
92+
2005: 'f00b66b57a23eb49a077e88704964a91',
93+
2006: '074ba90de5e62a37a5f001b7572f6baa',
94+
2007: 'cdef29a191cf165baaae80857ce5a980',
95+
2008: 'da907c76a1f12739333148504fd111c9',
96+
2009: '47890b306b875e681990b3db0c709da3',
97+
2010: '9a81f405f9e2f45d581078afd53c2d4b',
98+
2011: '13f4ef40b204aa1108dc0599d9546701',
99+
2012: '66b33146f9a9d9491be10c59c51e3e33',
100+
2013: 'f8d230f7dea493c47fbc74984ff856cc',
101+
2014: '68eb07ce86c1f7c2546ec43c2f9f7029',
102+
2015: 'f5a1b59fe54a70752f544c06cb965be4',
103+
2016: 'f0c2e74824fc281a57821e28e2c7fe6e',
104+
2017: 'a0aa8be0ed7d637f0f88f26d3742b20e',
105+
2018: 'a01f31547837ff1dfec1aba07b89bbec',
106+
2019: 'fa738201cddc1393dac4383b6ce2561a',
107+
2020: 'aa8f51690c7b01f3b3b413be9a7c36d6',
108+
2021: '47fc1794a64704a918b6ad586df4267c',
109+
2022: '11359748229e138cde971947864104a4',
110+
2023: '498ff8a512d32fe905720796fdb7fd52',
87111
}
88112

89113
cmap: ClassVar[dict[int, tuple[int, int, int, int]]] = {
@@ -111,7 +135,7 @@ def __init__(
111135
paths: Path | Iterable[Path] = 'data',
112136
crs: CRS | None = None,
113137
res: float | None = None,
114-
years: list[int] = [2019],
138+
years: list[int] = [2023],
115139
classes: list[int] = list(cmap.keys()),
116140
transforms: Callable[[dict[str, Any]], dict[str, Any]] | None = None,
117141
cache: bool = True,
@@ -183,19 +207,14 @@ def __getitem__(self, query: BoundingBox) -> dict[str, Any]:
183207

184208
def _verify(self) -> None:
185209
"""Verify the integrity of the dataset."""
186-
# Check if the extracted files already exist
187-
if self.files:
188-
return
189-
190-
# Check if the zip files have already been downloaded
210+
# Check if the TIFF files for the specified years have already been downloaded
191211
exists = []
192212
for year in self.years:
193-
zipfile_year = self.zipfile_glob.replace('*', str(year), 1)
213+
filename_year = self.filename_glob.replace('*', str(year), 1)
194214
assert isinstance(self.paths, str | os.PathLike)
195-
pathname = os.path.join(self.paths, '**', zipfile_year)
196-
if glob.glob(pathname, recursive=True):
215+
pathname = os.path.join(self.paths, filename_year)
216+
if os.path.exists(pathname):
197217
exists.append(True)
198-
self._extract()
199218
else:
200219
exists.append(False)
201220

@@ -208,7 +227,6 @@ def _verify(self) -> None:
208227

209228
# Download the dataset
210229
self._download()
211-
self._extract()
212230

213231
def _download(self) -> None:
214232
"""Download the dataset."""
@@ -219,14 +237,6 @@ def _download(self) -> None:
219237
md5=self.md5s[year] if self.checksum else None,
220238
)
221239

222-
def _extract(self) -> None:
223-
"""Extract the dataset."""
224-
for year in self.years:
225-
zipfile_name = self.zipfile_glob.replace('*', str(year), 1)
226-
assert isinstance(self.paths, str | os.PathLike)
227-
pathname = os.path.join(self.paths, '**', zipfile_name)
228-
extract_archive(glob.glob(pathname, recursive=True)[0], self.paths)
229-
230240
def plot(
231241
self,
232242
sample: dict[str, Any],

0 commit comments

Comments
 (0)