We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Samplers don't work in parallel
When I try to use RandomGeoSampler with `num_workers` >= 1, I get the following error:
```
Traceback (most recent call last):
  File "/anaconda/envs/azureml_py38/lib/python3.9/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/anaconda/envs/azureml_py38/lib/python3.9/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/anaconda/envs/azureml_py38/lib/python3.9/site-packages/torch/utils/data/_utils/worker.py", line 260, in _worker_loop
    r = index_queue.get(timeout=MP_STATUS_CHECK_INTERVAL)
  File "/anaconda/envs/azureml_py38/lib/python3.9/multiprocessing/queues.py", line 122, in get
    return _ForkingPickler.loads(res)
TypeError: __new__() missing 5 required positional arguments: 'maxx', 'miny', 'maxy', 'mint', and 'maxt'
```

--- 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/utils.py`
Content:
```
1 """Common dataset utilities."""
2 
3 import bz2
4 import contextlib
5 import gzip
6 import lzma
7 import os
8 import tarfile
9 import zipfile
10 from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
11 
12 import torch
13 from torch import Tensor
14 from torchvision.datasets.utils import check_integrity, download_url
15 
16 __all__ = (
17     "check_integrity",
18     "download_url",
19     "download_and_extract_archive",
20     "extract_archive",
21     "BoundingBox",
22     "working_dir",
23     "collate_dict",
24 )
25 
26 
27 class _rarfile:
28     class RarFile:
29         def __init__(self, *args: Any, **kwargs: Any) -> None:
30             self.args = args
31             self.kwargs = kwargs
32 
33         def __enter__(self) -> Any:
34             try:
35                 import rarfile
36             except ImportError:
37                 raise ImportError(
38                     "rarfile is not installed and is required to extract this dataset"
39                 )
40 
41             # TODO: catch exception for when rarfile is installed but not
42             # unrar/unar/bsdtar
43             return rarfile.RarFile(*self.args, **self.kwargs)
44 
45         def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None:
46             pass
47 
48 
49 def extract_archive(src: str, dst: Optional[str] = None) -> None:
50     """Extract an archive.
51 
52     Args:
53         src: file to be extracted
54         dst: directory to extract to (defaults to dirname of ``src``)
55 
56     Raises:
57         RuntimeError: if src file has unknown archival/compression scheme
58     """
59     if dst is None:
60         dst = os.path.dirname(src)
61 
62     suffix_and_extractor: List[Tuple[Union[str, Tuple[str, ...]], Any]] = [
63         (".rar", _rarfile.RarFile),
64         (
65             (".tar", ".tar.gz", ".tar.bz2", ".tar.xz", ".tgz", ".tbz2", ".tbz", ".txz"),
66             tarfile.open,
67         ),
68         (".zip", zipfile.ZipFile),
69     ]
70 
71     for suffix, extractor in suffix_and_extractor:
72         if src.endswith(suffix):
73             with extractor(src, "r") as f:
74                 f.extractall(dst)
75             return
76 
77     suffix_and_decompressor: List[Tuple[str, Any]] = [
78         (".bz2", bz2.open),
79         (".gz", gzip.open),
80         (".xz", lzma.open),
81     ]
82 
83     for suffix, decompressor in suffix_and_decompressor:
84         if src.endswith(suffix):
85             dst = os.path.join(dst, os.path.basename(src).replace(suffix, ""))
86             with decompressor(src, "rb") as sf, open(dst, "wb") as df:
87                 df.write(sf.read())
88             return
89 
90     raise RuntimeError("src file has unknown archival/compression scheme")
91 
92 
93 def download_and_extract_archive(
94     url: str,
95     download_root: str,
96     extract_root: Optional[str] = None,
97     filename: Optional[str] = None,
98     md5: Optional[str] = None,
99 ) -> None:
100     """Download and extract an archive.
101 
102     Args:
103         url: URL to download
104         download_root: directory to download to
105         extract_root: directory to extract to (defaults to ``download_root``)
106         filename: download filename (defaults to basename of ``url``)
107         md5: checksum for download verification
108     """
109     download_root = os.path.expanduser(download_root)
110     if extract_root is None:
111         extract_root = download_root
112     if not filename:
113         filename = os.path.basename(url)
114 
115     download_url(url, download_root, filename, md5)
116 
117     archive = os.path.join(download_root, filename)
118     print("Extracting {} to {}".format(archive, extract_root))
119     extract_archive(archive, extract_root)
120 
121 
122 def download_radiant_mlhub(
123     dataset_id: str, download_root: str, api_key: Optional[str] = None
124 ) -> None:
125     """Download a dataset from Radiant Earth.
126 
127     Args:
128         dataset_id: the ID of the dataset to fetch
129         download_root: directory to download to
130         api_key: the API key to use for all requests from the session. Can also be
131             passed in via the ``MLHUB_API_KEY`` environment variable, or configured in
132             ``~/.mlhub/profiles``.
133     """
134     try:
135         import radiant_mlhub
136     except ImportError:
137         raise ImportError(
138             "radiant_mlhub is not installed and is required to download this dataset"
139         )
140 
141     dataset = radiant_mlhub.Dataset.fetch(dataset_id, api_key=api_key)
142     dataset.download(output_dir=download_root, api_key=api_key)
143 
144 
145 class BoundingBox(Tuple[float, float, float, float, float, float]):
146     """Data class for indexing spatiotemporal data.
147 
148     Attributes:
149         minx (float): western boundary
150         maxx (float): eastern boundary
151         miny (float): southern boundary
152         maxy (float): northern boundary
153         mint (float): earliest boundary
154         maxt (float): latest boundary
155     """
156 
157     def __new__(
158         cls,
159         minx: float,
160         maxx: float,
161         miny: float,
162         maxy: float,
163         mint: float,
164         maxt: float,
165     ) -> "BoundingBox":
166         """Create a new instance of BoundingBox.
167 
168         Args:
169             minx: western boundary
170             maxx: eastern boundary
171             miny: southern boundary
172             maxy: northern boundary
173             mint: earliest boundary
174             maxt: latest boundary
175 
176         Raises:
177             ValueError: if bounding box is invalid
178                 (minx > maxx, miny > maxy, or mint > maxt)
179         """
180         if minx > maxx:
181             raise ValueError(f"Bounding box is invalid: 'minx={minx}' > 'maxx={maxx}'")
182         if miny > maxy:
183             raise ValueError(f"Bounding box is invalid: 'miny={miny}' > 'maxy={maxy}'")
184         if mint > maxt:
185             raise ValueError(f"Bounding box is invalid: 'mint={mint}' > 'maxt={maxt}'")
186 
187         # Using super() doesn't work with mypy, see:
188         # https://stackoverflow.com/q/60611012/5828163
189         return tuple.__new__(cls, [minx, maxx, miny, maxy, mint, maxt])
190 
191     def __init__(
192         self,
193         minx: float,
194         maxx: float,
195         miny: float,
196         maxy: float,
197         mint: float,
198         maxt: float,
199     ) -> None:
200         """Initialize a new instance of BoundingBox.
201 
202         Args:
203             minx: western boundary
204             maxx: eastern boundary
205             miny: southern boundary
206             maxy: northern boundary
207             mint: earliest boundary
208             maxt: latest boundary
209         """
210         self.minx = minx
211         self.maxx = maxx
212         self.miny = miny
213         self.maxy = maxy
214         self.mint = mint
215         self.maxt = maxt
216 
217     def __repr__(self) -> str:
218         """Return the formal string representation of the object.
219 
220         Returns:
221             formal string representation
222         """
223         return (
224             f"{self.__class__.__name__}(minx={self.minx}, maxx={self.maxx}, "
225             f"miny={self.miny}, maxy={self.maxy}, mint={self.mint}, maxt={self.maxt})"
226         )
227 
228     def intersects(self, other: "BoundingBox") -> bool:
229         """Whether or not two bounding boxes intersect.
230 
231         Args:
232             other: another bounding box
233 
234         Returns:
235             True if bounding boxes intersect, else False
236         """
237         return (
238             self.minx <= other.maxx
239             and self.maxx >= other.minx
240             and self.miny <= other.maxy
241             and self.maxy >= other.miny
242             and self.mint <= other.maxt
243             and self.maxt >= other.mint
244         )
245 
246 
247 @contextlib.contextmanager
248 def working_dir(dirname: str, create: bool = False) -> Iterator[None]:
249     """Context manager for changing directories.
250 
251     Args:
252         dirname: directory to temporarily change to
253         create: if True, create the destination directory
254     """
255     if create:
256         os.makedirs(dirname, exist_ok=True)
257 
258     cwd = os.getcwd()
259     os.chdir(dirname)
260 
261     try:
262         yield
263     finally:
264         os.chdir(cwd)
265 
266 
267 def collate_dict(samples: List[Dict[str, Any]]) -> Dict[str, Any]:
268     """Merge a list of samples to form a mini-batch of Tensors.
269 
270     Args:
271         samples: list of samples
272 
273     Returns:
274         a single sample
275     """
276     collated = {}
277     for key, value in samples[0].items():
278         if isinstance(value, Tensor):
279             collated[key] = torch.stack([sample[key] for sample in samples])
280         else:
281             collated[key] = [
282                 sample[key] for sample in samples
283             ]  # type: ignore[assignment]
284     return collated
285
```
--- 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/utils.py b/torchgeo/datasets/utils.py
--- a/torchgeo/datasets/utils.py
+++ b/torchgeo/datasets/utils.py
@@ -214,6 +214,14 @@
         self.mint = mint
         self.maxt = maxt
 
+    def __getnewargs__(self) -> Tuple[float, float, float, float, float, float]:
+        """Values passed to the ``__new__()`` method upon unpickling.
+
+        Returns:
+            tuple of bounds
+        """
+        return self.minx, self.maxx, self.miny, self.maxy, self.mint, self.maxt
+
     def __repr__(self) -> str:
         """Return the formal string representation of the object.

{"golden_diff": "diff --git a/torchgeo/datasets/utils.py b/torchgeo/datasets/utils.py\n--- a/torchgeo/datasets/utils.py\n+++ b/torchgeo/datasets/utils.py\n@@ -214,6 +214,14 @@\n         self.mint = mint\n         self.maxt = maxt\n \n+    def __getnewargs__(self) -> Tuple[float, float, float, float, float, float]:\n+        \"\"\"Values passed to the ``__new__()`` method upon unpickling.\n+\n+        Returns:\n+            tuple of bounds\n+        \"\"\"\n+        return self.minx, self.maxx, self.miny, self.maxy, self.mint, self.maxt\n+\n     def __repr__(self) -> str:\n         \"\"\"Return the formal string representation of the object.\n", "issue": "Samplers don't work in parallel\nWhen I try to use RandomGeoSampler with `num_workers` >= 1, I get the following error:\r\n```\r\nTraceback (most recent call last):\r\n  File \"/anaconda/envs/azureml_py38/lib/python3.9/multiprocessing/process.py\", line 315, in _bootstrap\r\n    self.run()\r\n  File \"/anaconda/envs/azureml_py38/lib/python3.9/multiprocessing/process.py\", line 108, in run\r\n    self._target(*self._args, **self._kwargs)\r\n  File \"/anaconda/envs/azureml_py38/lib/python3.9/site-packages/torch/utils/data/_utils/worker.py\", line 260, in _worker_loop\r\n    r = index_queue.get(timeout=MP_STATUS_CHECK_INTERVAL)\r\n  File \"/anaconda/envs/azureml_py38/lib/python3.9/multiprocessing/queues.py\", line 122, in get\r\n    return _ForkingPickler.loads(res)\r\nTypeError: __new__() missing 5 required positional arguments: 'maxx', 'miny', 'maxy', 'mint', and 'maxt'\r\n```\n", "before_files": [{"content": "\"\"\"Common dataset utilities.\"\"\"\n\nimport bz2\nimport contextlib\nimport gzip\nimport lzma\nimport os\nimport tarfile\nimport zipfile\nfrom typing import Any, Dict, Iterator, List, Optional, Tuple, Union\n\nimport torch\nfrom torch import Tensor\nfrom torchvision.datasets.utils import check_integrity, download_url\n\n__all__ = (\n    \"check_integrity\",\n    \"download_url\",\n    \"download_and_extract_archive\",\n    \"extract_archive\",\n    \"BoundingBox\",\n    \"working_dir\",\n    \"collate_dict\",\n)\n\n\nclass _rarfile:\n    class RarFile:\n        def __init__(self, *args: Any, **kwargs: Any) -> None:\n            self.args = args\n            self.kwargs = kwargs\n\n        def __enter__(self) -> Any:\n            try:\n                import rarfile\n            except ImportError:\n                raise ImportError(\n                    \"rarfile is not installed and is required to extract this dataset\"\n                )\n\n            # TODO: catch exception for when rarfile is installed but not\n            # unrar/unar/bsdtar\n            return rarfile.RarFile(*self.args, **self.kwargs)\n\n        def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None:\n            pass\n\n\ndef extract_archive(src: str, dst: Optional[str] = None) -> None:\n    \"\"\"Extract an archive.\n\n    Args:\n        src: file to be extracted\n        dst: directory to extract to (defaults to dirname of ``src``)\n\n    Raises:\n        RuntimeError: if src file has unknown archival/compression scheme\n    \"\"\"\n    if dst is None:\n        dst = os.path.dirname(src)\n\n    suffix_and_extractor: List[Tuple[Union[str, Tuple[str, ...]], Any]] = [\n        (\".rar\", _rarfile.RarFile),\n        (\n            (\".tar\", \".tar.gz\", \".tar.bz2\", \".tar.xz\", \".tgz\", \".tbz2\", \".tbz\", \".txz\"),\n            tarfile.open,\n        ),\n        (\".zip\", zipfile.ZipFile),\n    ]\n\n    for suffix, extractor in suffix_and_extractor:\n        if src.endswith(suffix):\n            with extractor(src, \"r\") as f:\n                f.extractall(dst)\n            return\n\n    suffix_and_decompressor: List[Tuple[str, Any]] = [\n        (\".bz2\", bz2.open),\n        (\".gz\", gzip.open),\n        (\".xz\", lzma.open),\n    ]\n\n    for suffix, decompressor in suffix_and_decompressor:\n        if src.endswith(suffix):\n            dst = os.path.join(dst, os.path.basename(src).replace(suffix, \"\"))\n            with decompressor(src, \"rb\") as sf, open(dst, \"wb\") as df:\n                df.write(sf.read())\n            return\n\n    raise RuntimeError(\"src file has unknown archival/compression scheme\")\n\n\ndef download_and_extract_archive(\n    url: str,\n    download_root: str,\n    extract_root: Optional[str] = None,\n    filename: Optional[str] = None,\n    md5: Optional[str] = None,\n) -> None:\n    \"\"\"Download and extract an archive.\n\n    Args:\n        url: URL to download\n        download_root: directory to download to\n        extract_root: directory to extract to (defaults to ``download_root``)\n        filename: download filename (defaults to basename of ``url``)\n        md5: checksum for download verification\n    \"\"\"\n    download_root = os.path.expanduser(download_root)\n    if extract_root is None:\n        extract_root = download_root\n    if not filename:\n        filename = os.path.basename(url)\n\n    download_url(url, download_root, filename, md5)\n\n    archive = os.path.join(download_root, filename)\n    print(\"Extracting {} to {}\".format(archive, extract_root))\n    extract_archive(archive, extract_root)\n\n\ndef download_radiant_mlhub(\n    dataset_id: str, download_root: str, api_key: Optional[str] = None\n) -> None:\n    \"\"\"Download a dataset from Radiant Earth.\n\n    Args:\n        dataset_id: the ID of the dataset to fetch\n        download_root: directory to download to\n        api_key: the API key to use for all requests from the session. Can also be\n            passed in via the ``MLHUB_API_KEY`` environment variable, or configured in\n            ``~/.mlhub/profiles``.\n    \"\"\"\n    try:\n        import radiant_mlhub\n    except ImportError:\n        raise ImportError(\n            \"radiant_mlhub is not installed and is required to download this dataset\"\n        )\n\n    dataset = radiant_mlhub.Dataset.fetch(dataset_id, api_key=api_key)\n    dataset.download(output_dir=download_root, api_key=api_key)\n\n\nclass BoundingBox(Tuple[float, float, float, float, float, float]):\n    \"\"\"Data class for indexing spatiotemporal data.\n\n    Attributes:\n        minx (float): western boundary\n        maxx (float): eastern boundary\n        miny (float): southern boundary\n        maxy (float): northern boundary\n        mint (float): earliest boundary\n        maxt (float): latest boundary\n    \"\"\"\n\n    def __new__(\n        cls,\n        minx: float,\n        maxx: float,\n        miny: float,\n        maxy: float,\n        mint: float,\n        maxt: float,\n    ) -> \"BoundingBox\":\n        \"\"\"Create a new instance of BoundingBox.\n\n        Args:\n            minx: western boundary\n            maxx: eastern boundary\n            miny: southern boundary\n            maxy: northern boundary\n            mint: earliest boundary\n            maxt: latest boundary\n\n        Raises:\n            ValueError: if bounding box is invalid\n                (minx > maxx, miny > maxy, or mint > maxt)\n        \"\"\"\n        if minx > maxx:\n            raise ValueError(f\"Bounding box is invalid: 'minx={minx}' > 'maxx={maxx}'\")\n        if miny > maxy:\n            raise ValueError(f\"Bounding box is invalid: 'miny={miny}' > 'maxy={maxy}'\")\n        if mint > maxt:\n            raise ValueError(f\"Bounding box is invalid: 'mint={mint}' > 'maxt={maxt}'\")\n\n        # Using super() doesn't work with mypy, see:\n        # https://stackoverflow.com/q/60611012/5828163\n        return tuple.__new__(cls, [minx, maxx, miny, maxy, mint, maxt])\n\n    def __init__(\n        self,\n        minx: float,\n        maxx: float,\n        miny: float,\n        maxy: float,\n        mint: float,\n        maxt: float,\n    ) -> None:\n        \"\"\"Initialize a new instance of BoundingBox.\n\n        Args:\n            minx: western boundary\n            maxx: eastern boundary\n            miny: southern boundary\n            maxy: northern boundary\n            mint: earliest boundary\n            maxt: latest boundary\n        \"\"\"\n        self.minx = minx\n        self.maxx = maxx\n        self.miny = miny\n        self.maxy = maxy\n        self.mint = mint\n        self.maxt = maxt\n\n    def __repr__(self) -> str:\n        \"\"\"Return the formal string representation of the object.\n\n        Returns:\n            formal string representation\n        \"\"\"\n        return (\n            f\"{self.__class__.__name__}(minx={self.minx}, maxx={self.maxx}, \"\n            f\"miny={self.miny}, maxy={self.maxy}, mint={self.mint}, maxt={self.maxt})\"\n        )\n\n    def intersects(self, other: \"BoundingBox\") -> bool:\n        \"\"\"Whether or not two bounding boxes intersect.\n\n        Args:\n            other: another bounding box\n\n        Returns:\n            True if bounding boxes intersect, else False\n        \"\"\"\n        return (\n            self.minx <= other.maxx\n            and self.maxx >= other.minx\n            and self.miny <= other.maxy\n            and self.maxy >= other.miny\n            and self.mint <= other.maxt\n            and self.maxt >= other.mint\n        )\n\n\n@contextlib.contextmanager\ndef working_dir(dirname: str, create: bool = False) -> Iterator[None]:\n    \"\"\"Context manager for changing directories.\n\n    Args:\n        dirname: directory to temporarily change to\n        create: if True, create the destination directory\n    \"\"\"\n    if create:\n        os.makedirs(dirname, exist_ok=True)\n\n    cwd = os.getcwd()\n    os.chdir(dirname)\n\n    try:\n        yield\n    finally:\n        os.chdir(cwd)\n\n\ndef collate_dict(samples: List[Dict[str, Any]]) -> Dict[str, Any]:\n    \"\"\"Merge a list of samples to form a mini-batch of Tensors.\n\n    Args:\n        samples: list of samples\n\n    Returns:\n        a single sample\n    \"\"\"\n    collated = {}\n    for key, value in samples[0].items():\n        if isinstance(value, Tensor):\n            collated[key] = torch.stack([sample[key] for sample in samples])\n        else:\n            collated[key] = [\n                sample[key] for sample in samples\n            ]  # type: ignore[assignment]\n    return collated\n", "path": "torchgeo/datasets/utils.py"}], "after_files": [{"content": "\"\"\"Common dataset utilities.\"\"\"\n\nimport bz2\nimport contextlib\nimport gzip\nimport lzma\nimport os\nimport tarfile\nimport zipfile\nfrom typing import Any, Dict, Iterator, List, Optional, Tuple, Union\n\nimport torch\nfrom torch import Tensor\nfrom torchvision.datasets.utils import check_integrity, download_url\n\n__all__ = (\n    \"check_integrity\",\n    \"download_url\",\n    \"download_and_extract_archive\",\n    \"extract_archive\",\n    \"BoundingBox\",\n    \"working_dir\",\n    \"collate_dict\",\n)\n\n\nclass _rarfile:\n    class RarFile:\n        def __init__(self, *args: Any, **kwargs: Any) -> None:\n            self.args = args\n            self.kwargs = kwargs\n\n        def __enter__(self) -> Any:\n            try:\n                import rarfile\n            except ImportError:\n                raise ImportError(\n                    \"rarfile is not installed and is required to extract this dataset\"\n                )\n\n            # TODO: catch exception for when rarfile is installed but not\n            # unrar/unar/bsdtar\n            return rarfile.RarFile(*self.args, **self.kwargs)\n\n        def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None:\n            pass\n\n\ndef extract_archive(src: str, dst: Optional[str] = None) -> None:\n    \"\"\"Extract an archive.\n\n    Args:\n        src: file to be extracted\n        dst: directory to extract to (defaults to dirname of ``src``)\n\n    Raises:\n        RuntimeError: if src file has unknown archival/compression scheme\n    \"\"\"\n    if dst is None:\n        dst = os.path.dirname(src)\n\n    suffix_and_extractor: List[Tuple[Union[str, Tuple[str, ...]], Any]] = [\n        (\".rar\", _rarfile.RarFile),\n        (\n            (\".tar\", \".tar.gz\", \".tar.bz2\", \".tar.xz\", \".tgz\", \".tbz2\", \".tbz\", \".txz\"),\n            tarfile.open,\n        ),\n        (\".zip\", zipfile.ZipFile),\n    ]\n\n    for suffix, extractor in suffix_and_extractor:\n        if src.endswith(suffix):\n            with extractor(src, \"r\") as f:\n                f.extractall(dst)\n            return\n\n    suffix_and_decompressor: List[Tuple[str, Any]] = [\n        (\".bz2\", bz2.open),\n        (\".gz\", gzip.open),\n        (\".xz\", lzma.open),\n    ]\n\n    for suffix, decompressor in suffix_and_decompressor:\n        if src.endswith(suffix):\n            dst = os.path.join(dst, os.path.basename(src).replace(suffix, \"\"))\n            with decompressor(src, \"rb\") as sf, open(dst, \"wb\") as df:\n                df.write(sf.read())\n            return\n\n    raise RuntimeError(\"src file has unknown archival/compression scheme\")\n\n\ndef download_and_extract_archive(\n    url: str,\n    download_root: str,\n    extract_root: Optional[str] = None,\n    filename: Optional[str] = None,\n    md5: Optional[str] = None,\n) -> None:\n    \"\"\"Download and extract an archive.\n\n    Args:\n        url: URL to download\n        download_root: directory to download to\n        extract_root: directory to extract to (defaults to ``download_root``)\n        filename: download filename (defaults to basename of ``url``)\n        md5: checksum for download verification\n    \"\"\"\n    download_root = os.path.expanduser(download_root)\n    if extract_root is None:\n        extract_root = download_root\n    if not filename:\n        filename = os.path.basename(url)\n\n    download_url(url, download_root, filename, md5)\n\n    archive = os.path.join(download_root, filename)\n    print(\"Extracting {} to {}\".format(archive, extract_root))\n    extract_archive(archive, extract_root)\n\n\ndef download_radiant_mlhub(\n    dataset_id: str, download_root: str, api_key: Optional[str] = None\n) -> None:\n    \"\"\"Download a dataset from Radiant Earth.\n\n    Args:\n        dataset_id: the ID of the dataset to fetch\n        download_root: directory to download to\n        api_key: the API key to use for all requests from the session. Can also be\n            passed in via the ``MLHUB_API_KEY`` environment variable, or configured in\n            ``~/.mlhub/profiles``.\n    \"\"\"\n    try:\n        import radiant_mlhub\n    except ImportError:\n        raise ImportError(\n            \"radiant_mlhub is not installed and is required to download this dataset\"\n        )\n\n    dataset = radiant_mlhub.Dataset.fetch(dataset_id, api_key=api_key)\n    dataset.download(output_dir=download_root, api_key=api_key)\n\n\nclass BoundingBox(Tuple[float, float, float, float, float, float]):\n    \"\"\"Data class for indexing spatiotemporal data.\n\n    Attributes:\n        minx (float): western boundary\n        maxx (float): eastern boundary\n        miny (float): southern boundary\n        maxy (float): northern boundary\n        mint (float): earliest boundary\n        maxt (float): latest boundary\n    \"\"\"\n\n    def __new__(\n        cls,\n        minx: float,\n        maxx: float,\n        miny: float,\n        maxy: float,\n        mint: float,\n        maxt: float,\n    ) -> \"BoundingBox\":\n        \"\"\"Create a new instance of BoundingBox.\n\n        Args:\n            minx: western boundary\n            maxx: eastern boundary\n            miny: southern boundary\n            maxy: northern boundary\n            mint: earliest boundary\n            maxt: latest boundary\n\n        Raises:\n            ValueError: if bounding box is invalid\n                (minx > maxx, miny > maxy, or mint > maxt)\n        \"\"\"\n        if minx > maxx:\n            raise ValueError(f\"Bounding box is invalid: 'minx={minx}' > 'maxx={maxx}'\")\n        if miny > maxy:\n            raise ValueError(f\"Bounding box is invalid: 'miny={miny}' > 'maxy={maxy}'\")\n        if mint > maxt:\n            raise ValueError(f\"Bounding box is invalid: 'mint={mint}' > 'maxt={maxt}'\")\n\n        # Using super() doesn't work with mypy, see:\n        # https://stackoverflow.com/q/60611012/5828163\n        return tuple.__new__(cls, [minx, maxx, miny, maxy, mint, maxt])\n\n    def __init__(\n        self,\n        minx: float,\n        maxx: float,\n        miny: float,\n        maxy: float,\n        mint: float,\n        maxt: float,\n    ) -> None:\n        \"\"\"Initialize a new instance of BoundingBox.\n\n        Args:\n            minx: western boundary\n            maxx: eastern boundary\n            miny: southern boundary\n            maxy: northern boundary\n            mint: earliest boundary\n            maxt: latest boundary\n        \"\"\"\n        self.minx = minx\n        self.maxx = maxx\n        self.miny = miny\n        self.maxy = maxy\n        self.mint = mint\n        self.maxt = maxt\n\n    def __getnewargs__(self) -> Tuple[float, float, float, float, float, float]:\n        \"\"\"Values passed to the ``__new__()`` method upon unpickling.\n\n        Returns:\n            tuple of bounds\n        \"\"\"\n        return self.minx, self.maxx, self.miny, self.maxy, self.mint, self.maxt\n\n    def __repr__(self) -> str:\n        \"\"\"Return the formal string representation of the object.\n\n        Returns:\n            formal string representation\n        \"\"\"\n        return (\n            f\"{self.__class__.__name__}(minx={self.minx}, maxx={self.maxx}, \"\n            f\"miny={self.miny}, maxy={self.maxy}, mint={self.mint}, maxt={self.maxt})\"\n        )\n\n    def intersects(self, other: \"BoundingBox\") -> bool:\n        \"\"\"Whether or not two bounding boxes intersect.\n\n        Args:\n            other: another bounding box\n\n        Returns:\n            True if bounding boxes intersect, else False\n        \"\"\"\n        return (\n            self.minx <= other.maxx\n            and self.maxx >= other.minx\n            and self.miny <= other.maxy\n            and self.maxy >= other.miny\n            and self.mint <= other.maxt\n            and self.maxt >= other.mint\n        )\n\n\n@contextlib.contextmanager\ndef working_dir(dirname: str, create: bool = False) -> Iterator[None]:\n    \"\"\"Context manager for changing directories.\n\n    Args:\n        dirname: directory to temporarily change to\n        create: if True, create the destination directory\n    \"\"\"\n    if create:\n        os.makedirs(dirname, exist_ok=True)\n\n    cwd = os.getcwd()\n    os.chdir(dirname)\n\n    try:\n        yield\n    finally:\n        os.chdir(cwd)\n\n\ndef collate_dict(samples: List[Dict[str, Any]]) -> Dict[str, Any]:\n    \"\"\"Merge a list of samples to form a mini-batch of Tensors.\n\n    Args:\n        samples: list of samples\n\n    Returns:\n        a single sample\n    \"\"\"\n    collated = {}\n    for key, value in samples[0].items():\n        if isinstance(value, Tensor):\n            collated[key] = torch.stack([sample[key] for sample in samples])\n        else:\n            collated[key] = [\n                sample[key] for sample in samples\n            ]  # type: ignore[assignment]\n    return collated\n", "path": "torchgeo/datasets/utils.py"}]}