We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
EuroSAT Dataset URL not being secure anymore
### Description

As `url = "https://madm.dfki.de/files/sentinel/EuroSATallBands.zip"` is not secure anymore, downloading the EuroSat dataset is blocked by default by certify.

We might need to either change the zip source or remove the `https`

### Steps to reproduce

Load the EuroSAT dataset
```python
from torchgeo.datasets import EuroSAT
eurosat_train = EuroSAT(download=True)
```

### Version

0.4.0

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torchgeo/datasets/eurosat.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 
4 """EuroSAT dataset."""
5 
6 import os
7 from typing import Callable, Dict, Optional, Sequence, cast
8 
9 import matplotlib.pyplot as plt
10 import numpy as np
11 import torch
12 from torch import Tensor
13 
14 from .geo import NonGeoClassificationDataset
15 from .utils import check_integrity, download_url, extract_archive, rasterio_loader
16 
17 
18 class EuroSAT(NonGeoClassificationDataset):
19     """EuroSAT dataset.
20 
21     The `EuroSAT <https://github.com/phelber/EuroSAT>`__ dataset is based on Sentinel-2
22     satellite images covering 13 spectral bands and consists of 10 target classes with
23     a total of 27,000 labeled and geo-referenced images.
24 
25     Dataset format:
26 
27     * rasters are 13-channel GeoTiffs
28     * labels are values in the range [0,9]
29 
30     Dataset classes:
31 
32     * Industrial Buildings
33     * Residential Buildings
34     * Annual Crop
35     * Permanent Crop
36     * River
37     * Sea and Lake
38     * Herbaceous Vegetation
39     * Highway
40     * Pasture
41     * Forest
42 
43     This dataset uses the train/val/test splits defined in the "In-domain representation
44     learning for remote sensing" paper:
45 
46     * https://arxiv.org/abs/1911.06721
47 
48     If you use this dataset in your research, please cite the following papers:
49 
50     * https://ieeexplore.ieee.org/document/8736785
51     * https://ieeexplore.ieee.org/document/8519248
52     """
53 
54     url = "https://madm.dfki.de/files/sentinel/EuroSATallBands.zip"  # 2.0 GB download
55     filename = "EuroSATallBands.zip"
56     md5 = "5ac12b3b2557aa56e1826e981e8e200e"
57 
58     # For some reason the class directories are actually nested in this directory
59     base_dir = os.path.join(
60         "ds", "images", "remote_sensing", "otherDatasets", "sentinel_2", "tif"
61     )
62 
63     splits = ["train", "val", "test"]
64     split_urls = {
65         "train": "https://storage.googleapis.com/remote_sensing_representations/eurosat-train.txt",  # noqa: E501
66         "val": "https://storage.googleapis.com/remote_sensing_representations/eurosat-val.txt",  # noqa: E501
67         "test": "https://storage.googleapis.com/remote_sensing_representations/eurosat-test.txt",  # noqa: E501
68     }
69     split_md5s = {
70         "train": "908f142e73d6acdf3f482c5e80d851b1",
71         "val": "95de90f2aa998f70a3b2416bfe0687b4",
72         "test": "7ae5ab94471417b6e315763121e67c5f",
73     }
74     classes = [
75         "Industrial Buildings",
76         "Residential Buildings",
77         "Annual Crop",
78         "Permanent Crop",
79         "River",
80         "Sea and Lake",
81         "Herbaceous Vegetation",
82         "Highway",
83         "Pasture",
84         "Forest",
85     ]
86 
87     all_band_names = (
88         "B01",
89         "B02",
90         "B03",
91         "B04",
92         "B05",
93         "B06",
94         "B07",
95         "B08",
96         "B08A",
97         "B09",
98         "B10",
99         "B11",
100         "B12",
101     )
102 
103     rgb_bands = ("B04", "B03", "B02")
104 
105     BAND_SETS = {"all": all_band_names, "rgb": rgb_bands}
106 
107     def __init__(
108         self,
109         root: str = "data",
110         split: str = "train",
111         bands: Sequence[str] = BAND_SETS["all"],
112         transforms: Optional[Callable[[Dict[str, Tensor]], Dict[str, Tensor]]] = None,
113         download: bool = False,
114         checksum: bool = False,
115     ) -> None:
116         """Initialize a new EuroSAT dataset instance.
117 
118         Args:
119             root: root directory where dataset can be found
120             split: one of "train", "val", or "test"
121             bands: a sequence of band names to load
122             transforms: a function/transform that takes input sample and its target as
123                 entry and returns a transformed version
124             download: if True, download dataset and store it in the root directory
125             checksum: if True, check the MD5 of the downloaded files (may be slow)
126 
127         Raises:
128             AssertionError: if ``split`` argument is invalid
129             RuntimeError: if ``download=False`` and data is not found, or checksums
130                 don't match
131 
132         .. versionadded:: 0.3
133            The *bands* parameter.
134         """
135         self.root = root
136         self.transforms = transforms
137         self.download = download
138         self.checksum = checksum
139 
140         assert split in ["train", "val", "test"]
141 
142         self._validate_bands(bands)
143         self.bands = bands
144         self.band_indices = Tensor(
145             [self.all_band_names.index(b) for b in bands if b in self.all_band_names]
146         ).long()
147 
148         self._verify()
149 
150         valid_fns = set()
151         with open(os.path.join(self.root, f"eurosat-{split}.txt")) as f:
152             for fn in f:
153                 valid_fns.add(fn.strip().replace(".jpg", ".tif"))
154         is_in_split: Callable[[str], bool] = lambda x: os.path.basename(x) in valid_fns
155 
156         super().__init__(
157             root=os.path.join(root, self.base_dir),
158             transforms=transforms,
159             loader=rasterio_loader,
160             is_valid_file=is_in_split,
161         )
162 
163     def __getitem__(self, index: int) -> Dict[str, Tensor]:
164         """Return an index within the dataset.
165 
166         Args:
167             index: index to return
168         Returns:
169             data and label at that index
170         """
171         image, label = self._load_image(index)
172 
173         image = torch.index_select(image, dim=0, index=self.band_indices).float()
174         sample = {"image": image, "label": label}
175 
176         if self.transforms is not None:
177             sample = self.transforms(sample)
178 
179         return sample
180 
181     def _check_integrity(self) -> bool:
182         """Check integrity of dataset.
183 
184         Returns:
185             True if dataset files are found and/or MD5s match, else False
186         """
187         integrity: bool = check_integrity(
188             os.path.join(self.root, self.filename), self.md5 if self.checksum else None
189         )
190         return integrity
191 
192     def _verify(self) -> None:
193         """Verify the integrity of the dataset.
194 
195         Raises:
196             RuntimeError: if ``download=False`` but dataset is missing or checksum fails
197         """
198         # Check if the files already exist
199         filepath = os.path.join(self.root, self.base_dir)
200         if os.path.exists(filepath):
201             return
202 
203         # Check if zip file already exists (if so then extract)
204         if self._check_integrity():
205             self._extract()
206             return
207 
208         # Check if the user requested to download the dataset
209         if not self.download:
210             raise RuntimeError(
211                 "Dataset not found in `root` directory and `download=False`, "
212                 "either specify a different `root` directory or use `download=True` "
213                 "to automatically download the dataset."
214             )
215 
216         # Download and extract the dataset
217         self._download()
218         self._extract()
219 
220     def _download(self) -> None:
221         """Download the dataset."""
222         download_url(
223             self.url,
224             self.root,
225             filename=self.filename,
226             md5=self.md5 if self.checksum else None,
227         )
228         for split in self.splits:
229             download_url(
230                 self.split_urls[split],
231                 self.root,
232                 filename=f"eurosat-{split}.txt",
233                 md5=self.split_md5s[split] if self.checksum else None,
234             )
235 
236     def _extract(self) -> None:
237         """Extract the dataset."""
238         filepath = os.path.join(self.root, self.filename)
239         extract_archive(filepath)
240 
241     def _validate_bands(self, bands: Sequence[str]) -> None:
242         """Validate list of bands.
243 
244         Args:
245             bands: user-provided sequence of bands to load
246 
247         Raises:
248             AssertionError: if ``bands`` is not a sequence
249             ValueError: if an invalid band name is provided
250 
251         .. versionadded:: 0.3
252         """
253         assert isinstance(bands, Sequence), "'bands' must be a sequence"
254         for band in bands:
255             if band not in self.all_band_names:
256                 raise ValueError(f"'{band}' is an invalid band name.")
257 
258     def plot(
259         self,
260         sample: Dict[str, Tensor],
261         show_titles: bool = True,
262         suptitle: Optional[str] = None,
263     ) -> plt.Figure:
264         """Plot a sample from the dataset.
265 
266         Args:
267             sample: a sample returned by :meth:`NonGeoClassificationDataset.__getitem__`
268             show_titles: flag indicating whether to show titles above each panel
269             suptitle: optional string to use as a suptitle
270 
271         Returns:
272             a matplotlib Figure with the rendered sample
273 
274         Raises:
275             ValueError: if RGB bands are not found in dataset
276 
277         .. versionadded:: 0.2
278         """
279         rgb_indices = []
280         for band in self.rgb_bands:
281             if band in self.bands:
282                 rgb_indices.append(self.bands.index(band))
283             else:
284                 raise ValueError("Dataset doesn't contain some of the RGB bands")
285 
286         image = np.take(sample["image"].numpy(), indices=rgb_indices, axis=0)
287         image = np.rollaxis(image, 0, 3)
288         image = np.clip(image / 3000, 0, 1)
289 
290         label = cast(int, sample["label"].item())
291         label_class = self.classes[label]
292 
293         showing_predictions = "prediction" in sample
294         if showing_predictions:
295             prediction = cast(int, sample["prediction"].item())
296             prediction_class = self.classes[prediction]
297 
298         fig, ax = plt.subplots(figsize=(4, 4))
299         ax.imshow(image)
300         ax.axis("off")
301         if show_titles:
302             title = f"Label: {label_class}"
303             if showing_predictions:
304                 title += f"\nPrediction: {prediction_class}"
305             ax.set_title(title)
306 
307         if suptitle is not None:
308             plt.suptitle(suptitle)
309         return fig
310
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/torchgeo/datasets/eurosat.py b/torchgeo/datasets/eurosat.py
--- a/torchgeo/datasets/eurosat.py
+++ b/torchgeo/datasets/eurosat.py
@@ -51,7 +51,9 @@
     * https://ieeexplore.ieee.org/document/8519248
     """
 
-    url = "https://madm.dfki.de/files/sentinel/EuroSATallBands.zip"  # 2.0 GB download
+    # TODO: Change to https after https://github.com/phelber/EuroSAT/issues/10 is
+    # resolved
+    url = "http://madm.dfki.de/files/sentinel/EuroSATallBands.zip"  # 2.0 GB download
     filename = "EuroSATallBands.zip"
     md5 = "5ac12b3b2557aa56e1826e981e8e200e"
 
@@ -104,6 +106,8 @@
 
     BAND_SETS = {"all": all_band_names, "rgb": rgb_bands}
 
+    # TODO: reset checksum to False after https://github.com/phelber/EuroSAT/issues/10
+    # is resolved
     def __init__(
         self,
         root: str = "data",
@@ -111,7 +115,7 @@
         bands: Sequence[str] = BAND_SETS["all"],
         transforms: Optional[Callable[[Dict[str, Tensor]], Dict[str, Tensor]]] = None,
         download: bool = False,
-        checksum: bool = False,
+        checksum: bool = True,
     ) -> None:
         """Initialize a new EuroSAT dataset instance.

{"golden_diff": "diff --git a/torchgeo/datasets/eurosat.py b/torchgeo/datasets/eurosat.py\n--- a/torchgeo/datasets/eurosat.py\n+++ b/torchgeo/datasets/eurosat.py\n@@ -51,7 +51,9 @@\n     * https://ieeexplore.ieee.org/document/8519248\n     \"\"\"\n \n-    url = \"https://madm.dfki.de/files/sentinel/EuroSATallBands.zip\"  # 2.0 GB download\n+    # TODO: Change to https after https://github.com/phelber/EuroSAT/issues/10 is\n+    # resolved\n+    url = \"http://madm.dfki.de/files/sentinel/EuroSATallBands.zip\"  # 2.0 GB download\n     filename = \"EuroSATallBands.zip\"\n     md5 = \"5ac12b3b2557aa56e1826e981e8e200e\"\n \n@@ -104,6 +106,8 @@\n \n     BAND_SETS = {\"all\": all_band_names, \"rgb\": rgb_bands}\n \n+    # TODO: reset checksum to False after https://github.com/phelber/EuroSAT/issues/10\n+    # is resolved\n     def __init__(\n         self,\n         root: str = \"data\",\n@@ -111,7 +115,7 @@\n         bands: Sequence[str] = BAND_SETS[\"all\"],\n         transforms: Optional[Callable[[Dict[str, Tensor]], Dict[str, Tensor]]] = None,\n         download: bool = False,\n-        checksum: bool = False,\n+        checksum: bool = True,\n     ) -> None:\n         \"\"\"Initialize a new EuroSAT dataset instance.\n", "issue": "EuroSAT Dataset URL not being secure anymore\n### Description\r\n\r\nAs `url = \"https://madm.dfki.de/files/sentinel/EuroSATallBands.zip\"` is not secure anymore, downloading the EuroSat dataset is blocked by default by certify.\r\n\r\nWe might need to either change the zip source or remove the `https`\r\n\r\n### Steps to reproduce\r\n\r\nLoad the EuroSAT dataset\r\n```python\r\nfrom torchgeo.datasets import EuroSAT\r\neurosat_train = EuroSAT(download=True)\r\n```\r\n\r\n### Version\r\n\r\n0.4.0\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\n\"\"\"EuroSAT dataset.\"\"\"\n\nimport os\nfrom typing import Callable, Dict, Optional, Sequence, cast\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom .geo import NonGeoClassificationDataset\nfrom .utils import check_integrity, download_url, extract_archive, rasterio_loader\n\n\nclass EuroSAT(NonGeoClassificationDataset):\n    \"\"\"EuroSAT dataset.\n\n    The `EuroSAT <https://github.com/phelber/EuroSAT>`__ dataset is based on Sentinel-2\n    satellite images covering 13 spectral bands and consists of 10 target classes with\n    a total of 27,000 labeled and geo-referenced images.\n\n    Dataset format:\n\n    * rasters are 13-channel GeoTiffs\n    * labels are values in the range [0,9]\n\n    Dataset classes:\n\n    * Industrial Buildings\n    * Residential Buildings\n    * Annual Crop\n    * Permanent Crop\n    * River\n    * Sea and Lake\n    * Herbaceous Vegetation\n    * Highway\n    * Pasture\n    * Forest\n\n    This dataset uses the train/val/test splits defined in the \"In-domain representation\n    learning for remote sensing\" paper:\n\n    * https://arxiv.org/abs/1911.06721\n\n    If you use this dataset in your research, please cite the following papers:\n\n    * https://ieeexplore.ieee.org/document/8736785\n    * https://ieeexplore.ieee.org/document/8519248\n    \"\"\"\n\n    url = \"https://madm.dfki.de/files/sentinel/EuroSATallBands.zip\"  # 2.0 GB download\n    filename = \"EuroSATallBands.zip\"\n    md5 = \"5ac12b3b2557aa56e1826e981e8e200e\"\n\n    # For some reason the class directories are actually nested in this directory\n    base_dir = os.path.join(\n        \"ds\", \"images\", \"remote_sensing\", \"otherDatasets\", \"sentinel_2\", \"tif\"\n    )\n\n    splits = [\"train\", \"val\", \"test\"]\n    split_urls = {\n        \"train\": \"https://storage.googleapis.com/remote_sensing_representations/eurosat-train.txt\",  # noqa: E501\n        \"val\": \"https://storage.googleapis.com/remote_sensing_representations/eurosat-val.txt\",  # noqa: E501\n        \"test\": \"https://storage.googleapis.com/remote_sensing_representations/eurosat-test.txt\",  # noqa: E501\n    }\n    split_md5s = {\n        \"train\": \"908f142e73d6acdf3f482c5e80d851b1\",\n        \"val\": \"95de90f2aa998f70a3b2416bfe0687b4\",\n        \"test\": \"7ae5ab94471417b6e315763121e67c5f\",\n    }\n    classes = [\n        \"Industrial Buildings\",\n        \"Residential Buildings\",\n        \"Annual Crop\",\n        \"Permanent Crop\",\n        \"River\",\n        \"Sea and Lake\",\n        \"Herbaceous Vegetation\",\n        \"Highway\",\n        \"Pasture\",\n        \"Forest\",\n    ]\n\n    all_band_names = (\n        \"B01\",\n        \"B02\",\n        \"B03\",\n        \"B04\",\n        \"B05\",\n        \"B06\",\n        \"B07\",\n        \"B08\",\n        \"B08A\",\n        \"B09\",\n        \"B10\",\n        \"B11\",\n        \"B12\",\n    )\n\n    rgb_bands = (\"B04\", \"B03\", \"B02\")\n\n    BAND_SETS = {\"all\": all_band_names, \"rgb\": rgb_bands}\n\n    def __init__(\n        self,\n        root: str = \"data\",\n        split: str = \"train\",\n        bands: Sequence[str] = BAND_SETS[\"all\"],\n        transforms: Optional[Callable[[Dict[str, Tensor]], Dict[str, Tensor]]] = None,\n        download: bool = False,\n        checksum: bool = False,\n    ) -> None:\n        \"\"\"Initialize a new EuroSAT dataset instance.\n\n        Args:\n            root: root directory where dataset can be found\n            split: one of \"train\", \"val\", or \"test\"\n            bands: a sequence of band names to load\n            transforms: a function/transform that takes input sample and its target as\n                entry and returns a transformed version\n            download: if True, download dataset and store it in the root directory\n            checksum: if True, check the MD5 of the downloaded files (may be slow)\n\n        Raises:\n            AssertionError: if ``split`` argument is invalid\n            RuntimeError: if ``download=False`` and data is not found, or checksums\n                don't match\n\n        .. versionadded:: 0.3\n           The *bands* parameter.\n        \"\"\"\n        self.root = root\n        self.transforms = transforms\n        self.download = download\n        self.checksum = checksum\n\n        assert split in [\"train\", \"val\", \"test\"]\n\n        self._validate_bands(bands)\n        self.bands = bands\n        self.band_indices = Tensor(\n            [self.all_band_names.index(b) for b in bands if b in self.all_band_names]\n        ).long()\n\n        self._verify()\n\n        valid_fns = set()\n        with open(os.path.join(self.root, f\"eurosat-{split}.txt\")) as f:\n            for fn in f:\n                valid_fns.add(fn.strip().replace(\".jpg\", \".tif\"))\n        is_in_split: Callable[[str], bool] = lambda x: os.path.basename(x) in valid_fns\n\n        super().__init__(\n            root=os.path.join(root, self.base_dir),\n            transforms=transforms,\n            loader=rasterio_loader,\n            is_valid_file=is_in_split,\n        )\n\n    def __getitem__(self, index: int) -> Dict[str, Tensor]:\n        \"\"\"Return an index within the dataset.\n\n        Args:\n            index: index to return\n        Returns:\n            data and label at that index\n        \"\"\"\n        image, label = self._load_image(index)\n\n        image = torch.index_select(image, dim=0, index=self.band_indices).float()\n        sample = {\"image\": image, \"label\": label}\n\n        if self.transforms is not None:\n            sample = self.transforms(sample)\n\n        return sample\n\n    def _check_integrity(self) -> bool:\n        \"\"\"Check integrity of dataset.\n\n        Returns:\n            True if dataset files are found and/or MD5s match, else False\n        \"\"\"\n        integrity: bool = check_integrity(\n            os.path.join(self.root, self.filename), self.md5 if self.checksum else None\n        )\n        return integrity\n\n    def _verify(self) -> None:\n        \"\"\"Verify the integrity of the dataset.\n\n        Raises:\n            RuntimeError: if ``download=False`` but dataset is missing or checksum fails\n        \"\"\"\n        # Check if the files already exist\n        filepath = os.path.join(self.root, self.base_dir)\n        if os.path.exists(filepath):\n            return\n\n        # Check if zip file already exists (if so then extract)\n        if self._check_integrity():\n            self._extract()\n            return\n\n        # Check if the user requested to download the dataset\n        if not self.download:\n            raise RuntimeError(\n                \"Dataset not found in `root` directory and `download=False`, \"\n                \"either specify a different `root` directory or use `download=True` \"\n                \"to automatically download the dataset.\"\n            )\n\n        # Download and extract the dataset\n        self._download()\n        self._extract()\n\n    def _download(self) -> None:\n        \"\"\"Download the dataset.\"\"\"\n        download_url(\n            self.url,\n            self.root,\n            filename=self.filename,\n            md5=self.md5 if self.checksum else None,\n        )\n        for split in self.splits:\n            download_url(\n                self.split_urls[split],\n                self.root,\n                filename=f\"eurosat-{split}.txt\",\n                md5=self.split_md5s[split] if self.checksum else None,\n            )\n\n    def _extract(self) -> None:\n        \"\"\"Extract the dataset.\"\"\"\n        filepath = os.path.join(self.root, self.filename)\n        extract_archive(filepath)\n\n    def _validate_bands(self, bands: Sequence[str]) -> None:\n        \"\"\"Validate list of bands.\n\n        Args:\n            bands: user-provided sequence of bands to load\n\n        Raises:\n            AssertionError: if ``bands`` is not a sequence\n            ValueError: if an invalid band name is provided\n\n        .. versionadded:: 0.3\n        \"\"\"\n        assert isinstance(bands, Sequence), \"'bands' must be a sequence\"\n        for band in bands:\n            if band not in self.all_band_names:\n                raise ValueError(f\"'{band}' is an invalid band name.\")\n\n    def plot(\n        self,\n        sample: Dict[str, Tensor],\n        show_titles: bool = True,\n        suptitle: Optional[str] = None,\n    ) -> plt.Figure:\n        \"\"\"Plot a sample from the dataset.\n\n        Args:\n            sample: a sample returned by :meth:`NonGeoClassificationDataset.__getitem__`\n            show_titles: flag indicating whether to show titles above each panel\n            suptitle: optional string to use as a suptitle\n\n        Returns:\n            a matplotlib Figure with the rendered sample\n\n        Raises:\n            ValueError: if RGB bands are not found in dataset\n\n        .. versionadded:: 0.2\n        \"\"\"\n        rgb_indices = []\n        for band in self.rgb_bands:\n            if band in self.bands:\n                rgb_indices.append(self.bands.index(band))\n            else:\n                raise ValueError(\"Dataset doesn't contain some of the RGB bands\")\n\n        image = np.take(sample[\"image\"].numpy(), indices=rgb_indices, axis=0)\n        image = np.rollaxis(image, 0, 3)\n        image = np.clip(image / 3000, 0, 1)\n\n        label = cast(int, sample[\"label\"].item())\n        label_class = self.classes[label]\n\n        showing_predictions = \"prediction\" in sample\n        if showing_predictions:\n            prediction = cast(int, sample[\"prediction\"].item())\n            prediction_class = self.classes[prediction]\n\n        fig, ax = plt.subplots(figsize=(4, 4))\n        ax.imshow(image)\n        ax.axis(\"off\")\n        if show_titles:\n            title = f\"Label: {label_class}\"\n            if showing_predictions:\n                title += f\"\\nPrediction: {prediction_class}\"\n            ax.set_title(title)\n\n        if suptitle is not None:\n            plt.suptitle(suptitle)\n        return fig\n", "path": "torchgeo/datasets/eurosat.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\n\"\"\"EuroSAT dataset.\"\"\"\n\nimport os\nfrom typing import Callable, Dict, Optional, Sequence, cast\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom .geo import NonGeoClassificationDataset\nfrom .utils import check_integrity, download_url, extract_archive, rasterio_loader\n\n\nclass EuroSAT(NonGeoClassificationDataset):\n    \"\"\"EuroSAT dataset.\n\n    The `EuroSAT <https://github.com/phelber/EuroSAT>`__ dataset is based on Sentinel-2\n    satellite images covering 13 spectral bands and consists of 10 target classes with\n    a total of 27,000 labeled and geo-referenced images.\n\n    Dataset format:\n\n    * rasters are 13-channel GeoTiffs\n    * labels are values in the range [0,9]\n\n    Dataset classes:\n\n    * Industrial Buildings\n    * Residential Buildings\n    * Annual Crop\n    * Permanent Crop\n    * River\n    * Sea and Lake\n    * Herbaceous Vegetation\n    * Highway\n    * Pasture\n    * Forest\n\n    This dataset uses the train/val/test splits defined in the \"In-domain representation\n    learning for remote sensing\" paper:\n\n    * https://arxiv.org/abs/1911.06721\n\n    If you use this dataset in your research, please cite the following papers:\n\n    * https://ieeexplore.ieee.org/document/8736785\n    * https://ieeexplore.ieee.org/document/8519248\n    \"\"\"\n\n    # TODO: Change to https after https://github.com/phelber/EuroSAT/issues/10 is\n    # resolved\n    url = \"http://madm.dfki.de/files/sentinel/EuroSATallBands.zip\"  # 2.0 GB download\n    filename = \"EuroSATallBands.zip\"\n    md5 = \"5ac12b3b2557aa56e1826e981e8e200e\"\n\n    # For some reason the class directories are actually nested in this directory\n    base_dir = os.path.join(\n        \"ds\", \"images\", \"remote_sensing\", \"otherDatasets\", \"sentinel_2\", \"tif\"\n    )\n\n    splits = [\"train\", \"val\", \"test\"]\n    split_urls = {\n        \"train\": \"https://storage.googleapis.com/remote_sensing_representations/eurosat-train.txt\",  # noqa: E501\n        \"val\": \"https://storage.googleapis.com/remote_sensing_representations/eurosat-val.txt\",  # noqa: E501\n        \"test\": \"https://storage.googleapis.com/remote_sensing_representations/eurosat-test.txt\",  # noqa: E501\n    }\n    split_md5s = {\n        \"train\": \"908f142e73d6acdf3f482c5e80d851b1\",\n        \"val\": \"95de90f2aa998f70a3b2416bfe0687b4\",\n        \"test\": \"7ae5ab94471417b6e315763121e67c5f\",\n    }\n    classes = [\n        \"Industrial Buildings\",\n        \"Residential Buildings\",\n        \"Annual Crop\",\n        \"Permanent Crop\",\n        \"River\",\n        \"Sea and Lake\",\n        \"Herbaceous Vegetation\",\n        \"Highway\",\n        \"Pasture\",\n        \"Forest\",\n    ]\n\n    all_band_names = (\n        \"B01\",\n        \"B02\",\n        \"B03\",\n        \"B04\",\n        \"B05\",\n        \"B06\",\n        \"B07\",\n        \"B08\",\n        \"B08A\",\n        \"B09\",\n        \"B10\",\n        \"B11\",\n        \"B12\",\n    )\n\n    rgb_bands = (\"B04\", \"B03\", \"B02\")\n\n    BAND_SETS = {\"all\": all_band_names, \"rgb\": rgb_bands}\n\n    # TODO: reset checksum to False after https://github.com/phelber/EuroSAT/issues/10\n    # is resolved\n    def __init__(\n        self,\n        root: str = \"data\",\n        split: str = \"train\",\n        bands: Sequence[str] = BAND_SETS[\"all\"],\n        transforms: Optional[Callable[[Dict[str, Tensor]], Dict[str, Tensor]]] = None,\n        download: bool = False,\n        checksum: bool = True,\n    ) -> None:\n        \"\"\"Initialize a new EuroSAT dataset instance.\n\n        Args:\n            root: root directory where dataset can be found\n            split: one of \"train\", \"val\", or \"test\"\n            bands: a sequence of band names to load\n            transforms: a function/transform that takes input sample and its target as\n                entry and returns a transformed version\n            download: if True, download dataset and store it in the root directory\n            checksum: if True, check the MD5 of the downloaded files (may be slow)\n\n        Raises:\n            AssertionError: if ``split`` argument is invalid\n            RuntimeError: if ``download=False`` and data is not found, or checksums\n                don't match\n\n        .. versionadded:: 0.3\n           The *bands* parameter.\n        \"\"\"\n        self.root = root\n        self.transforms = transforms\n        self.download = download\n        self.checksum = checksum\n\n        assert split in [\"train\", \"val\", \"test\"]\n\n        self._validate_bands(bands)\n        self.bands = bands\n        self.band_indices = Tensor(\n            [self.all_band_names.index(b) for b in bands if b in self.all_band_names]\n        ).long()\n\n        self._verify()\n\n        valid_fns = set()\n        with open(os.path.join(self.root, f\"eurosat-{split}.txt\")) as f:\n            for fn in f:\n                valid_fns.add(fn.strip().replace(\".jpg\", \".tif\"))\n        is_in_split: Callable[[str], bool] = lambda x: os.path.basename(x) in valid_fns\n\n        super().__init__(\n            root=os.path.join(root, self.base_dir),\n            transforms=transforms,\n            loader=rasterio_loader,\n            is_valid_file=is_in_split,\n        )\n\n    def __getitem__(self, index: int) -> Dict[str, Tensor]:\n        \"\"\"Return an index within the dataset.\n\n        Args:\n            index: index to return\n        Returns:\n            data and label at that index\n        \"\"\"\n        image, label = self._load_image(index)\n\n        image = torch.index_select(image, dim=0, index=self.band_indices).float()\n        sample = {\"image\": image, \"label\": label}\n\n        if self.transforms is not None:\n            sample = self.transforms(sample)\n\n        return sample\n\n    def _check_integrity(self) -> bool:\n        \"\"\"Check integrity of dataset.\n\n        Returns:\n            True if dataset files are found and/or MD5s match, else False\n        \"\"\"\n        integrity: bool = check_integrity(\n            os.path.join(self.root, self.filename), self.md5 if self.checksum else None\n        )\n        return integrity\n\n    def _verify(self) -> None:\n        \"\"\"Verify the integrity of the dataset.\n\n        Raises:\n            RuntimeError: if ``download=False`` but dataset is missing or checksum fails\n        \"\"\"\n        # Check if the files already exist\n        filepath = os.path.join(self.root, self.base_dir)\n        if os.path.exists(filepath):\n            return\n\n        # Check if zip file already exists (if so then extract)\n        if self._check_integrity():\n            self._extract()\n            return\n\n        # Check if the user requested to download the dataset\n        if not self.download:\n            raise RuntimeError(\n                \"Dataset not found in `root` directory and `download=False`, \"\n                \"either specify a different `root` directory or use `download=True` \"\n                \"to automatically download the dataset.\"\n            )\n\n        # Download and extract the dataset\n        self._download()\n        self._extract()\n\n    def _download(self) -> None:\n        \"\"\"Download the dataset.\"\"\"\n        download_url(\n            self.url,\n            self.root,\n            filename=self.filename,\n            md5=self.md5 if self.checksum else None,\n        )\n        for split in self.splits:\n            download_url(\n                self.split_urls[split],\n                self.root,\n                filename=f\"eurosat-{split}.txt\",\n                md5=self.split_md5s[split] if self.checksum else None,\n            )\n\n    def _extract(self) -> None:\n        \"\"\"Extract the dataset.\"\"\"\n        filepath = os.path.join(self.root, self.filename)\n        extract_archive(filepath)\n\n    def _validate_bands(self, bands: Sequence[str]) -> None:\n        \"\"\"Validate list of bands.\n\n        Args:\n            bands: user-provided sequence of bands to load\n\n        Raises:\n            AssertionError: if ``bands`` is not a sequence\n            ValueError: if an invalid band name is provided\n\n        .. versionadded:: 0.3\n        \"\"\"\n        assert isinstance(bands, Sequence), \"'bands' must be a sequence\"\n        for band in bands:\n            if band not in self.all_band_names:\n                raise ValueError(f\"'{band}' is an invalid band name.\")\n\n    def plot(\n        self,\n        sample: Dict[str, Tensor],\n        show_titles: bool = True,\n        suptitle: Optional[str] = None,\n    ) -> plt.Figure:\n        \"\"\"Plot a sample from the dataset.\n\n        Args:\n            sample: a sample returned by :meth:`NonGeoClassificationDataset.__getitem__`\n            show_titles: flag indicating whether to show titles above each panel\n            suptitle: optional string to use as a suptitle\n\n        Returns:\n            a matplotlib Figure with the rendered sample\n\n        Raises:\n            ValueError: if RGB bands are not found in dataset\n\n        .. versionadded:: 0.2\n        \"\"\"\n        rgb_indices = []\n        for band in self.rgb_bands:\n            if band in self.bands:\n                rgb_indices.append(self.bands.index(band))\n            else:\n                raise ValueError(\"Dataset doesn't contain some of the RGB bands\")\n\n        image = np.take(sample[\"image\"].numpy(), indices=rgb_indices, axis=0)\n        image = np.rollaxis(image, 0, 3)\n        image = np.clip(image / 3000, 0, 1)\n\n        label = cast(int, sample[\"label\"].item())\n        label_class = self.classes[label]\n\n        showing_predictions = \"prediction\" in sample\n        if showing_predictions:\n            prediction = cast(int, sample[\"prediction\"].item())\n            prediction_class = self.classes[prediction]\n\n        fig, ax = plt.subplots(figsize=(4, 4))\n        ax.imshow(image)\n        ax.axis(\"off\")\n        if show_titles:\n            title = f\"Label: {label_class}\"\n            if showing_predictions:\n                title += f\"\\nPrediction: {prediction_class}\"\n            ax.set_title(title)\n\n        if suptitle is not None:\n            plt.suptitle(suptitle)\n        return fig\n", "path": "torchgeo/datasets/eurosat.py"}]}