コード例 #1
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_partial_apply(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        ProjectionExtension.ext(proj_item).apply(epsg=1111)

        self.assertEqual(ProjectionExtension.ext(proj_item).epsg, 1111)
        proj_item.validate()
コード例 #2
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_item_ext_add_to(self) -> None:
        item = pystac.Item.from_file(self.example_uri)
        item.stac_extensions.remove(ProjectionExtension.get_schema_uri())
        self.assertNotIn(ProjectionExtension.get_schema_uri(), item.stac_extensions)

        _ = ProjectionExtension.ext(item, add_if_missing=True)

        self.assertIn(ProjectionExtension.get_schema_uri(), item.stac_extensions)
コード例 #3
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_bbox(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:bbox", proj_item.properties)
        proj_bbox = ProjectionExtension.ext(proj_item).bbox
        self.assertEqual(proj_bbox, proj_item.properties["proj:bbox"])

        # Set
        ProjectionExtension.ext(proj_item).bbox = [1.0, 2.0, 3.0, 4.0]
        self.assertEqual(proj_item.properties["proj:bbox"], [1.0, 2.0, 3.0, 4.0])

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).bbox,
            ProjectionExtension.ext(proj_item).bbox,
        )
        self.assertEqual(ProjectionExtension.ext(asset_prop).bbox, [1.0, 2.0, 3.0, 4.0])

        # Set to Asset
        asset_value = [10.0, 20.0, 30.0, 40.0]
        ProjectionExtension.ext(asset_no_prop).bbox = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).bbox,
            ProjectionExtension.ext(proj_item).bbox,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).bbox, asset_value)

        # Validate
        proj_item.validate()
コード例 #4
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_wkt2(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:wkt2", proj_item.properties)
        proj_wkt2 = ProjectionExtension.ext(proj_item).wkt2
        self.assertEqual(proj_wkt2, proj_item.properties["proj:wkt2"])

        # Set
        ProjectionExtension.ext(proj_item).wkt2 = WKT2
        self.assertEqual(WKT2, proj_item.properties["proj:wkt2"])

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).wkt2,
            ProjectionExtension.ext(proj_item).wkt2,
        )
        self.assertTrue(
            "TEST_TEXT" in get_opt(ProjectionExtension.ext(asset_prop).wkt2)
        )

        # Set to Asset
        asset_value = "TEST TEXT 2"
        ProjectionExtension.ext(asset_no_prop).wkt2 = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).wkt2,
            ProjectionExtension.ext(proj_item).wkt2,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).wkt2, asset_value)

        # Validate
        proj_item.validate()
コード例 #5
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_epsg(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:epsg", proj_item.properties)
        proj_epsg = ProjectionExtension.ext(proj_item).epsg
        self.assertEqual(proj_epsg, proj_item.properties["proj:epsg"])

        # Set
        assert proj_epsg is not None
        ProjectionExtension.ext(proj_item).epsg = proj_epsg + 100
        self.assertEqual(proj_epsg + 100, proj_item.properties["proj:epsg"])

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).epsg,
            ProjectionExtension.ext(proj_item).epsg,
        )
        self.assertEqual(ProjectionExtension.ext(asset_prop).epsg, 9999)

        # Set to Asset
        ProjectionExtension.ext(asset_no_prop).epsg = 8888
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).epsg,
            ProjectionExtension.ext(proj_item).epsg,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).epsg, 8888)

        # Validate
        proj_item.validate()
コード例 #6
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_shape(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:shape", proj_item.properties)
        proj_shape = ProjectionExtension.ext(proj_item).shape
        self.assertEqual(proj_shape, proj_item.properties["proj:shape"])

        # Set
        new_val = [100, 200]
        ProjectionExtension.ext(proj_item).shape = new_val
        self.assertEqual(proj_item.properties["proj:shape"], new_val)

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).shape,
            ProjectionExtension.ext(proj_item).shape,
        )
        self.assertEqual(ProjectionExtension.ext(asset_prop).shape, [16781, 16621])

        # Set to Asset
        asset_value = [1, 2]
        ProjectionExtension.ext(asset_no_prop).shape = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).shape,
            ProjectionExtension.ext(proj_item).shape,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).shape, asset_value)

        # Validate
        proj_item.validate()
コード例 #7
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_apply(self) -> None:
        item = next(iter(TestCases.test_case_2().get_all_items()))
        self.assertFalse(ProjectionExtension.has_extension(item))

        ProjectionExtension.add_to(item)
        ProjectionExtension.ext(item).apply(
            4326,
            wkt2=WKT2,
            projjson=PROJJSON,
            geometry=item.geometry,
            bbox=item.bbox,
            centroid={"lat": 0.0, "lon": 1.0},
            shape=[100, 100],
            transform=[30.0, 0.0, 224985.0, 0.0, -30.0, 6790215.0, 0.0, 0.0, 1.0],
        )
コード例 #8
0
ファイル: test_create.py プロジェクト: stac-utils/stactools
    def test_one_datetime(self) -> None:
        item = create.item(self.path)
        self.assertEqual(item.id,
                         os.path.splitext(os.path.basename(self.path))[0])
        self.assertIsNotNone(item.datetime)
        self.assertEqual(
            item.geometry, {
                'type':
                'Polygon',
                'coordinates':
                [[[-95.780872, 29.517294], [-95.783782, 29.623358],
                  [-96.041791, 29.617689], [-96.038613, 29.511649],
                  [-95.780872, 29.517294]]]
            })
        self.assertEqual(item.bbox,
                         [-96.041791, 29.511649, -95.780872, 29.623358])
        self.assertIsNone(item.common_metadata.start_datetime)
        self.assertIsNone(item.common_metadata.end_datetime)

        projection = ProjectionExtension.ext(item)
        self.assertEqual(projection.epsg, 32615)
        self.assertIsNone(projection.wkt2)
        self.assertIsNone(projection.projjson)
        self.assertEqual(
            projection.transform,
            [97.69921875, 0.0, 205437.0, 0.0, -45.9609375, 3280290.0])
        self.assertEqual(projection.shape, (256, 256))

        data = item.assets['data']
        self.assertEqual(data.href, self.path)
        self.assertIsNone(data.title)
        self.assertIsNone(data.description)
        self.assertEqual(data.roles, ['data'])
        self.assertEqual(data.media_type, None)
        item.validate()
コード例 #9
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_summaries_adds_uri(self) -> None:
        col = pystac.Collection.from_file(self.example_uri)
        col.stac_extensions = []
        self.assertRaisesRegex(
            pystac.ExtensionNotImplemented,
            r"Could not find extension schema URI.*",
            ProjectionExtension.summaries,
            col,
            False,
        )
        _ = ProjectionExtension.summaries(col, True)

        self.assertIn(ProjectionExtension.get_schema_uri(), col.stac_extensions)

        ProjectionExtension.remove_from(col)
        self.assertNotIn(ProjectionExtension.get_schema_uri(), col.stac_extensions)
コード例 #10
0
ファイル: create.py プロジェクト: stac-utils/stactools
def item(href: str,
         read_href_modifier: Optional[ReadHrefModifier] = None) -> Item:
    """Creates a STAC Item from the asset at the provided href.

    The `read_href_modifer` argument can be used to modify the href for the
    rasterio read, e.g. if you need to sign a url.

    This function is intentionally minimal in its signature and capabilities. If
    you need to customize your Item, do so after creation.

    This function sets:
    - id
    - geometry
    - bbox
    - datetime (to the time of item creation): you'll probably want to change this
    - the proj extension
        - either the EPSG code or, if not available, the WKT2
        - transform
        - shape
    - a single asset with key 'data'
        - asset href
        - asset roles to ['data']

    In particular, the datetime and asset media type fields most likely need to be updated.
    """
    id = os.path.splitext(os.path.basename(href))[0]
    if read_href_modifier:
        modified_href = read_href_modifier(href)
    else:
        modified_href = href
    with rasterio.open(modified_href) as dataset:
        crs = dataset.crs
        proj_bbox = dataset.bounds
        proj_transform = list(dataset.transform)[0:6]
        proj_shape = dataset.shape
    proj_geometry = shapely.geometry.mapping(shapely.geometry.box(*proj_bbox))
    geometry = stactools.core.projection.reproject_geom(crs,
                                                        'EPSG:4326',
                                                        proj_geometry,
                                                        precision=6)
    bbox = list(shapely.geometry.shape(geometry).bounds)
    item = Item(id=id,
                geometry=geometry,
                bbox=bbox,
                datetime=datetime.datetime.now(),
                properties={})

    projection = ProjectionExtension.ext(item, add_if_missing=True)
    epsg = crs.to_epsg()
    if epsg:
        projection.epsg = epsg
    else:
        projection.wkt2 = crs.to_wkt('WKT2')
    projection.transform = proj_transform
    projection.shape = proj_shape

    item.add_asset('data', Asset(href=href, roles=['data']))

    return item
コード例 #11
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_extension_not_implemented(self) -> None:
        # Should raise exception if Item does not include extension URI
        item = pystac.Item.from_file(self.example_uri)
        item.stac_extensions.remove(ProjectionExtension.get_schema_uri())

        with self.assertRaises(pystac.ExtensionNotImplemented):
            _ = ProjectionExtension.ext(item)

        # Should raise exception if owning Item does not include extension URI
        asset = item.assets["B8"]

        with self.assertRaises(pystac.ExtensionNotImplemented):
            _ = ProjectionExtension.ext(asset)

        # Should succeed if Asset has no owner
        ownerless_asset = pystac.Asset.from_dict(asset.to_dict())
        _ = ProjectionExtension.ext(ownerless_asset)
コード例 #12
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_get_summaries(self) -> None:
        col = pystac.Collection.from_file(self.example_uri)
        proj_summaries = ProjectionExtension.summaries(col)

        # Get

        epsg_summaries = proj_summaries.epsg
        assert epsg_summaries is not None
        self.assertListEqual(epsg_summaries, [32614])
コード例 #13
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_projjson(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:projjson", proj_item.properties)
        proj_projjson = ProjectionExtension.ext(proj_item).projjson
        self.assertEqual(proj_projjson, proj_item.properties["proj:projjson"])

        # Set
        ProjectionExtension.ext(proj_item).projjson = PROJJSON
        self.assertEqual(PROJJSON, proj_item.properties["proj:projjson"])

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).projjson,
            ProjectionExtension.ext(proj_item).projjson,
        )
        asset_prop_json = ProjectionExtension.ext(asset_prop).projjson
        assert asset_prop_json is not None
        self.assertEqual(asset_prop_json["id"]["code"], 9999)

        # Set to Asset
        asset_value = deepcopy(PROJJSON)
        asset_value["id"]["code"] = 7777
        ProjectionExtension.ext(asset_no_prop).projjson = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).projjson,
            ProjectionExtension.ext(proj_item).projjson,
        )
        asset_no_prop_json = ProjectionExtension.ext(asset_no_prop).projjson
        assert asset_no_prop_json is not None
        self.assertEqual(asset_no_prop_json["id"]["code"], 7777)

        # Validate
        proj_item.validate()

        # Ensure setting bad projjson fails validation
        with self.assertRaises(pystac.STACValidationError):
            ProjectionExtension.ext(proj_item).projjson = {"bad": "data"}
            proj_item.validate()
コード例 #14
0
ファイル: test_eo.py プロジェクト: stac-utils/pystac
    def test_migration(self) -> None:
        with open(self.item_0_8_path) as src:
            item_dict = json.load(src)

        self.assertIn("eo:epsg", item_dict["properties"])

        item = Item.from_file(self.item_0_8_path)

        self.assertNotIn("eo:epsg", item.properties)
        self.assertIn("proj:epsg", item.properties)
        self.assertIn(ProjectionExtension.get_schema_uri(), item.stac_extensions)
コード例 #15
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_set_summaries(self) -> None:
        col = pystac.Collection.from_file(self.example_uri)
        proj_summaries = ProjectionExtension.summaries(col)

        # Set

        proj_summaries.epsg = [4326]

        col_dict = col.to_dict()
        self.assertEqual(len(col_dict["summaries"]["proj:epsg"]), 1)
        self.assertEqual(col_dict["summaries"]["proj:epsg"][0], 4326)
コード例 #16
0
ファイル: layer.py プロジェクト: hobu/usgs-lidar
    def add_stac(self, tile):

        if not tile.poly:
            return None

        item = pystac.Item(
            tile.name, mapping(tile.poly), list(tile.poly.bounds),
            datetime.datetime.now(),
            {'description': 'A USGS Lidar pointcloud in Entwine/EPT format'})

        #item.ext.enable(pystac.Extensions.POINTCLOUD)

        # icky
        s = tile.ept['schema']
        p = []
        for d in s:
            p.append(Schema(d))

        PointcloudExtension.add_to(item)
        PointcloudExtension.ext(item).apply(
            tile.num_points,
            PhenomenologyType.LIDAR,
            "ept",
            p,
        )

        ProjectionExtension.add_to(item)
        ProjectionExtension.ext(item).apply(3857, projjson=PROJJSON)

        #        item.ext.pointcloud.apply(tile.num_points, 'lidar', 'ept', p, epsg='EPSG:3857')

        asset = pystac.Asset(tile.url, 'entwine',
                             'The ept.json for accessing data')
        item.add_asset('ept.json', asset)

        item_link = pystac.Link('self',
                                f'{self.args.stac_base_url}{tile.name}.json')
        item_parent = pystac.Link('parent',
                                  f'{self.args.stac_base_url}catalog.json')
        item.add_links([item_link, item_parent])
        return item
コード例 #17
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_crs_string(self) -> None:
        item = pystac.Item.from_file(self.example_uri)
        ProjectionExtension.remove_from(item)
        for key in list(item.properties.keys()):
            if key.startswith("proj:"):
                item.properties.pop(key)
        self.assertIsNone(item.properties.get("proj:epsg"))
        self.assertIsNone(item.properties.get("proj:wkt2"))
        self.assertIsNone(item.properties.get("proj:projjson"))

        projection = ProjectionExtension.ext(item, add_if_missing=True)
        self.assertIsNone(projection.crs_string)

        projection.projjson = PROJJSON
        self.assertEqual(projection.crs_string, json.dumps(PROJJSON))

        projection.wkt2 = WKT2
        self.assertEqual(projection.crs_string, WKT2)

        projection.epsg = 4326
        self.assertEqual(projection.crs_string, "EPSG:4326")
コード例 #18
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_centroid(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:centroid", proj_item.properties)
        proj_centroid = ProjectionExtension.ext(proj_item).centroid
        self.assertEqual(proj_centroid, proj_item.properties["proj:centroid"])

        # Set
        new_val = {"lat": 2.0, "lon": 3.0}
        ProjectionExtension.ext(proj_item).centroid = new_val
        self.assertEqual(proj_item.properties["proj:centroid"], new_val)

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).centroid,
            ProjectionExtension.ext(proj_item).centroid,
        )
        self.assertEqual(
            ProjectionExtension.ext(asset_prop).centroid, {"lat": 0.5, "lon": 0.3}
        )

        # Set to Asset
        asset_value = {"lat": 1.5, "lon": 1.3}
        ProjectionExtension.ext(asset_no_prop).centroid = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).centroid,
            ProjectionExtension.ext(proj_item).centroid,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).centroid, asset_value)

        # Validate
        proj_item.validate()

        # Ensure setting bad centroid fails validation
        with self.assertRaises(pystac.STACValidationError):
            ProjectionExtension.ext(proj_item).centroid = {"lat": 2.0, "lng": 3.0}
            proj_item.validate()
コード例 #19
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_geometry(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:geometry", proj_item.properties)
        proj_geometry = ProjectionExtension.ext(proj_item).geometry
        self.assertEqual(proj_geometry, proj_item.properties["proj:geometry"])

        # Set
        ProjectionExtension.ext(proj_item).geometry = proj_item.geometry
        self.assertEqual(proj_item.geometry, proj_item.properties["proj:geometry"])

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).geometry,
            ProjectionExtension.ext(proj_item).geometry,
        )
        asset_prop_geometry = ProjectionExtension.ext(asset_prop).geometry
        assert asset_prop_geometry is not None
        self.assertEqual(asset_prop_geometry["coordinates"][0][0], [0.0, 0.0])

        # Set to Asset
        asset_value: Dict[str, Any] = {"type": "Point", "coordinates": [1.0, 2.0]}
        ProjectionExtension.ext(asset_no_prop).geometry = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).geometry,
            ProjectionExtension.ext(proj_item).geometry,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).geometry, asset_value)

        # Validate
        proj_item.validate()

        # Ensure setting bad geometry fails validation
        with self.assertRaises(pystac.STACValidationError):
            ProjectionExtension.ext(proj_item).geometry = {"bad": "data"}
            proj_item.validate()
コード例 #20
0
def process_projection(item, granule, band1_file):
    proj_ext = ProjectionExtension.ext(item, add_if_missing=True)
    with rasterio.open(band1_file) as band1_dataset:
        proj_ext.transform = band1_dataset.transform
        height, width = band1_dataset.shape
        proj_ext.shape = [height, width]
    for attribute in granule.AdditionalAttributes.AdditionalAttribute:
        if attribute.Name == "MGRS_TILE_ID":
            value = attribute.Values.Value.cdata
            lat_band = value[3]
            # Case is important for ordinal comparison
            if lat_band.casefold() > "m":
                hemi = "326"
            else:
                hemi = "327"
            epsg = int(hemi + value[0:2])
            proj_ext.epsg = epsg
コード例 #21
0
ファイル: test_projection.py プロジェクト: stac-utils/pystac
    def test_transform(self) -> None:
        proj_item = pystac.Item.from_file(self.example_uri)

        # Get
        self.assertIn("proj:transform", proj_item.properties)
        proj_transform = ProjectionExtension.ext(proj_item).transform
        self.assertEqual(proj_transform, proj_item.properties["proj:transform"])

        # Set
        new_val = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
        ProjectionExtension.ext(proj_item).transform = new_val
        self.assertEqual(proj_item.properties["proj:transform"], new_val)

        # Get from Asset
        asset_no_prop = proj_item.assets["B1"]
        asset_prop = proj_item.assets["B8"]
        self.assertEqual(
            ProjectionExtension.ext(asset_no_prop).transform,
            ProjectionExtension.ext(proj_item).transform,
        )
        self.assertEqual(
            ProjectionExtension.ext(asset_prop).transform,
            [15.0, 0.0, 224992.5, 0.0, -15.0, 6790207.5, 0.0, 0.0, 1.0],
        )

        # Set to Asset
        asset_value = [2.0, 4.0, 6.0, 8.0, 10.0, 12.0]
        ProjectionExtension.ext(asset_no_prop).transform = asset_value
        self.assertNotEqual(
            ProjectionExtension.ext(asset_no_prop).transform,
            ProjectionExtension.ext(proj_item).transform,
        )
        self.assertEqual(ProjectionExtension.ext(asset_no_prop).transform, asset_value)

        # Validate
        proj_item.validate()
コード例 #22
0
ファイル: stac.py プロジェクト: jeremyh/eo-datasets
def to_pystac_item(
    dataset: DatasetDoc,
    stac_item_destination_url: str,
    dataset_location: Optional[str] = None,
    odc_dataset_metadata_url: Optional[str] = None,
    explorer_base_url: Optional[str] = None,
    collection_url: Optional[str] = None,
) -> pystac.Item:
    """
    Convert the given ODC Dataset into a Stac Item document.

    Note: You may want to call `validate_item(doc)` on the outputs to find any
    incomplete properties.

    :param collection_url: URL to the Stac Collection. Either this or an explorer_base_url
                           should be specified for Stac compliance.
    :param stac_item_destination_url: Public 'self' URL where the stac document will be findable.
    :param dataset_location: Use this location instead of picking from dataset.locations
                             (for calculating relative band paths)
    :param odc_dataset_metadata_url: Public URL for the original ODC dataset yaml document
    :param explorer_base_url: An Explorer instance that contains this dataset.
                              Will allow links to things such as the product definition.
    """

    if dataset.geometry is not None:
        geom = Geometry(dataset.geometry, CRS(dataset.crs))
        wgs84_geometry = geom.to_crs(CRS("epsg:4326"), math.inf)

        geometry = wgs84_geometry.json
        bbox = wgs84_geometry.boundingbox
    else:
        geometry = None
        bbox = None

    properties = eo3_to_stac_properties(dataset, title=dataset.label)
    properties.update(_lineage_fields(dataset.lineage))

    dt = properties["datetime"]
    del properties["datetime"]

    # TODO: choose remote if there's multiple locations?
    # Without a dataset location, all paths will be relative.
    dataset_location = dataset_location or (dataset.locations[0]
                                            if dataset.locations else None)

    item = Item(
        id=str(dataset.id),
        datetime=dt,
        properties=properties,
        geometry=geometry,
        bbox=bbox,
        collection=dataset.product.name,
    )

    # Add links
    if stac_item_destination_url:
        item.links.append(
            Link(
                rel="self",
                media_type=MediaType.JSON,
                target=stac_item_destination_url,
            ))
    if odc_dataset_metadata_url:
        item.links.append(
            Link(
                title="ODC Dataset YAML",
                rel="odc_yaml",
                media_type="text/yaml",
                target=odc_dataset_metadata_url,
            ))

    for link in _odc_links(explorer_base_url, dataset, collection_url):
        item.links.append(link)

    EOExtension.ext(item, add_if_missing=True)

    if dataset.geometry:
        proj = ProjectionExtension.ext(item, add_if_missing=True)
        epsg, wkt = _get_projection(dataset)
        if epsg is not None:
            proj.apply(epsg=epsg, **_proj_fields(dataset.grids))
        elif wkt is not None:
            proj.apply(wkt2=wkt, **_proj_fields(dataset.grids))
        else:
            raise STACError(
                "Projection extension requires either epsg or wkt for crs.")

    # To pass validation, only add 'view' extension when we're using it somewhere.
    if any(k.startswith("view:") for k in properties.keys()):
        ViewExtension.ext(item, add_if_missing=True)

    # Add assets that are data
    for name, measurement in dataset.measurements.items():
        if not dataset_location and not measurement.path:
            # No URL to link to. URL is mandatory for Stac validation.
            continue

        asset = Asset(
            href=_uri_resolve(dataset_location, measurement.path),
            media_type=_media_type(Path(measurement.path)),
            title=name,
            roles=["data"],
        )
        eo = EOExtension.ext(asset)

        # TODO: pull out more information about the band
        band = Band.create(name)
        eo.apply(bands=[band])

        if dataset.grids:
            proj_fields = _proj_fields(dataset.grids, measurement.grid)
            if proj_fields is not None:
                proj = ProjectionExtension.ext(asset)
                # Not sure how this handles None for an EPSG code
                proj.apply(
                    shape=proj_fields["shape"],
                    transform=proj_fields["transform"],
                    epsg=epsg,
                )

        item.add_asset(name, asset=asset)

    # Add assets that are accessories
    for name, measurement in dataset.accessories.items():
        if not dataset_location and not measurement.path:
            # No URL to link to. URL is mandatory for Stac validation.
            continue

        asset = Asset(
            href=_uri_resolve(dataset_location, measurement.path),
            media_type=_media_type(Path(measurement.path)),
            title=_asset_title_fields(name),
            roles=_asset_roles_fields(name),
        )

        item.add_asset(name, asset=asset)

    return item