> ## Documentation Index
> Fetch the complete documentation index at: https://docs.labelbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Import geospatial annotations

> Developer guide for importing annotations on geospatial data and sample import formats.

<CardGroup cols={2}>
  <Card title="Open In Colab" icon="infinity" iconType="solid" href="https://colab.research.google.com/github/Labelbox/labelbox-notebooks/blob/main/annotation_import/tiled.ipynb" horizontal />

  <Card title="GitHub" icon="github" iconType="solid" href="https://github.com/Labelbox/labelbox-notebooks/blob/main/annotation_import/tiled.ipynb" horizontal />
</CardGroup>

## Overview

To import annotations in Labelbox, you need to create an annotations payload. In this section, we provide this payload for every supported annotation type.

### Annotation payload types

Labelbox supports two formats for the annotations payload:

* Python annotation types (recommended)

  * Provides a seamless transition between third-party platforms, machine learning pipelines, and Labelbox.
  * Allows you to build annotations locally with local file paths, numpy arrays, or URLs
  * Easily convert Python Annotation Type format to NDJSON format to quickly import annotations to Labelbox
  * Supports one-level nested classification (radio, checklist, or free-form text) under a tool or classification annotation.

* JSON

  * Skips formatting annotation payload in the Labelbox Python annotation type
  * Supports any levels of nested classification (radio, checklist, or free-form text) under a tool or classification annotation.

### Label import types

Labelbox additionally supports two types of label imports:

* [Model-assisted labeling (MAL)](/docs/model-assisted-labeling)
  * This workflow allows you to import computer-generated predictions (or simply annotations created outside of Labelbox) as pre-labels on an asset.
* [Ground truth](https://www.google.com/url?q=https%3A%2F%2Fdocs.labelbox.com%2Fdocs%2Fimport-ground-truth)
  * This workflow functionality allows you to bulk import your ground truth annotations from an external or third-party labeling system into Labelbox *Annotate*. Using the label import API to import external data is a useful way to consolidate and migrate all annotations into Labelbox as a single source of truth.

## Supported annotations

The following annotations are supported for an geospatial data row:

* Radio
* Checklist
* Free-form text
* Point
* Polyline
* Polygon
* Bounding Box

## Classifications

### Radio (single choice)

<CodeGroup>
  ```python Python annotation theme={null}
  radio_annotation = lb_types.ClassificationAnnotation(
      name="radio_question",
      value=lb_types.Radio(answer =
          lb_types.ClassificationAnswer(name = "first_radio_answer")
      )
  )
  ```

  ```json NDJSON theme={null}
  radio_annotation_ndjson = {
    "name": "radio_question",
    "answer": {"name": "first_radio_answer"}
  }
  ```
</CodeGroup>

### Checklist (multiple choice)

<CodeGroup>
  ```python Python annotation theme={null}
  checklist_annotation = lb_types.ClassificationAnnotation(
      name="checklist_question",
      value=lb_types.Checklist(answer = [
          lb_types.ClassificationAnswer(name = "first_checklist_answer"),
          lb_types.ClassificationAnswer(name = "second_checklist_answer"),
          lb_types.ClassificationAnswer(name = "third_checklist_answer")
      ])
    )
  ```

  ```json NDJSON theme={null}
  checklist_annotation_ndjson = {
    "name": "checklist_question",
    "answer": [
      {"name": "first_checklist_answer"},
      {"name": "second_checklist_answer"},
      {"name": "third_checklist_answer"},
    ]
  }
  ```
</CodeGroup>

### Free-form text

<CodeGroup>
  ```python Python annotation theme={null}
  text_annotation = lb_types.ClassificationAnnotation(
      name = "free_text",
      value = lb_types.Text(answer="sample text")
  )
  ```

  ```json NDJSON theme={null}
  text_annotation_ndjson = {
    "name": "free_text",
    "answer": "sample text",
  }
  ```
</CodeGroup>

### Nested classifications

<CodeGroup>
  ```python Python annotation theme={null}
  nested_radio_annotation = lb_types.ClassificationAnnotation(
    name="nested_radio_question",
    value=lb_types.Radio(
      answer=lb_types.ClassificationAnswer(
        name="first_radio_answer",
        classifications=[
          lb_types.ClassificationAnnotation(
            name="sub_radio_question",
            value=lb_types.Radio(
              answer=lb_types.ClassificationAnswer(
                name="first_sub_radio_answer"
              )
            )
          )
        ]
      )
    )
  )

  nested_checklist_annotation = lb_types.ClassificationAnnotation(
    name="nested_checklist_question",
    value=lb_types.Checklist(
      answer=[lb_types.ClassificationAnswer(
        name="first_checklist_answer",
        classifications=[
          lb_types.ClassificationAnnotation(
            name="sub_checklist_question",
            value=lb_types.Checklist(
              answer=[lb_types.ClassificationAnswer(
              name="first_sub_checklist_answer"
            )]
          ))
        ]
      )]
    )
  )
  ```

  ```json NDJSON theme={null}
  nested_radio_annotation_ndjson= {
    "name": "nested_radio_question",
    "answer": {
        "name": "first_radio_answer",
        "classifications": [
          {
            "name": "sub_radio_question",
            "answer": {"name": "first_sub_radio_answer"}
          }
        ]
      }
  }

  nested_checklist_annotation_ndjson = {
    "name": "nested_checklist_question",
    "answer": [{
        "name": "first_checklist_answer",
        "classifications" : [
          {
            "name": "sub_checklist_question",
            "answer": {"name": "first_sub_checklist_answer"}
          }
        ]
    }]
  }
  ```
</CodeGroup>

### Point

<CodeGroup>
  ```python Python annotation theme={null}
  point_annotation = lb_types.ObjectAnnotation(
    name = "point_geo",
    value = lb_types.Point(x=-99.20647859573366, y=19.40018029091072),
  )
  ```

  ```python NDJSON theme={null}
  point_annotation_ndjson = {
      "name": "point_geo",
      "point": {
           "x": -99.20647859573366,
           "y": 19.40018029091072
       }
  }
  ```
</CodeGroup>

### Polyline

<CodeGroup>
  ```python Python annotation theme={null}
  # Coordinates in the desired EPSG coordinate system
  coords = [
              [
                  -99.20842051506044,
                  19.40032196622975
              ],
              [
                  -99.20809864997865,
                  19.39758963475322
              ],
              [
                  -99.20758366584778,
                  19.39776167179227
              ],
              [
                  -99.20728325843811,
                  19.3973265189299
              ]
          ]

  line_points = []
  line_points_ndjson = []

  for sub in coords: 
    line_points.append(lb_types.Point(x=sub[0], y=sub[1]))
    line_points_ndjson.append({"x":sub[0], "y":sub[1]})

  # Python Annotation 
  polyline_annotation = lb_types.ObjectAnnotation(
    name = "polyline_geo",
    value = lb_types.Line(points=line_points),
  )
  ```

  ```python NDJSON theme={null}
  # Coordinates in the desired EPSG coordinate system
  coords = [
    [
      -99.20842051506044,
      19.40032196622975
    ],
    [
      -99.20809864997865,
      19.39758963475322
    ],
    [
      -99.20758366584778,
      19.39776167179227
    ],
    [
      -99.20728325843811,
      19.3973265189299
    ]
  ]

  line_points = []
  line_points_ndjson = []

  for sub in coords: 
    line_points.append(lb_types.Point(x=sub[0], y=sub[1]))
    line_points_ndjson.append({"x":sub[0], "y":sub[1]})

  # NDJSON 
  polyline_annotation_ndjson = {
    "name": "polyline_geo",
    "line": line_points_ndjson
  }
  ```
</CodeGroup>

### Polygon

<CodeGroup>
  ```python Python annotation theme={null}
  polygon_points = []

  for sub in coords_polygon: 
    polygon_points.append(lb_types.Point(x=sub[0], y=sub[1]))


  polygon_annotation = lb_types.ObjectAnnotation(
    name = "polygon_geo",
    value = lb_types.Polygon(points=polygon_points),
  )
  ```

  ```python NDJSON theme={null}
  # Coordinates in the desired EPSG coordinate system

  polygon_points_ndjson = []

  for sub in coords_polygon:
    polygon_points_ndjson.append({"x":sub[0], "y":sub[1]})

  polygon_annotation_ndjson = {
    "name": "polygon_geo",
    "polygon": polygon_points_ndjson
  }
  ```

  ```python coords_polygon expandable theme={null}
  coords_polygon = [
      [
          -99.21042680740356,
          19.40036244486966
      ],
      [
          -99.2104160785675,
          19.40017017124035
      ],
      [
          -99.2103409767151,
          19.400008256428897
      ],
      [
          -99.21014785766603,
          19.400008256428897
      ],
      [
          -99.21019077301027,
          19.39983622176518
      ],
      [
          -99.21022295951845,
          19.399674306621385
      ],
      [
          -99.21029806137086,
          19.39951239131646
      ],
      [
          -99.2102873325348,
          19.399340356128437
      ],
      [
          -99.21025514602663,
          19.399117722085677
      ],
      [
          -99.21024441719057,
          19.39892544698541
      ],
      [
          -99.2102336883545,
          19.39874329141769
      ],
      [
          -99.21021223068239,
          19.398561135646027
      ],
      [
          -99.21018004417421,
          19.398399219233365
      ],
      [
          -99.21011567115785,
          19.39822718286836
      ],
      [
          -99.20992255210878,
          19.398136104719125
      ],
      [
          -99.20974016189577,
          19.398085505725305
      ],
      [
          -99.20957922935487,
          19.398004547302467
      ],
      [
          -99.20939683914186,
          19.39792358883935
      ],
      [
          -99.20918226242067,
          19.39786286996558
      ],
      [
          -99.20899987220764,
          19.397822390703805
      ],
      [
          -99.20891404151918,
          19.397994427496787
      ],
      [
          -99.20890331268312,
          19.398176583902874
      ],
      [
          -99.20889258384706,
          19.398368859888045
      ],
      [
          -99.20889258384706,
          19.398540896103246
      ],
      [
          -99.20890331268312,
          19.39872305189756
      ],
      [
          -99.20889258384706,
          19.39890520748796
      ],
      [
          -99.20889258384706,
          19.39907724313608
      ],
      [
          -99.20889258384706,
          19.399259398329956
      ],
      [
          -99.20890331268312,
          19.399431433603585
      ],
      [
          -99.20890331268312,
          19.39961358840092
      ],
      [
          -99.20890331268312,
          19.399785623300048
      ],
      [
          -99.20897841453552,
          19.399937418648214
      ],
      [
          -99.20919299125673,
          19.399937418648214
      ],
      [
          -99.2093861103058,
          19.39991717927664
      ],
      [
          -99.20956850051881,
          19.39996777770086
      ],
      [
          -99.20961141586305,
          19.40013981222548
      ],
      [
          -99.20963287353517,
          19.40032196622975
      ],
      [
          -99.20978307724,
          19.4004130431554
      ],
      [
          -99.20996546745302,
          19.40039280384301
      ],
      [
          -99.21019077301027,
          19.400372564528084
      ],
      [
          -99.21042680740356,
          19.40036244486966
      ]

  ]
  ```
</CodeGroup>

### Bounding Box

<CodeGroup>
  ```python Python annotation theme={null}
  bbox_top_left = lb_types.Point(x= -99.20746564865112, y=19.39799442829336)
  bbox_bottom_right = lb_types.Point(x=-99.20568466186523, y=19.39925939999194)

  # Python Annotation

  bbox_annotation = lb_types.ObjectAnnotation(
  name = "bbox_geo",
  value = lb_types.Rectangle(start=bbox_top_left, end=bbox_bottom_right)
  )

  ```

  ```python NDJSON theme={null}
  # Coordinates in the desired EPSG coordinate system
  coord_object =  {
    "coordinates" : [[
      [
        -99.20746564865112,
        19.39799442829336
      ],
      [
        -99.20746564865112,
        19.39925939999194
      ],
      [
        -99.20568466186523,
        19.39925939999194
      ],
      [
        -99.20568466186523,
        19.39799442829336
      ],
      [
        -99.20746564865112,
        19.39799442829336
      ]
    ]]
  }

  # NDJSON
  bbox_annotation_ndjson = {
    "name" : "bbox_geo",
    "bbox" : {
      'top': coord_object["coordinates"][0][1][1],
      'left': coord_object["coordinates"][0][1][0],
      'height': coord_object["coordinates"][0][3][1] - coord_object["coordinates"][0][1][1],
      'width': coord_object["coordinates"][0][3][0] - coord_object["coordinates"][0][1][0]
    }
  }
  ```
</CodeGroup>

### Mask to bounding box

Additionally, you create a bounding box with a segmentation mask using the code below.

<CodeGroup>
  ```python Python annotation theme={null}
  # Let's create another polygon annotation with Python annotation tools that draws the image using cv2 libraries

  hsv = cv2.cvtColor(tiled*image_data.value, cv2.COLOR_RGB2HSV)
  mask = cv2.inRange(hsv, (25, 50, 25), (100, 150, 255))
  kernel = np.ones((15, 20), np.uint8)
  mask = cv2.erode(mask, kernel)
  mask = cv2.dilate(mask, kernel)
  mask_annotation = lb_types.MaskData.from_2D_arr(mask)
  mask_data = lb_types.Mask(mask=mask_annotation, color=[255, 255, 255])
  h, w, * = tiled_image_data.value.shape
  pixel_bounds = lb_types.TiledBounds(epsg=lb_types.EPSG.SIMPLEPIXEL,
  bounds=[lb_types.Point(x=0, y=0),
  lb_types.Point(x=w, y=h)])
  transformer = lb_types.EPSGTransformer.create_pixel_to_geo_transformer(
  src_epsg=pixel_bounds.epsg,
  pixel_bounds=pixel_bounds,
  geo_bounds=tiled_image_data.tile_bounds,
  zoom=20)
  pixel_polygons = mask_data.shapely.simplify(3)
  list_of_polygons = [transformer(lb_types.Polygon.from_shapely(p)) for p in pixel_polygons.geoms]
  polygon_annotation_two = lb_types.ObjectAnnotation(value=list_of_polygons[0], name="polygon_geo_2")

  ```
</CodeGroup>

### Tool with nested classification

<CodeGroup>
  ```python Python annotation theme={null}
  tool_with_radio_subclass_annotation = lb_types.ObjectAnnotation(
      name=# Feature name,
      value=# Add tool annotation (lb_types."tool"),
      classifications=[
          lb_types.ClassificationAnnotation(
              name="sub_radio_question",
              value=lb_types.Radio(answer=lb_types.ClassificationAnswer(
                  name="first_sub_radio_answer")))
      ])
  ```

  ```python Bounding Box Example theme={null}
  bbox_with_radio_subclass_annotation = lb_types.ObjectAnnotation(
      name="bbox_with_radio_subclass",
      value=lb_types.Rectangle(
          start=lb_types.Point(x=541, y=933),  # x = left, y = top
          end=lb_types.Point(x=871, y=1124),  # x= left + width , y = top + height
      ),
      classifications=[
          lb_types.ClassificationAnnotation(
              name="sub_radio_question",
              value=lb_types.Radio(answer=lb_types.ClassificationAnswer(
                  name="first_sub_radio_answer")))
      ])
  ```

  ```python NDJSON theme={null}
  bbox_with_radio_subclass_ndjson = {
      "name": "bbox_with_radio_subclass",
      "classifications": [{
          "name": "sub_radio_question",
          "answer": {
              "name": "first_sub_radio_answer"
          }
      }],
      "bbox": {
          "top": 933,
          "left": 541,
          "height": 191,
          "width": 330
      }
  }
  ```
</CodeGroup>

## Example: Import pre-labels or ground truths

The steps to import annotations as pre-labels (machine-assisted learning) are similar to those to import annotations as ground truth labels. However, they vary slightly, and we will describe the differences for each scenario.

### Before you start

The below imports are needed to use the code examples in this section.

<CodeGroup>
  ```python Python theme={null}
  import uuid
  import numpy as np
  import cv2
  import labelbox as lb
  import labelbox.types as lb_types
  ```
</CodeGroup>

Replace the value of `API_KEY` with a valid [API key](/reference/create-api-key) to connect to the Labelbox client.

<CodeGroup>
  ```python Python theme={null}
  API_KEY = None
  client = lb.Client(API_KEY)
  ```
</CodeGroup>

### Step 1: Import data rows

Data rows must first be uploaded to **Catalog** to attach annotations.

This example shows how to create a data row in **Catalog** by attaching it to a [dataset](/reference/dataset) .

<CodeGroup>
  ```python theme={null}
  top_left_bound = lb_types.Point(x=-99.21052827588443, y=19.400498983095076)
  bottom_right_bound = lb_types.Point(x=-99.20534818927473, y=19.39533555271248)

  epsg = lb_types.EPSG.EPSG4326
  bounds = lb_types.TiledBounds(epsg=epsg, bounds=[top_left_bound, bottom_right_bound])
  global_key = "mexico_city"

  tile_layer = lb_types.TileLayer(
      url="https://s3-us-west-1.amazonaws.com/lb-tiler-layers/mexico_city/{z}/{x}/{y}.png"
  )

  tiled_image_data = lb_types.TiledImageData(tile_layer=tile_layer,
                                    tile_bounds=bounds,
                                    zoom_levels=[17, 23])

  asset = {
      "row_data": tiled_image_data.asdict(),
      "global_key": global_key,
      "media_type": "TMS_GEO"
  }

  dataset = client.create_dataset(name="geo_demo_dataset")
  task= dataset.create_data_rows([asset])
  print("Errors:",task.errors)
  print("Failed data rows:", task.failed_data_rows)
  ```
</CodeGroup>

### Step 2: Set up ontology

Your project ontology should support the tools and classifications required by your annotations. To ensure accurate schema feature mapping, the value used as the `name` parameter should match the value of the `name` field in your annotation.

For example, when we created an annotation above, we provided a name`annotation_name`. Now, when we set up our ontology, we must ensure that the name of our bounding box tool is also `anotations_name`. The same alignment must hold true for the other tools and classifications we create in our ontology.

This example shows how to create an ontology containing all supported [annotation types](#supported-annotations) .

<CodeGroup>
  ```python Python expandable theme={null}
  ontology_builder = lb.OntologyBuilder(
      tools=[
          lb.Tool(tool=lb.Tool.Type.POINT, name="point_geo"),
          lb.Tool(tool=lb.Tool.Type.LINE, name="polyline_geo"),
          lb.Tool(tool=lb.Tool.Type.POLYGON, name="polygon_geo"),
          lb.Tool(tool=lb.Tool.Type.POLYGON, name="polygon_geo_2"),
          lb.Tool(tool=lb.Tool.Type.BBOX, name="bbox_geo"),
          lb.Tool(
            tool=lb.Tool.Type.BBOX,
            name="bbox_checklist_geo",
            classifications=[
                  lb.Classification(
                      class_type=lb.Classification.Type.CHECKLIST,
                      name="checklist_class_name",
                      options=[
                        lb.Option(value="first_checklist_answer")
                      ]
                  ),
              ]
            ),
          lb.Tool(
            tool=lb.Tool.Type.BBOX,
            name="bbox_text_geo",
            classifications=[
                  lb.Classification(
                      class_type=lb.Classification.Type.TEXT,
                      name="free_text_geo"
                  ),
              ]
            )
        ],
        classifications = [
            lb.Classification(
                class_type=lb.Classification.Type.CHECKLIST,
                name="checklist_question_geo",
                options=[
                    lb.Option(value="first_checklist_answer"),
                    lb.Option(value="second_checklist_answer"),
                    lb.Option(value="third_checklist_answer")
                ]
            ),
            lb.Classification(
                class_type=lb.Classification.Type.RADIO,
                name="radio_question_geo",
                options=[
                    lb.Option(value="first_radio_answer")
                ]
            ),

        lb.Classification(
          class_type=lb.Classification.Type.RADIO,
          name="nested_radio_question",
          options=[
            lb.Option(value="first_radio_answer",
              options=[
                  lb.Classification(
                    class_type=lb.Classification.Type.RADIO,
                    name="sub_radio_question",
                    options=[
                      lb.Option(value="first_sub_radio_answer")
                    ]
                ),
              ]
            ),
          ],
        ),
        lb.Classification(class_type=lb.Classification.Type.TEXT,
                          name="free_text"),
        lb.Classification(
          class_type=lb.Classification.Type.CHECKLIST,
          name="nested_checklist_question",
          options=[
              lb.Option("first_checklist_answer",
                options=[
                  lb.Classification(
                      class_type=lb.Classification.Type.CHECKLIST,
                      name="sub_checklist_question",
                      options=[lb.Option("first_sub_checklist_answer")]
                  )
              ]
            )
          ]
      )
    ]
  )

  ontology = client.create_ontology("Ontology Geospatial Annotations",
  ontology_builder.asdict(),
  media_type=lb.MediaType.Geospatial_Tile)
  ```
</CodeGroup>

### Step 3: Set Up a Labeling Project

<CodeGroup>
  ```python Python theme={null}
  # Create a project
  project = client.create_project(
    name="Geospatial Project Demo",
    media_type=lb.MediaType.Geospatial_Tile
  )

  # Connect the ontology

  project.connect_ontology(ontology)
  ```
</CodeGroup>

### Step 4: Send Data Rows to Project

<CodeGroup>
  ```python Python theme={null}
  # Create a batch to send to your MAL project
  batch = project.create_batch(
    "first-batch-geo-demo", # Each batch in a project must have a unique name
    global_keys=[global_key], # Paginated collection of data row objects, list of data row ids or global keys
    priority=5 # priority between 1(Highest) - 5(lowest)
  )

  print("Batch: ", batch)
  ```
</CodeGroup>

### Step 5: Create annotation payloads

For help understanding annotation payloads, see [overview](#overview). To declare payloads, you can use Python annotation types (*preferred*) or NDJSON objects. For annotations that you want to import as ground truth labels, you can also specify [benchmarks](/docs/benchmark) using the `is_benchmark_reference` flag.

These examples demonstrate each format and how to compose annotations into labels attached to data rows.

<CodeGroup>
  ```python Python annotation payload theme={null}
  labels =[]
  labels.append(
      lb_types.Label(
          data={
              "global_key": global_key,
              "tile_layer": tile_layer,
              "tile_bounds":bounds,
              "zoom_levels": [12, 20]
          },
          annotations = [
              point_annotation,
              polyline_annotation,
              polygon_annotation,
              bbox_annotation,
              radio_annotation,
              bbox_with_checklist_subclass,
              bbox_with_free_text_subclass,
              checklist_annotation,
              polygon_annotation_two,
              nested_checklist_annotation,
              nested_radio_annotation,
              text_annotation
          ],
          # Optional: set the label as a benchmark
          # Only supported for groud truth imports
          is_benchmark_reference = True
      )
  )
  ```

  ```python NDJSON payload theme={null}
  label_ndjson = []

  for annotations in [point_annotation_ndjson,
                      polyline_annotation_ndjson,
                      polygon_annotation_ndjson,
                      bbox_annotation_ndjson,
                      radio_annotation_ndjson,
                      bbox_with_checklist_subclass_ndjson,
                      bbox_with_free_text_subclass_ndjson,
                      checklist_annotation_ndjson,
                      nested_checklist_annotation_ndjson,
                      nested_radio_annotation_ndjson,
                      text_annotation_ndjson
                      ]:
    annotations.update({
        'dataRow': {
            'globalKey': global_key
        }
    })
    label_ndjson.append(annotations)
  ```
</CodeGroup>

### Step 6: Import annotation payload

For prelabeled (model-assisted labeling) scenarios, pass your payload as the value of the `predictions` parameter. For ground truths, pass the payload to the `labels` parameter.

#### Option A: Upload as [prelabels (model assisted labeling)](/docs/model-assisted-labeling)

This option is helpful for speeding up the initial labeling process and reducing the manual labeling workload for high-volume datasets.

<CodeGroup>
  ```python MAL import theme={null}
  # Upload MAL label for this data row in project
  upload_job = lb.MALPredictionImport.create_from_objects(
      client = client,
      project_id = project.uid,
      name="mal_job"+str(uuid.uuid4()),
      predictions=label
  )

  print(f"Errors: {upload_job.errors}", )
  print(f"Status of uploads: {upload_job.statuses}")
  ```
</CodeGroup>

#### Option B: Upload to a labeling project as [ground truth](/docs/import-ground-truth)

This option is helpful for loading high-confidence labels from another platform or previous projects that just need review rather than manual labeling effort.

<CodeGroup>
  ```python Label import theme={null}
  # Upload label for this data row in project
  upload_job = lb.LabelImport.create_from_objects(
      client = client,
      project_id = project.uid,
      name="label_import_job"+str(uuid.uuid4()),
      labels=label
  )

  print(f"Errors: {upload_job.errors}", )
  print(f"Status of uploads: {upload_job.statuses}")
  ```
</CodeGroup>
