Skip to content

Commit b405ba2

Browse files
committed
Update File Info Extension to v2.0.0
1 parent a9e884d commit b405ba2

File tree

2 files changed

+119
-126
lines changed

2 files changed

+119
-126
lines changed

pystac/extensions/file.py

Lines changed: 118 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,91 @@
33
https://github.com/stac-extensions/file
44
"""
55

6-
import enum
6+
from enum import Enum
77
from typing import Any, Dict, Generic, List, Optional, Set, TypeVar, cast
88

99
import pystac
10-
from pystac.extensions.base import (
11-
ExtensionManagementMixin,
12-
PropertiesExtension,
13-
SummariesExtension,
14-
)
10+
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
1511
from pystac.extensions.hooks import ExtensionHooks
1612
from pystac.serialization.identify import (
1713
OldExtensionShortIDs,
1814
STACJSONDescription,
1915
STACVersionID,
2016
)
21-
from pystac.utils import map_opt
17+
from pystac.utils import get_required
2218

2319
T = TypeVar("T", pystac.Item, pystac.Asset)
2420

25-
SCHEMA_URI = "https://stac-extensions.github.io/file/v1.0.0/schema.json"
21+
SCHEMA_URI = "https://stac-extensions.github.io/file/v2.0.0/schema.json"
2622

2723
PREFIX = "file:"
28-
DATA_TYPE_PROP = PREFIX + "data_type"
29-
SIZE_PROP = PREFIX + "size"
30-
NODATA_PROP = PREFIX + "nodata"
24+
BYTE_ORDER_PROP = PREFIX + "byte_order"
3125
CHECKSUM_PROP = PREFIX + "checksum"
26+
HEADER_SIZE_PROP = PREFIX + "header_size"
27+
SIZE_PROP = PREFIX + "size"
28+
VALUES_PROP = PREFIX + "values"
29+
3230

31+
class ByteOrder(str, Enum):
32+
"""List of allows values for the ``"file:byte_order"`` field defined by the
33+
:stac-ext:`File Info Extension <file>`."""
3334

34-
class FileDataType(str, enum.Enum):
3535
def __str__(self) -> str:
3636
return str(self.value)
3737

38-
INT8 = "int8"
39-
INT16 = "int16"
40-
INT32 = "int32"
41-
INT64 = "int64"
42-
UINT8 = "uint8"
43-
UINT16 = "uint16"
44-
UINT32 = "uint32"
45-
UINT64 = "uint64"
46-
FLOAT16 = "float16"
47-
FLOAT32 = "float32"
48-
FLOAT64 = "float64"
49-
CINT16 = "cint16"
50-
CINT32 = "cint32"
51-
CFLOAT32 = "cfloat32"
52-
CFLOAT64 = "cfloat64"
53-
OTHER = "other"
38+
LITTLE_ENDIAN = "little-endian"
39+
BIG_ENDIAN = "big-endian"
40+
41+
42+
class MappingObject:
43+
"""Represents a value map used by assets that are used as classification layers, and
44+
give details about the values in the asset and their meanings."""
45+
46+
properties: Dict[str, Any]
47+
48+
def __init__(self, properties: Dict[str, Any]) -> None:
49+
self.properties = properties
50+
51+
def apply(self, values: List[Any], summary: str) -> None:
52+
"""Sets the properties for this :class:`~MappingObject` instance.
53+
54+
Args:
55+
values : The value(s) in the file. At least one array element is required.
56+
summary : A short description of the value(s).
57+
"""
58+
self.values = values
59+
self.summary = summary
60+
61+
@classmethod
62+
def create(cls, values: List[Any], summary: str) -> "MappingObject":
63+
"""Creates a new :class:`~MapptingObject` instance.
64+
65+
Args:
66+
values : The value(s) in the file. At least one array element is required.
67+
summary : A short description of the value(s).
68+
"""
69+
m = cls({})
70+
m.apply(values=values, summary=summary)
71+
return m
72+
73+
@property
74+
def values(self) -> List[Any]:
75+
"""Gets or sets the list of value(s) in the file. At least one array element is
76+
required."""
77+
return get_required(self.properties["values"], self, "values")
78+
79+
@values.setter
80+
def values(self, v: List[Any]) -> None:
81+
self.properties["values"] = v
82+
83+
@property
84+
def summary(self) -> str:
85+
"""Gets or sets the short description of the value(s)."""
86+
return get_required(self.properties["summary"], self, "summary")
87+
88+
@summary.setter
89+
def summary(self, v: str) -> None:
90+
self.properties["summary"] = v
5491

5592

5693
class FileExtension(
@@ -73,62 +110,81 @@ class FileExtension(
73110

74111
def apply(
75112
self,
76-
data_type: Optional[FileDataType] = None,
77-
size: Optional[int] = None,
78-
nodata: Optional[List[Any]] = None,
113+
byte_order: Optional[ByteOrder] = None,
79114
checksum: Optional[str] = None,
115+
header_size: Optional[int] = None,
116+
size: Optional[int] = None,
117+
values: Optional[List[MappingObject]] = None,
80118
) -> None:
81119
"""Applies file extension properties to the extended Item.
82120
83121
Args:
84-
data_type : The data type of the file.
85-
size : size of the file in bytes.
86-
nodata : Value(s) for no-data.
87-
checksum : Multihash for the corresponding file,
122+
byte_order : Optional byte order of integer values in the file. One of
123+
``"big-endian"`` or ``"little-endian"``.
124+
checksum : Optional multihash for the corresponding file,
88125
encoded as hexadecimal (base 16) string with lowercase letters.
126+
header_size : Optional header size of the file, in bytes.
127+
size : Optional size of the file, in bytes.
128+
values : Optional list of :class:`~MappingObject` instances that lists the
129+
values that are in the file and describe their meaning. See the
130+
:stac-ext:`Mapping Object <file#mapping-object>` docs for an example.
131+
If given, at least one array element is required.
89132
"""
90-
self.data_type = data_type
91-
self.size = size
92-
self.nodata = nodata
133+
self.byte_order = byte_order
93134
self.checksum = checksum
135+
self.header_size = header_size
136+
self.size = size
137+
self.values = values
138+
139+
@property
140+
def byte_order(self) -> Optional[ByteOrder]:
141+
"""Gets or sets the byte order of integer values in the file. One of big-endian
142+
or little-endian."""
143+
return self._get_property(BYTE_ORDER_PROP, ByteOrder)
144+
145+
@byte_order.setter
146+
def byte_order(self, v: Optional[ByteOrder]) -> None:
147+
self._set_property(BYTE_ORDER_PROP, v)
94148

95149
@property
96-
def data_type(self) -> Optional[FileDataType]:
97-
"""Get or sets the data_type of the file."""
98-
return map_opt(
99-
lambda s: FileDataType(s), self._get_property(DATA_TYPE_PROP, str)
100-
)
150+
def checksum(self) -> Optional[str]:
151+
"""Get or sets the multihash for the corresponding file, encoded as hexadecimal
152+
(base 16) string with lowercase letters."""
153+
return self._get_property(CHECKSUM_PROP, str)
154+
155+
@checksum.setter
156+
def checksum(self, v: Optional[str]) -> None:
157+
self._set_property(CHECKSUM_PROP, v)
158+
159+
@property
160+
def header_size(self) -> Optional[int]:
161+
"""Get or sets the header size of the file, in bytes."""
162+
return self._get_property(HEADER_SIZE_PROP, int)
101163

102-
@data_type.setter
103-
def data_type(self, v: Optional[FileDataType]) -> None:
104-
self._set_property(DATA_TYPE_PROP, str(v))
164+
@header_size.setter
165+
def header_size(self, v: Optional[int]) -> None:
166+
self._set_property(HEADER_SIZE_PROP, v)
105167

106168
@property
107169
def size(self) -> Optional[int]:
108-
"""Get or sets the size in bytes of the file."""
170+
"""Get or sets the size of the file, in bytes."""
109171
return self._get_property(SIZE_PROP, int)
110172

111173
@size.setter
112174
def size(self, v: Optional[int]) -> None:
113175
self._set_property(SIZE_PROP, v)
114176

115177
@property
116-
def nodata(self) -> Optional[List[Any]]:
117-
"""Get or sets the no data values."""
118-
return self._get_property(NODATA_PROP, List[Any])
178+
def values(self) -> Optional[List[MappingObject]]:
179+
"""Get or sets the list of :class:`~MappingObject` instances that lists the
180+
values that are in the file and describe their meaning. See the
181+
:stac-ext:`Mapping Object <file#mapping-object>` docs for an example. If given,
182+
at least one array element is required."""
183+
return self._get_property(VALUES_PROP, List[MappingObject])
119184

120-
@nodata.setter
121-
def nodata(self, v: Optional[List[Any]]) -> None:
122-
self._set_property(NODATA_PROP, v)
123-
124-
@property
125-
def checksum(self) -> Optional[str]:
126-
"""Get or sets the checksum"""
127-
return self._get_property(CHECKSUM_PROP, str)
128-
129-
@checksum.setter
130-
def checksum(self, v: Optional[str]) -> None:
131-
self._set_property(CHECKSUM_PROP, v)
185+
@values.setter
186+
def values(self, v: Optional[List[MappingObject]]) -> None:
187+
self._set_property(VALUES_PROP, v)
132188

133189
@classmethod
134190
def get_schema_uri(cls) -> str:
@@ -155,10 +211,6 @@ def ext(obj: T) -> "FileExtension[T]":
155211
f"File extension does not apply to type {type(obj)}"
156212
)
157213

158-
@staticmethod
159-
def summaries(obj: pystac.Collection) -> "SummariesFileExtension":
160-
return SummariesFileExtension(obj)
161-
162214

163215
class ItemFileExtension(FileExtension[pystac.Item]):
164216
"""A concrete implementation of :class:`FileExtension` on an :class:`~pystac.Item`
@@ -196,39 +248,6 @@ def __repr__(self) -> str:
196248
return "<AssetFileExtension Asset href={}>".format(self.asset_href)
197249

198250

199-
class SummariesFileExtension(SummariesExtension):
200-
@property
201-
def data_type(self) -> Optional[List[FileDataType]]:
202-
"""Get or sets the summary of data_type values for this Collection."""
203-
204-
return map_opt(
205-
lambda x: [FileDataType(t) for t in x],
206-
self.summaries.get_list(DATA_TYPE_PROP),
207-
)
208-
209-
@data_type.setter
210-
def data_type(self, v: Optional[List[FileDataType]]) -> None:
211-
self._set_summary(DATA_TYPE_PROP, map_opt(lambda x: [str(t) for t in x], v))
212-
213-
@property
214-
def size(self) -> Optional[pystac.RangeSummary[int]]:
215-
"""Get or sets the summary of size values for this Collection."""
216-
return self.summaries.get_range(SIZE_PROP)
217-
218-
@size.setter
219-
def size(self, v: Optional[pystac.RangeSummary[int]]) -> None:
220-
self._set_summary(SIZE_PROP, v)
221-
222-
@property
223-
def nodata(self) -> Optional[List[Any]]:
224-
"""Get or sets the summary of nodata values for this Collection."""
225-
return self.summaries.get_list(NODATA_PROP)
226-
227-
@nodata.setter
228-
def nodata(self, v: Optional[List[Any]]) -> None:
229-
self._set_summary(NODATA_PROP, v)
230-
231-
232251
class FileExtensionHooks(ExtensionHooks):
233252
schema_uri: str = SCHEMA_URI
234253
prev_extension_ids: Set[str] = set(["file"])

tests/extensions/test_file.py

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pystac
55
from tests.utils import TestCases, assert_to_from_dict
6-
from pystac.extensions.file import FileExtension, FileDataType
6+
from pystac.extensions.file import FileExtension
77

88

99
class FileTest(unittest.TestCase):
@@ -50,32 +50,6 @@ def test_asset_checksum(self) -> None:
5050
self.assertEqual(new_checksum, FileExtension.ext(asset).checksum)
5151
item.validate()
5252

53-
def test_asset_data_type(self) -> None:
54-
item = pystac.Item.from_file(self.FILE_EXAMPLE_URI)
55-
asset = item.assets["thumbnail"]
56-
57-
# Get
58-
self.assertEqual(FileDataType.UINT8, FileExtension.ext(asset).data_type)
59-
60-
# Set
61-
new_data_type = FileDataType.UINT16
62-
FileExtension.ext(asset).data_type = new_data_type
63-
self.assertEqual(new_data_type, FileExtension.ext(asset).data_type)
64-
item.validate()
65-
66-
def test_asset_nodata(self) -> None:
67-
item = pystac.Item.from_file(self.FILE_EXAMPLE_URI)
68-
asset = item.assets["thumbnail"]
69-
70-
# Get
71-
self.assertEqual([], FileExtension.ext(asset).nodata)
72-
73-
# Set
74-
new_nodata = [-1]
75-
FileExtension.ext(asset).nodata = new_nodata
76-
self.assertEqual(new_nodata, FileExtension.ext(asset).nodata)
77-
item.validate()
78-
7953
def test_migrates_old_checksum(self) -> None:
8054
example_path = TestCases.get_path(
8155
"data-files/examples/1.0.0-beta.2/"

0 commit comments

Comments
 (0)