prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Add support for multipolygons
  Enable RV to handle geojson label stores that are encoded as multipolygons
  
  </issue>
  <code>
  [start of src/rastervision/label_stores/object_detection_geojson_file.py]
  1 import json
  2 
  3 import numpy as np
  4 
  5 from rastervision.core.box import Box
  6 from rastervision.labels.object_detection_labels import (ObjectDetectionLabels)
  7 from rastervision.label_stores.utils import (add_classes_to_geojson,
  8                                              load_label_store_json)
  9 from rastervision.label_stores.object_detection_label_store import (
  10     ObjectDetectionLabelStore)
  11 from rastervision.label_stores.utils import boxes_to_geojson
  12 from rastervision.utils.files import str_to_file, NotWritableError
  13 
  14 
  15 def geojson_to_labels(geojson_dict, crs_transformer, extent=None):
  16     """Convert GeoJSON to ObjectDetectionLabels object.
  17 
  18     If extent is provided, filter out the boxes that lie "more than a little
  19     bit" outside the extent.
  20 
  21     Args:
  22         geojson_dict: dict in GeoJSON format
  23         crs_transformer: used to convert map coords in geojson to pixel coords
  24             in labels object
  25         extent: Box in pixel coords
  26 
  27     Returns:
  28         ObjectDetectionLabels
  29     """
  30     features = geojson_dict['features']
  31     boxes = []
  32     class_ids = []
  33     scores = []
  34 
  35     for feature in features:
  36         # Convert polygon to pixel coords and then convert to bounding box.
  37         polygon = feature['geometry']['coordinates'][0]
  38         polygon = [crs_transformer.map_to_pixel(p) for p in polygon]
  39         xmin, ymin = np.min(polygon, axis=0)
  40         xmax, ymax = np.max(polygon, axis=0)
  41         boxes.append(Box(ymin, xmin, ymax, xmax))
  42 
  43         properties = feature['properties']
  44         class_ids.append(properties['class_id'])
  45         scores.append(properties.get('score', 1.0))
  46 
  47     boxes = np.array([box.npbox_format() for box in boxes], dtype=float)
  48     class_ids = np.array(class_ids)
  49     scores = np.array(scores)
  50 
  51     labels = ObjectDetectionLabels(boxes, class_ids, scores=scores)
  52     if extent is not None:
  53         labels = ObjectDetectionLabels.get_overlapping(
  54             labels, extent, ioa_thresh=0.8, clip=True)
  55     return labels
  56 
  57 
  58 class ObjectDetectionGeoJSONFile(ObjectDetectionLabelStore):
  59     def __init__(self,
  60                  uri,
  61                  crs_transformer,
  62                  class_map,
  63                  extent=None,
  64                  readable=True,
  65                  writable=False):
  66         """Construct ObjectDetectionLabelStore backed by a GeoJSON file.
  67 
  68         Args:
  69             uri: uri of GeoJSON file containing labels
  70             crs_transformer: CRSTransformer to convert from map coords in label
  71                 in GeoJSON file to pixel coords.
  72             class_map: ClassMap used to infer class_ids from class_name
  73                 (or label) field
  74             extent: Box used to filter the labels by extent
  75             readable: if True, expect the file to exist
  76             writable: if True, allow writing to disk
  77         """
  78         self.uri = uri
  79         self.crs_transformer = crs_transformer
  80         self.class_map = class_map
  81         self.readable = readable
  82         self.writable = writable
  83 
  84         self.labels = ObjectDetectionLabels.make_empty()
  85 
  86         json_dict = load_label_store_json(uri, readable)
  87         if json_dict:
  88             geojson = add_classes_to_geojson(json_dict, class_map)
  89             self.labels = geojson_to_labels(
  90                 geojson, crs_transformer, extent=extent)
  91 
  92     def save(self):
  93         """Save labels to URI if writable."""
  94         if self.writable:
  95             boxes = self.labels.get_boxes()
  96             class_ids = self.labels.get_class_ids().tolist()
  97             scores = self.labels.get_scores().tolist()
  98             geojson_dict = boxes_to_geojson(
  99                 boxes,
  100                 class_ids,
  101                 self.crs_transformer,
  102                 self.class_map,
  103                 scores=scores)
  104             geojson_str = json.dumps(geojson_dict)
  105             str_to_file(geojson_str, self.uri)
  106         else:
  107             raise NotWritableError('Cannot write with writable=False')
  108
  [end of src/rastervision/label_stores/object_detection_geojson_file.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/src/rastervision/label_stores/object_detection_geojson_file.py b/src/rastervision/label_stores/object_detection_geojson_file.py
  --- a/src/rastervision/label_stores/object_detection_geojson_file.py
  +++ b/src/rastervision/label_stores/object_detection_geojson_file.py
  @@ -32,9 +32,7 @@
       class_ids = []
       scores = []
   
  -    for feature in features:
  -        # Convert polygon to pixel coords and then convert to bounding box.
  -        polygon = feature['geometry']['coordinates'][0]
  +    def polygon_to_label(polygon, crs_transformer):
           polygon = [crs_transformer.map_to_pixel(p) for p in polygon]
           xmin, ymin = np.min(polygon, axis=0)
           xmax, ymax = np.max(polygon, axis=0)
  @@ -44,6 +42,19 @@
           class_ids.append(properties['class_id'])
           scores.append(properties.get('score', 1.0))
   
  +    for feature in features:
  +        geom_type = feature['geometry']['type']
  +        coordinates = feature['geometry']['coordinates']
  +        if geom_type == 'MultiPolygon':
  +            for polygon in coordinates:
  +                polygon_to_label(polygon[0], crs_transformer)
  +        elif geom_type == 'Polygon':
  +            polygon_to_label(coordinates[0], crs_transformer)
  +        else:
  +            raise Exception(
  +                "Geometries of type {} are not supported in object detection \
  +                labels.".format(geom_type))
  +
       boxes = np.array([box.npbox_format() for box in boxes], dtype=float)
       class_ids = np.array(class_ids)
       scores = np.array(scores)
  
verification_info: {"golden_diff": "diff --git a/src/rastervision/label_stores/object_detection_geojson_file.py b/src/rastervision/label_stores/object_detection_geojson_file.py\n--- a/src/rastervision/label_stores/object_detection_geojson_file.py\n+++ b/src/rastervision/label_stores/object_detection_geojson_file.py\n@@ -32,9 +32,7 @@\n     class_ids = []\n     scores = []\n \n-    for feature in features:\n-        # Convert polygon to pixel coords and then convert to bounding box.\n-        polygon = feature['geometry']['coordinates'][0]\n+    def polygon_to_label(polygon, crs_transformer):\n         polygon = [crs_transformer.map_to_pixel(p) for p in polygon]\n         xmin, ymin = np.min(polygon, axis=0)\n         xmax, ymax = np.max(polygon, axis=0)\n@@ -44,6 +42,19 @@\n         class_ids.append(properties['class_id'])\n         scores.append(properties.get('score', 1.0))\n \n+    for feature in features:\n+        geom_type = feature['geometry']['type']\n+        coordinates = feature['geometry']['coordinates']\n+        if geom_type == 'MultiPolygon':\n+            for polygon in coordinates:\n+                polygon_to_label(polygon[0], crs_transformer)\n+        elif geom_type == 'Polygon':\n+            polygon_to_label(coordinates[0], crs_transformer)\n+        else:\n+            raise Exception(\n+                \"Geometries of type {} are not supported in object detection \\\n+                labels.\".format(geom_type))\n+\n     boxes = np.array([box.npbox_format() for box in boxes], dtype=float)\n     class_ids = np.array(class_ids)\n     scores = np.array(scores)\n", "issue": "Add support for multipolygons\nEnable RV to handle geojson label stores that are encoded as multipolygons\n", "before_files": [{"content": "import json\n\nimport numpy as np\n\nfrom rastervision.core.box import Box\nfrom rastervision.labels.object_detection_labels import (ObjectDetectionLabels)\nfrom rastervision.label_stores.utils import (add_classes_to_geojson,\n                                             load_label_store_json)\nfrom rastervision.label_stores.object_detection_label_store import (\n    ObjectDetectionLabelStore)\nfrom rastervision.label_stores.utils import boxes_to_geojson\nfrom rastervision.utils.files import str_to_file, NotWritableError\n\n\ndef geojson_to_labels(geojson_dict, crs_transformer, extent=None):\n    \"\"\"Convert GeoJSON to ObjectDetectionLabels object.\n\n    If extent is provided, filter out the boxes that lie \"more than a little\n    bit\" outside the extent.\n\n    Args:\n        geojson_dict: dict in GeoJSON format\n        crs_transformer: used to convert map coords in geojson to pixel coords\n            in labels object\n        extent: Box in pixel coords\n\n    Returns:\n        ObjectDetectionLabels\n    \"\"\"\n    features = geojson_dict['features']\n    boxes = []\n    class_ids = []\n    scores = []\n\n    for feature in features:\n        # Convert polygon to pixel coords and then convert to bounding box.\n        polygon = feature['geometry']['coordinates'][0]\n        polygon = [crs_transformer.map_to_pixel(p) for p in polygon]\n        xmin, ymin = np.min(polygon, axis=0)\n        xmax, ymax = np.max(polygon, axis=0)\n        boxes.append(Box(ymin, xmin, ymax, xmax))\n\n        properties = feature['properties']\n        class_ids.append(properties['class_id'])\n        scores.append(properties.get('score', 1.0))\n\n    boxes = np.array([box.npbox_format() for box in boxes], dtype=float)\n    class_ids = np.array(class_ids)\n    scores = np.array(scores)\n\n    labels = ObjectDetectionLabels(boxes, class_ids, scores=scores)\n    if extent is not None:\n        labels = ObjectDetectionLabels.get_overlapping(\n            labels, extent, ioa_thresh=0.8, clip=True)\n    return labels\n\n\nclass ObjectDetectionGeoJSONFile(ObjectDetectionLabelStore):\n    def __init__(self,\n                 uri,\n                 crs_transformer,\n                 class_map,\n                 extent=None,\n                 readable=True,\n                 writable=False):\n        \"\"\"Construct ObjectDetectionLabelStore backed by a GeoJSON file.\n\n        Args:\n            uri: uri of GeoJSON file containing labels\n            crs_transformer: CRSTransformer to convert from map coords in label\n                in GeoJSON file to pixel coords.\n            class_map: ClassMap used to infer class_ids from class_name\n                (or label) field\n            extent: Box used to filter the labels by extent\n            readable: if True, expect the file to exist\n            writable: if True, allow writing to disk\n        \"\"\"\n        self.uri = uri\n        self.crs_transformer = crs_transformer\n        self.class_map = class_map\n        self.readable = readable\n        self.writable = writable\n\n        self.labels = ObjectDetectionLabels.make_empty()\n\n        json_dict = load_label_store_json(uri, readable)\n        if json_dict:\n            geojson = add_classes_to_geojson(json_dict, class_map)\n            self.labels = geojson_to_labels(\n                geojson, crs_transformer, extent=extent)\n\n    def save(self):\n        \"\"\"Save labels to URI if writable.\"\"\"\n        if self.writable:\n            boxes = self.labels.get_boxes()\n            class_ids = self.labels.get_class_ids().tolist()\n            scores = self.labels.get_scores().tolist()\n            geojson_dict = boxes_to_geojson(\n                boxes,\n                class_ids,\n                self.crs_transformer,\n                self.class_map,\n                scores=scores)\n            geojson_str = json.dumps(geojson_dict)\n            str_to_file(geojson_str, self.uri)\n        else:\n            raise NotWritableError('Cannot write with writable=False')\n", "path": "src/rastervision/label_stores/object_detection_geojson_file.py"}]}