-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathstac.py
166 lines (142 loc) · 5.7 KB
/
stac.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import os.path
import re
from typing import Optional
from pystac import (CatalogType, Collection, Extent, Asset, Summaries,
SpatialExtent, TemporalExtent)
from pystac.extensions.projection import ProjectionExtension
from pystac.extensions.item_assets import ItemAssetsExtension
from pystac.extensions.raster import (
DataType,
RasterBand,
RasterExtension,
Sampling,
)
from pystac.media_type import MediaType
import rasterio
from shapely.geometry import mapping, box
from pystac import Item
from stactools.core.io import ReadHrefModifier
from stactools.cop_dem import constants as co
def create_item(href: str,
read_href_modifier: Optional[ReadHrefModifier] = None,
host: Optional[str] = None) -> Item:
"""Creates a STAC Item from a single tile of Copernicus DEM data."""
if read_href_modifier:
modified_href = read_href_modifier(href)
else:
modified_href = href
with rasterio.open(modified_href) as dataset:
if dataset.crs.to_epsg() != co.COP_DEM_EPSG:
raise ValueError(f"Dataset {href} is not EPSG:{co.COP_DEM_EPSG}, "
"which is required for Copernicus DEM data")
bbox = list(dataset.bounds)
geometry = mapping(box(*bbox))
transform = dataset.transform
shape = dataset.shape
item = Item(id=os.path.splitext(os.path.basename(href))[0],
geometry=geometry,
bbox=bbox,
datetime=co.COP_DEM_COLLECTION_START,
properties={},
stac_extensions=[])
p = re.compile(r'Copernicus_DSM_COG_(\d\d)_(.*)_DEM.tif')
m = p.match(os.path.basename(href))
if m:
if m.group(1) == '30':
gsd = 90
elif m.group(1) == '10':
gsd = 30
else:
raise ValueError("unknown resolution {}".format(m.group(1)))
title = m.group(2)
else:
raise ValueError("unable to parse {}".format(href))
item.add_links(co.COP_DEM_LINKS)
item.common_metadata.platform = co.COP_DEM_PLATFORM
item.common_metadata.gsd = gsd
if host and host not in co.COP_DEM_HOST.keys():
raise ValueError(
f"Invalid host: {host}. Must be one of {list(co.COP_DEM_HOST.keys())}"
)
if host and (host_provider := co.COP_DEM_HOST.get(host)):
providers = [*co.COP_DEM_PROVIDERS, host_provider]
else:
providers = co.COP_DEM_PROVIDERS
item.common_metadata.providers = providers
item.common_metadata.license = "proprietary"
data_asset = Asset(
href=href,
title=title,
description=None,
media_type=MediaType.COG,
roles=["data"],
)
data_bands = RasterBand.create(sampling=Sampling.POINT,
data_type=DataType.FLOAT32,
spatial_resolution=gsd,
unit="meter")
RasterExtension.ext(data_asset).bands = [data_bands]
item.add_asset("data", data_asset)
projection = ProjectionExtension.ext(item, add_if_missing=True)
projection.epsg = co.COP_DEM_EPSG
projection.transform = transform[0:6]
projection.shape = shape
return item
def create_collection(product: str, host: Optional[str] = None) -> Collection:
"""Create a STAC Collection
This function includes logic to extract all relevant metadata from
an asset describing the STAC collection and/or metadata coded into an
accompanying constants.py file.
See `Collection<https://pystac.readthedocs.io/en/latest/api.html#collection>`_.
Args:
Product (str): glo-30 or glo-90
Returns:
Item: STAC Item object
Returns:
Collection: STAC Collection object
"""
if product.lower() == "glo-30":
summaries = {
"gsd": [30],
"platform": [co.COP_DEM_PLATFORM],
# "instruments": ,
}
elif product.lower() == "glo-90":
summaries = {
"gsd": [90],
"platform": [co.COP_DEM_PLATFORM],
# "instruments": ,
}
else:
raise ValueError(
f"{product} is not a valid product. Must be one of {co.COP_DEM_PRODUCTS}"
)
# Allow host to be selected by cli option
if host and host not in co.COP_DEM_HOST.keys():
raise ValueError(
f"Invalid host: {host}. Must be one of {list(co.COP_DEM_HOST.keys())}"
)
if host and (host_provider := co.COP_DEM_HOST.get(host)):
providers = [*co.COP_DEM_PROVIDERS, host_provider]
else:
providers = co.COP_DEM_PROVIDERS
collection = Collection(id=f"cop-dem-{product.lower()}",
title=f"Copernicus DEM {product.upper()}",
description=co.COP_DEM_DESCRIPTION,
license="proprietary",
keywords=co.COP_DEM_KEYWORDS,
catalog_type=CatalogType.RELATIVE_PUBLISHED,
summaries=Summaries(summaries),
extent=Extent(
SpatialExtent(co.COP_DEM_SPATIAL_EXTENT),
TemporalExtent([co.COP_DEM_TEMPORAL_EXTENT])),
providers=providers,
stac_extensions=[
ItemAssetsExtension.get_schema_uri(),
ProjectionExtension.get_schema_uri(),
RasterExtension.get_schema_uri(),
])
collection.add_links(co.COP_DEM_LINKS)
assets = ItemAssetsExtension.ext(collection, add_if_missing=True)
assets.item_assets = co.COP_DEM_ASSETS
return collection