We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AgriFieldNet missing filename glob
### Description

The filename glob is missing from the AgriFieldNet dataset.

### Steps to reproduce

1. Check line 76 in torchgeo/datasets/agrifieldnet.py

### Version

main

--- 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/agrifieldnet.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 
4 """AgriFieldNet India Challenge dataset."""
5 
6 import os
7 import re
8 from collections.abc import Callable, Iterable, Sequence
9 from typing import Any, cast
10 
11 import matplotlib.pyplot as plt
12 import torch
13 from matplotlib.figure import Figure
14 from rasterio.crs import CRS
15 from torch import Tensor
16 
17 from .errors import RGBBandsMissingError
18 from .geo import RasterDataset
19 from .utils import BoundingBox
20 
21 
22 class AgriFieldNet(RasterDataset):
23     """AgriFieldNet India Challenge dataset.
24 
25     The `AgriFieldNet India Challenge
26     <https://zindi.africa/competitions/agrifieldnet-india-challenge>`__ dataset
27     includes satellite imagery from Sentinel-2 cloud free composites
28     (single snapshot) and labels for crop type that were collected by ground survey.
29     The Sentinel-2 data are then matched with corresponding labels.
30     The dataset contains 7081 fields, which have been split into training and
31     test sets (5551 fields in the train and 1530 fields in the test).
32     Satellite imagery and labels are tiled into 256x256 chips adding up to 1217 tiles.
33     The fields are distributed across all chips, some chips may only have train or
34     test fields and some may have both. Since the labels are derived from data
35     collected on the ground, not all the pixels are labeled in each chip.
36     If the field ID for a pixel is set to 0 it means that pixel is not included in
37     either of the train or test set (and correspondingly the crop label
38     will be 0 as well). For this challenge train and test sets have slightly
39     different crop type distributions. The train set follows the distribution
40     of ground reference data which is a skewed distribution with a few dominant
41     crops being over represented. The test set was drawn randomly from an area
42     weighted field list that ensured that fields with less common crop types
43     were better represented in the test set. The original dataset can be
44     downloaded from `Source Cooperative <https://beta.source.coop/
45     radiantearth/agrifieldnet-competition/>`__.
46 
47     Dataset format:
48 
49     * images are 12-band Sentinel-2 data
50     * masks are tiff images with unique values representing the class and field id
51 
52     Dataset classes:
53 
54     0 - No-Data
55     1 - Wheat
56     2 - Mustard
57     3 - Lentil
58     4 - No Crop/Fallow
59     5 - Green pea
60     6 - Sugarcane
61     8 - Garlic
62     9 - Maize
63     13 - Gram
64     14 - Coriander
65     15 - Potato
66     16 - Berseem
67     36 - Rice
68 
69     If you use this dataset in your research, please cite the following dataset:
70 
71     * https://doi.org/10.34911/rdnt.wu92p1
72 
73     .. versionadded:: 0.6
74     """
75 
76     filename_regex = r"""
77         ^ref_agrifieldnet_competition_v1_source_
78         (?P<unique_folder_id>[a-z0-9]{5})
79         _(?P<band>B[0-9A-Z]{2})_10m
80     """
81 
82     rgb_bands = ['B04', 'B03', 'B02']
83     all_bands = [
84         'B01',
85         'B02',
86         'B03',
87         'B04',
88         'B05',
89         'B06',
90         'B07',
91         'B08',
92         'B8A',
93         'B09',
94         'B11',
95         'B12',
96     ]
97 
98     cmap = {
99         0: (0, 0, 0, 255),
100         1: (255, 211, 0, 255),
101         2: (255, 37, 37, 255),
102         3: (0, 168, 226, 255),
103         4: (255, 158, 9, 255),
104         5: (37, 111, 0, 255),
105         6: (255, 255, 0, 255),
106         8: (111, 166, 0, 255),
107         9: (0, 175, 73, 255),
108         13: (222, 166, 9, 255),
109         14: (222, 166, 9, 255),
110         15: (124, 211, 255, 255),
111         16: (226, 0, 124, 255),
112         36: (137, 96, 83, 255),
113     }
114 
115     def __init__(
116         self,
117         paths: str | Iterable[str] = 'data',
118         crs: CRS | None = None,
119         classes: list[int] = list(cmap.keys()),
120         bands: Sequence[str] = all_bands,
121         transforms: Callable[[dict[str, Tensor]], dict[str, Tensor]] | None = None,
122         cache: bool = True,
123     ) -> None:
124         """Initialize a new AgriFieldNet dataset instance.
125 
126         Args:
127             paths: one or more root directories to search for files to load
128             crs: :term:`coordinate reference system (CRS)` to warp to
129                 (defaults to the CRS of the first file found)
130             classes: list of classes to include, the rest will be mapped to 0
131                 (defaults to all classes)
132             bands: the subset of bands to load
133             transforms: a function/transform that takes input sample and its target as
134                 entry and returns a transformed version
135             cache: if True, cache the dataset in memory
136 
137         Raises:
138             DatasetNotFoundError: If dataset is not found.
139         """
140         assert (
141             set(classes) <= self.cmap.keys()
142         ), f'Only the following classes are valid: {list(self.cmap.keys())}.'
143         assert 0 in classes, 'Classes must include the background class: 0'
144 
145         self.paths = paths
146         self.classes = classes
147         self.ordinal_map = torch.zeros(max(self.cmap.keys()) + 1, dtype=self.dtype)
148         self.ordinal_cmap = torch.zeros((len(self.classes), 4), dtype=torch.uint8)
149 
150         super().__init__(
151             paths=paths, crs=crs, bands=bands, transforms=transforms, cache=cache
152         )
153 
154         # Map chosen classes to ordinal numbers, all others mapped to background class
155         for v, k in enumerate(self.classes):
156             self.ordinal_map[k] = v
157             self.ordinal_cmap[v] = torch.tensor(self.cmap[k])
158 
159     def __getitem__(self, query: BoundingBox) -> dict[str, Any]:
160         """Return an index within the dataset.
161 
162         Args:
163             query: (minx, maxx, miny, maxy, mint, maxt) coordinates to index
164 
165         Returns:
166             data, label, and field ids at that index
167         """
168         assert isinstance(self.paths, str)
169 
170         hits = self.index.intersection(tuple(query), objects=True)
171         filepaths = cast(list[str], [hit.object for hit in hits])
172 
173         if not filepaths:
174             raise IndexError(
175                 f'query: {query} not found in index with bounds: {self.bounds}'
176             )
177 
178         data_list: list[Tensor] = []
179         filename_regex = re.compile(self.filename_regex, re.VERBOSE)
180         for band in self.bands:
181             band_filepaths = []
182             for filepath in filepaths:
183                 filename = os.path.basename(filepath)
184                 directory = os.path.dirname(filepath)
185                 match = re.match(filename_regex, filename)
186                 if match:
187                     if 'band' in match.groupdict():
188                         start = match.start('band')
189                         end = match.end('band')
190                         filename = filename[:start] + band + filename[end:]
191                 filepath = os.path.join(directory, filename)
192                 band_filepaths.append(filepath)
193             data_list.append(self._merge_files(band_filepaths, query))
194         image = torch.cat(data_list)
195 
196         mask_filepaths = []
197         for root, dirs, files in os.walk(os.path.join(self.paths, 'train_labels')):
198             for file in files:
199                 if not file.endswith('_field_ids.tif') and file.endswith('.tif'):
200                     file_path = os.path.join(root, file)
201                     mask_filepaths.append(file_path)
202 
203         mask = self._merge_files(mask_filepaths, query)
204         mask = self.ordinal_map[mask.squeeze().long()]
205 
206         sample = {
207             'crs': self.crs,
208             'bbox': query,
209             'image': image.float(),
210             'mask': mask.long(),
211         }
212 
213         if self.transforms is not None:
214             sample = self.transforms(sample)
215 
216         return sample
217 
218     def plot(
219         self,
220         sample: dict[str, Tensor],
221         show_titles: bool = True,
222         suptitle: str | None = None,
223     ) -> Figure:
224         """Plot a sample from the dataset.
225 
226         Args:
227             sample: a sample returned by :meth:`__getitem__`
228             show_titles: flag indicating whether to show titles above each panel
229             suptitle: optional string to use as a suptitle
230 
231         Returns:
232             a matplotlib Figure with the rendered sample
233 
234         Raises:
235             RGBBandsMissingError: If *bands* does not include all RGB bands.
236         """
237         rgb_indices = []
238         for band in self.rgb_bands:
239             if band in self.bands:
240                 rgb_indices.append(self.bands.index(band))
241             else:
242                 raise RGBBandsMissingError()
243 
244         image = sample['image'][rgb_indices].permute(1, 2, 0)
245         image = (image - image.min()) / (image.max() - image.min())
246 
247         mask = sample['mask'].squeeze()
248         ncols = 2
249 
250         showing_prediction = 'prediction' in sample
251         if showing_prediction:
252             pred = sample['prediction'].squeeze()
253             ncols += 1
254 
255         fig, axs = plt.subplots(nrows=1, ncols=ncols, figsize=(ncols * 4, 4))
256         axs[0].imshow(image)
257         axs[0].axis('off')
258         axs[1].imshow(self.ordinal_cmap[mask], interpolation='none')
259         axs[1].axis('off')
260         if show_titles:
261             axs[0].set_title('Image')
262             axs[1].set_title('Mask')
263 
264         if showing_prediction:
265             axs[2].imshow(self.ordinal_cmap[pred], interpolation='none')
266             axs[2].axis('off')
267             if show_titles:
268                 axs[2].set_title('Prediction')
269 
270         if suptitle is not None:
271             plt.suptitle(suptitle)
272 
273         return fig
274
```
--- 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/agrifieldnet.py b/torchgeo/datasets/agrifieldnet.py
--- a/torchgeo/datasets/agrifieldnet.py
+++ b/torchgeo/datasets/agrifieldnet.py
@@ -73,6 +73,7 @@
     .. versionadded:: 0.6
     """
 
+    filename_glob = 'ref_agrifieldnet_competition_v1_source_*_{}_10m.*'
     filename_regex = r"""
         ^ref_agrifieldnet_competition_v1_source_
         (?P<unique_folder_id>[a-z0-9]{5})
@@ -146,6 +147,7 @@
         self.classes = classes
         self.ordinal_map = torch.zeros(max(self.cmap.keys()) + 1, dtype=self.dtype)
         self.ordinal_cmap = torch.zeros((len(self.classes), 4), dtype=torch.uint8)
+        self.filename_glob = self.filename_glob.format(bands[0])
 
         super().__init__(
             paths=paths, crs=crs, bands=bands, transforms=transforms, cache=cache

{"golden_diff": "diff --git a/torchgeo/datasets/agrifieldnet.py b/torchgeo/datasets/agrifieldnet.py\n--- a/torchgeo/datasets/agrifieldnet.py\n+++ b/torchgeo/datasets/agrifieldnet.py\n@@ -73,6 +73,7 @@\n     .. versionadded:: 0.6\n     \"\"\"\n \n+    filename_glob = 'ref_agrifieldnet_competition_v1_source_*_{}_10m.*'\n     filename_regex = r\"\"\"\n         ^ref_agrifieldnet_competition_v1_source_\n         (?P<unique_folder_id>[a-z0-9]{5})\n@@ -146,6 +147,7 @@\n         self.classes = classes\n         self.ordinal_map = torch.zeros(max(self.cmap.keys()) + 1, dtype=self.dtype)\n         self.ordinal_cmap = torch.zeros((len(self.classes), 4), dtype=torch.uint8)\n+        self.filename_glob = self.filename_glob.format(bands[0])\n \n         super().__init__(\n             paths=paths, crs=crs, bands=bands, transforms=transforms, cache=cache\n", "issue": "AgriFieldNet missing filename glob\n### Description\n\nThe filename glob is missing from the AgriFieldNet dataset.\n\n### Steps to reproduce\n\n1. Check line 76 in torchgeo/datasets/agrifieldnet.py\n\n### Version\n\nmain\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\n\"\"\"AgriFieldNet India Challenge dataset.\"\"\"\n\nimport os\nimport re\nfrom collections.abc import Callable, Iterable, Sequence\nfrom typing import Any, cast\n\nimport matplotlib.pyplot as plt\nimport torch\nfrom matplotlib.figure import Figure\nfrom rasterio.crs import CRS\nfrom torch import Tensor\n\nfrom .errors import RGBBandsMissingError\nfrom .geo import RasterDataset\nfrom .utils import BoundingBox\n\n\nclass AgriFieldNet(RasterDataset):\n    \"\"\"AgriFieldNet India Challenge dataset.\n\n    The `AgriFieldNet India Challenge\n    <https://zindi.africa/competitions/agrifieldnet-india-challenge>`__ dataset\n    includes satellite imagery from Sentinel-2 cloud free composites\n    (single snapshot) and labels for crop type that were collected by ground survey.\n    The Sentinel-2 data are then matched with corresponding labels.\n    The dataset contains 7081 fields, which have been split into training and\n    test sets (5551 fields in the train and 1530 fields in the test).\n    Satellite imagery and labels are tiled into 256x256 chips adding up to 1217 tiles.\n    The fields are distributed across all chips, some chips may only have train or\n    test fields and some may have both. Since the labels are derived from data\n    collected on the ground, not all the pixels are labeled in each chip.\n    If the field ID for a pixel is set to 0 it means that pixel is not included in\n    either of the train or test set (and correspondingly the crop label\n    will be 0 as well). For this challenge train and test sets have slightly\n    different crop type distributions. The train set follows the distribution\n    of ground reference data which is a skewed distribution with a few dominant\n    crops being over represented. The test set was drawn randomly from an area\n    weighted field list that ensured that fields with less common crop types\n    were better represented in the test set. The original dataset can be\n    downloaded from `Source Cooperative <https://beta.source.coop/\n    radiantearth/agrifieldnet-competition/>`__.\n\n    Dataset format:\n\n    * images are 12-band Sentinel-2 data\n    * masks are tiff images with unique values representing the class and field id\n\n    Dataset classes:\n\n    0 - No-Data\n    1 - Wheat\n    2 - Mustard\n    3 - Lentil\n    4 - No Crop/Fallow\n    5 - Green pea\n    6 - Sugarcane\n    8 - Garlic\n    9 - Maize\n    13 - Gram\n    14 - Coriander\n    15 - Potato\n    16 - Berseem\n    36 - Rice\n\n    If you use this dataset in your research, please cite the following dataset:\n\n    * https://doi.org/10.34911/rdnt.wu92p1\n\n    .. versionadded:: 0.6\n    \"\"\"\n\n    filename_regex = r\"\"\"\n        ^ref_agrifieldnet_competition_v1_source_\n        (?P<unique_folder_id>[a-z0-9]{5})\n        _(?P<band>B[0-9A-Z]{2})_10m\n    \"\"\"\n\n    rgb_bands = ['B04', 'B03', 'B02']\n    all_bands = [\n        'B01',\n        'B02',\n        'B03',\n        'B04',\n        'B05',\n        'B06',\n        'B07',\n        'B08',\n        'B8A',\n        'B09',\n        'B11',\n        'B12',\n    ]\n\n    cmap = {\n        0: (0, 0, 0, 255),\n        1: (255, 211, 0, 255),\n        2: (255, 37, 37, 255),\n        3: (0, 168, 226, 255),\n        4: (255, 158, 9, 255),\n        5: (37, 111, 0, 255),\n        6: (255, 255, 0, 255),\n        8: (111, 166, 0, 255),\n        9: (0, 175, 73, 255),\n        13: (222, 166, 9, 255),\n        14: (222, 166, 9, 255),\n        15: (124, 211, 255, 255),\n        16: (226, 0, 124, 255),\n        36: (137, 96, 83, 255),\n    }\n\n    def __init__(\n        self,\n        paths: str | Iterable[str] = 'data',\n        crs: CRS | None = None,\n        classes: list[int] = list(cmap.keys()),\n        bands: Sequence[str] = all_bands,\n        transforms: Callable[[dict[str, Tensor]], dict[str, Tensor]] | None = None,\n        cache: bool = True,\n    ) -> None:\n        \"\"\"Initialize a new AgriFieldNet dataset instance.\n\n        Args:\n            paths: one or more root directories to search for files to load\n            crs: :term:`coordinate reference system (CRS)` to warp to\n                (defaults to the CRS of the first file found)\n            classes: list of classes to include, the rest will be mapped to 0\n                (defaults to all classes)\n            bands: the subset of bands to load\n            transforms: a function/transform that takes input sample and its target as\n                entry and returns a transformed version\n            cache: if True, cache the dataset in memory\n\n        Raises:\n            DatasetNotFoundError: If dataset is not found.\n        \"\"\"\n        assert (\n            set(classes) <= self.cmap.keys()\n        ), f'Only the following classes are valid: {list(self.cmap.keys())}.'\n        assert 0 in classes, 'Classes must include the background class: 0'\n\n        self.paths = paths\n        self.classes = classes\n        self.ordinal_map = torch.zeros(max(self.cmap.keys()) + 1, dtype=self.dtype)\n        self.ordinal_cmap = torch.zeros((len(self.classes), 4), dtype=torch.uint8)\n\n        super().__init__(\n            paths=paths, crs=crs, bands=bands, transforms=transforms, cache=cache\n        )\n\n        # Map chosen classes to ordinal numbers, all others mapped to background class\n        for v, k in enumerate(self.classes):\n            self.ordinal_map[k] = v\n            self.ordinal_cmap[v] = torch.tensor(self.cmap[k])\n\n    def __getitem__(self, query: BoundingBox) -> dict[str, Any]:\n        \"\"\"Return an index within the dataset.\n\n        Args:\n            query: (minx, maxx, miny, maxy, mint, maxt) coordinates to index\n\n        Returns:\n            data, label, and field ids at that index\n        \"\"\"\n        assert isinstance(self.paths, str)\n\n        hits = self.index.intersection(tuple(query), objects=True)\n        filepaths = cast(list[str], [hit.object for hit in hits])\n\n        if not filepaths:\n            raise IndexError(\n                f'query: {query} not found in index with bounds: {self.bounds}'\n            )\n\n        data_list: list[Tensor] = []\n        filename_regex = re.compile(self.filename_regex, re.VERBOSE)\n        for band in self.bands:\n            band_filepaths = []\n            for filepath in filepaths:\n                filename = os.path.basename(filepath)\n                directory = os.path.dirname(filepath)\n                match = re.match(filename_regex, filename)\n                if match:\n                    if 'band' in match.groupdict():\n                        start = match.start('band')\n                        end = match.end('band')\n                        filename = filename[:start] + band + filename[end:]\n                filepath = os.path.join(directory, filename)\n                band_filepaths.append(filepath)\n            data_list.append(self._merge_files(band_filepaths, query))\n        image = torch.cat(data_list)\n\n        mask_filepaths = []\n        for root, dirs, files in os.walk(os.path.join(self.paths, 'train_labels')):\n            for file in files:\n                if not file.endswith('_field_ids.tif') and file.endswith('.tif'):\n                    file_path = os.path.join(root, file)\n                    mask_filepaths.append(file_path)\n\n        mask = self._merge_files(mask_filepaths, query)\n        mask = self.ordinal_map[mask.squeeze().long()]\n\n        sample = {\n            'crs': self.crs,\n            'bbox': query,\n            'image': image.float(),\n            'mask': mask.long(),\n        }\n\n        if self.transforms is not None:\n            sample = self.transforms(sample)\n\n        return sample\n\n    def plot(\n        self,\n        sample: dict[str, Tensor],\n        show_titles: bool = True,\n        suptitle: str | None = None,\n    ) -> Figure:\n        \"\"\"Plot a sample from the dataset.\n\n        Args:\n            sample: a sample returned by :meth:`__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            RGBBandsMissingError: If *bands* does not include all RGB bands.\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 RGBBandsMissingError()\n\n        image = sample['image'][rgb_indices].permute(1, 2, 0)\n        image = (image - image.min()) / (image.max() - image.min())\n\n        mask = sample['mask'].squeeze()\n        ncols = 2\n\n        showing_prediction = 'prediction' in sample\n        if showing_prediction:\n            pred = sample['prediction'].squeeze()\n            ncols += 1\n\n        fig, axs = plt.subplots(nrows=1, ncols=ncols, figsize=(ncols * 4, 4))\n        axs[0].imshow(image)\n        axs[0].axis('off')\n        axs[1].imshow(self.ordinal_cmap[mask], interpolation='none')\n        axs[1].axis('off')\n        if show_titles:\n            axs[0].set_title('Image')\n            axs[1].set_title('Mask')\n\n        if showing_prediction:\n            axs[2].imshow(self.ordinal_cmap[pred], interpolation='none')\n            axs[2].axis('off')\n            if show_titles:\n                axs[2].set_title('Prediction')\n\n        if suptitle is not None:\n            plt.suptitle(suptitle)\n\n        return fig\n", "path": "torchgeo/datasets/agrifieldnet.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\n\"\"\"AgriFieldNet India Challenge dataset.\"\"\"\n\nimport os\nimport re\nfrom collections.abc import Callable, Iterable, Sequence\nfrom typing import Any, cast\n\nimport matplotlib.pyplot as plt\nimport torch\nfrom matplotlib.figure import Figure\nfrom rasterio.crs import CRS\nfrom torch import Tensor\n\nfrom .errors import RGBBandsMissingError\nfrom .geo import RasterDataset\nfrom .utils import BoundingBox\n\n\nclass AgriFieldNet(RasterDataset):\n    \"\"\"AgriFieldNet India Challenge dataset.\n\n    The `AgriFieldNet India Challenge\n    <https://zindi.africa/competitions/agrifieldnet-india-challenge>`__ dataset\n    includes satellite imagery from Sentinel-2 cloud free composites\n    (single snapshot) and labels for crop type that were collected by ground survey.\n    The Sentinel-2 data are then matched with corresponding labels.\n    The dataset contains 7081 fields, which have been split into training and\n    test sets (5551 fields in the train and 1530 fields in the test).\n    Satellite imagery and labels are tiled into 256x256 chips adding up to 1217 tiles.\n    The fields are distributed across all chips, some chips may only have train or\n    test fields and some may have both. Since the labels are derived from data\n    collected on the ground, not all the pixels are labeled in each chip.\n    If the field ID for a pixel is set to 0 it means that pixel is not included in\n    either of the train or test set (and correspondingly the crop label\n    will be 0 as well). For this challenge train and test sets have slightly\n    different crop type distributions. The train set follows the distribution\n    of ground reference data which is a skewed distribution with a few dominant\n    crops being over represented. The test set was drawn randomly from an area\n    weighted field list that ensured that fields with less common crop types\n    were better represented in the test set. The original dataset can be\n    downloaded from `Source Cooperative <https://beta.source.coop/\n    radiantearth/agrifieldnet-competition/>`__.\n\n    Dataset format:\n\n    * images are 12-band Sentinel-2 data\n    * masks are tiff images with unique values representing the class and field id\n\n    Dataset classes:\n\n    0 - No-Data\n    1 - Wheat\n    2 - Mustard\n    3 - Lentil\n    4 - No Crop/Fallow\n    5 - Green pea\n    6 - Sugarcane\n    8 - Garlic\n    9 - Maize\n    13 - Gram\n    14 - Coriander\n    15 - Potato\n    16 - Berseem\n    36 - Rice\n\n    If you use this dataset in your research, please cite the following dataset:\n\n    * https://doi.org/10.34911/rdnt.wu92p1\n\n    .. versionadded:: 0.6\n    \"\"\"\n\n    filename_glob = 'ref_agrifieldnet_competition_v1_source_*_{}_10m.*'\n    filename_regex = r\"\"\"\n        ^ref_agrifieldnet_competition_v1_source_\n        (?P<unique_folder_id>[a-z0-9]{5})\n        _(?P<band>B[0-9A-Z]{2})_10m\n    \"\"\"\n\n    rgb_bands = ['B04', 'B03', 'B02']\n    all_bands = [\n        'B01',\n        'B02',\n        'B03',\n        'B04',\n        'B05',\n        'B06',\n        'B07',\n        'B08',\n        'B8A',\n        'B09',\n        'B11',\n        'B12',\n    ]\n\n    cmap = {\n        0: (0, 0, 0, 255),\n        1: (255, 211, 0, 255),\n        2: (255, 37, 37, 255),\n        3: (0, 168, 226, 255),\n        4: (255, 158, 9, 255),\n        5: (37, 111, 0, 255),\n        6: (255, 255, 0, 255),\n        8: (111, 166, 0, 255),\n        9: (0, 175, 73, 255),\n        13: (222, 166, 9, 255),\n        14: (222, 166, 9, 255),\n        15: (124, 211, 255, 255),\n        16: (226, 0, 124, 255),\n        36: (137, 96, 83, 255),\n    }\n\n    def __init__(\n        self,\n        paths: str | Iterable[str] = 'data',\n        crs: CRS | None = None,\n        classes: list[int] = list(cmap.keys()),\n        bands: Sequence[str] = all_bands,\n        transforms: Callable[[dict[str, Tensor]], dict[str, Tensor]] | None = None,\n        cache: bool = True,\n    ) -> None:\n        \"\"\"Initialize a new AgriFieldNet dataset instance.\n\n        Args:\n            paths: one or more root directories to search for files to load\n            crs: :term:`coordinate reference system (CRS)` to warp to\n                (defaults to the CRS of the first file found)\n            classes: list of classes to include, the rest will be mapped to 0\n                (defaults to all classes)\n            bands: the subset of bands to load\n            transforms: a function/transform that takes input sample and its target as\n                entry and returns a transformed version\n            cache: if True, cache the dataset in memory\n\n        Raises:\n            DatasetNotFoundError: If dataset is not found.\n        \"\"\"\n        assert (\n            set(classes) <= self.cmap.keys()\n        ), f'Only the following classes are valid: {list(self.cmap.keys())}.'\n        assert 0 in classes, 'Classes must include the background class: 0'\n\n        self.paths = paths\n        self.classes = classes\n        self.ordinal_map = torch.zeros(max(self.cmap.keys()) + 1, dtype=self.dtype)\n        self.ordinal_cmap = torch.zeros((len(self.classes), 4), dtype=torch.uint8)\n        self.filename_glob = self.filename_glob.format(bands[0])\n\n        super().__init__(\n            paths=paths, crs=crs, bands=bands, transforms=transforms, cache=cache\n        )\n\n        # Map chosen classes to ordinal numbers, all others mapped to background class\n        for v, k in enumerate(self.classes):\n            self.ordinal_map[k] = v\n            self.ordinal_cmap[v] = torch.tensor(self.cmap[k])\n\n    def __getitem__(self, query: BoundingBox) -> dict[str, Any]:\n        \"\"\"Return an index within the dataset.\n\n        Args:\n            query: (minx, maxx, miny, maxy, mint, maxt) coordinates to index\n\n        Returns:\n            data, label, and field ids at that index\n        \"\"\"\n        assert isinstance(self.paths, str)\n\n        hits = self.index.intersection(tuple(query), objects=True)\n        filepaths = cast(list[str], [hit.object for hit in hits])\n\n        if not filepaths:\n            raise IndexError(\n                f'query: {query} not found in index with bounds: {self.bounds}'\n            )\n\n        data_list: list[Tensor] = []\n        filename_regex = re.compile(self.filename_regex, re.VERBOSE)\n        for band in self.bands:\n            band_filepaths = []\n            for filepath in filepaths:\n                filename = os.path.basename(filepath)\n                directory = os.path.dirname(filepath)\n                match = re.match(filename_regex, filename)\n                if match:\n                    if 'band' in match.groupdict():\n                        start = match.start('band')\n                        end = match.end('band')\n                        filename = filename[:start] + band + filename[end:]\n                filepath = os.path.join(directory, filename)\n                band_filepaths.append(filepath)\n            data_list.append(self._merge_files(band_filepaths, query))\n        image = torch.cat(data_list)\n\n        mask_filepaths = []\n        for root, dirs, files in os.walk(os.path.join(self.paths, 'train_labels')):\n            for file in files:\n                if not file.endswith('_field_ids.tif') and file.endswith('.tif'):\n                    file_path = os.path.join(root, file)\n                    mask_filepaths.append(file_path)\n\n        mask = self._merge_files(mask_filepaths, query)\n        mask = self.ordinal_map[mask.squeeze().long()]\n\n        sample = {\n            'crs': self.crs,\n            'bbox': query,\n            'image': image.float(),\n            'mask': mask.long(),\n        }\n\n        if self.transforms is not None:\n            sample = self.transforms(sample)\n\n        return sample\n\n    def plot(\n        self,\n        sample: dict[str, Tensor],\n        show_titles: bool = True,\n        suptitle: str | None = None,\n    ) -> Figure:\n        \"\"\"Plot a sample from the dataset.\n\n        Args:\n            sample: a sample returned by :meth:`__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            RGBBandsMissingError: If *bands* does not include all RGB bands.\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 RGBBandsMissingError()\n\n        image = sample['image'][rgb_indices].permute(1, 2, 0)\n        image = (image - image.min()) / (image.max() - image.min())\n\n        mask = sample['mask'].squeeze()\n        ncols = 2\n\n        showing_prediction = 'prediction' in sample\n        if showing_prediction:\n            pred = sample['prediction'].squeeze()\n            ncols += 1\n\n        fig, axs = plt.subplots(nrows=1, ncols=ncols, figsize=(ncols * 4, 4))\n        axs[0].imshow(image)\n        axs[0].axis('off')\n        axs[1].imshow(self.ordinal_cmap[mask], interpolation='none')\n        axs[1].axis('off')\n        if show_titles:\n            axs[0].set_title('Image')\n            axs[1].set_title('Mask')\n\n        if showing_prediction:\n            axs[2].imshow(self.ordinal_cmap[pred], interpolation='none')\n            axs[2].axis('off')\n            if show_titles:\n                axs[2].set_title('Prediction')\n\n        if suptitle is not None:\n            plt.suptitle(suptitle)\n\n        return fig\n", "path": "torchgeo/datasets/agrifieldnet.py"}]}