Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codespellignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
filetest
4 changes: 3 additions & 1 deletion pystac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class STACError(Exception):
import pystac.extensions.timestamps
import pystac.extensions.version
import pystac.extensions.view
import pystac.extensions.file

STAC_EXTENSIONS = extensions.base.RegisteredSTACExtensions([
extensions.eo.EO_EXTENSION_DEFINITION, extensions.label.LABEL_EXTENSION_DEFINITION,
Expand All @@ -49,7 +50,8 @@ class STACError(Exception):
extensions.sat.SAT_EXTENSION_DEFINITION, extensions.scientific.SCIENTIFIC_EXTENSION_DEFINITION,
extensions.single_file_stac.SFS_EXTENSION_DEFINITION,
extensions.timestamps.TIMESTAMPS_EXTENSION_DEFINITION,
extensions.version.VERSION_EXTENSION_DEFINITION, extensions.view.VIEW_EXTENSION_DEFINITION
extensions.version.VERSION_EXTENSION_DEFINITION, extensions.view.VIEW_EXTENSION_DEFINITION,
extensions.file.FILE_EXTENSION_DEFINITION
])


Expand Down
1 change: 1 addition & 0 deletions pystac/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ def __str__(self):
TIMESTAMPS = 'timestamps'
VERSION = 'version'
VIEW = 'view'
FILE = 'file'
227 changes: 227 additions & 0 deletions pystac/extensions/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import enum

from pystac import Extensions
from pystac.item import Item
from pystac.extensions.base import (ItemExtension, ExtensionDefinition, ExtendedObject)


class FileDataType(enum.Enum):
INT8 = "int8"
INT16 = "int16"
INT32 = "int32"
INT64 = "int64"
UINT8 = "uint8"
UINT16 = "uint16"
UINT32 = "uint32"
UINT64 = "uint64"
FLOAT16 = "float16"
FLOAT32 = "float32"
FLOAT64 = "float64"
CINT16 = "cint16"
CINT32 = "cint32"
CFLOAT32 = "cfloat32"
CFLOAT64 = "cfloat64"
OTHER = "other"


class FileItemExt(ItemExtension):
"""FileItemExt is the extension of the Item in the file extension which
adds file related details such as checksum, data type and size for assets.

Args:
item (Item): The item to be extended.

Attributes:
item (Item): The Item that is being extended.

Note:
Using FileItemExt to directly wrap an item will add the 'file' extension ID to
the item's stac_extensions.
"""
def __init__(self, item):
if item.stac_extensions is None:
item.stac_extensions = [Extensions.FILE]
elif Extensions.FILE not in item.stac_extensions:
item.stac_extensions.append(Extensions.FILE)

self.item = item

def apply(self, data_type=None, size=None, nodata=None, checksum=None):
"""Applies file extension properties to the extended Item.

Args:
data_type (FileDataType): The data type of the file.
size (int or None): size of the file in bytes.
nodata (List[Object] or None): Value(s) for no-data.
checksum (str or None): Multihash for the corresponding file,
encoded as hexadecimal (base 16) string with lowercase letters.
"""
self.data_type = data_type
self.size = size
self.nodata = nodata
self.checksum = checksum

def _set_property(self, key, value, asset):
target = self.item.properties if asset is None else asset.properties
if value is None:
target.pop(key, None)
else:
target[key] = value

@property
def data_type(self):
"""Get or sets the data_type of the file.

Returns:
FileDataType
"""
return self.get_data_type()

@data_type.setter
def data_type(self, v):
self.set_data_type(v)

def get_data_type(self, asset=None):
"""Gets an Item or an Asset data_type.

If an Asset is supplied and the data_type property exists on the Asset,
returns the Asset's value. Otherwise returns the Item's value

Returns:
FileDataType
"""
if asset is not None and 'file:data_type' in asset.properties:
data_type = asset.properties.get('file:data_type')
else:
data_type = self.item.properties.get('file:data_type')

if data_type is not None:
return FileDataType(data_type)

def set_data_type(self, data_type, asset=None):
"""Set an Item or an Asset data_type.

If an Asset is supplied, sets the property on the Asset.
Otherwise sets the Item's value.
"""
self._set_property('file:data_type', data_type.value, asset)

@property
def size(self):
"""Get or sets the size in bytes of the file

Returns:
int or None
"""
return self.get_size()

@size.setter
def size(self, v):
self.set_size(v)

def get_size(self, asset=None):
"""Gets an Item or an Asset file size.

If an Asset is supplied and the Item property exists on the Asset,
returns the Asset's value. Otherwise returns the Item's value

Returns:
float
"""
if asset is None or 'file:size' not in asset.properties:
return self.item.properties.get('file:size')
else:
return asset.properties.get('file:size')

def set_size(self, size, asset=None):
"""Set an Item or an Asset size.

If an Asset is supplied, sets the property on the Asset.
Otherwise sets the Item's value.
"""
self._set_property('file:size', size, asset)

@property
def nodata(self):
"""Get or sets the no data values

Returns:
int or None
"""
return self.get_nodata()

@nodata.setter
def nodata(self, v):
self.set_nodata(v)

def get_nodata(self, asset=None):
"""Gets an Item or an Asset nodata values.

If an Asset is supplied and the Item property exists on the Asset,
returns the Asset's value. Otherwise returns the Item's value

Returns:
list[object]
"""
if asset is None or 'file:nodata' not in asset.properties:
return self.item.properties.get('file:nodata')
else:
return asset.properties.get('file:nodata')

def set_nodata(self, nodata, asset=None):
"""Set an Item or an Asset nodata values.

If an Asset is supplied, sets the property on the Asset.
Otherwise sets the Item's value.
"""
self._set_property('file:nodata', nodata, asset)

@property
def checksum(self):
"""Get or sets the checksum

Returns:
str or None
"""
return self.get_checksum()

@checksum.setter
def checksum(self, v):
self.set_checksum(v)

def get_checksum(self, asset=None):
"""Gets an Item or an Asset checksum.

If an Asset is supplied and the Item property exists on the Asset,
returns the Asset's value. Otherwise returns the Item's value

Returns:
list[object]
"""
if asset is None or 'file:checksum' not in asset.properties:
return self.item.properties.get('file:checksum')
else:
return asset.properties.get('file:checksum')

def set_checksum(self, checksum, asset=None):
"""Set an Item or an Asset checksum.

If an Asset is supplied, sets the property on the Asset.
Otherwise sets the Item's value.
"""
self._set_property('file:checksum', checksum, asset)

def __repr__(self):
return '<FileItemExt Item id={}>'.format(self.item.id)

@classmethod
def _object_links(cls):
return []

@classmethod
def from_item(cls, item):
return cls(item)


FILE_EXTENSION_DEFINITION = ExtensionDefinition(Extensions.FILE,
[ExtendedObject(Item, FileItemExt)])
100 changes: 100 additions & 0 deletions tests/data-files/file/file-example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"id": "S1A_EW_GRDM_1SSH_20181103T235855_20181103T235955_024430_02AD5D_5616",
"type": "Feature",
"stac_version": "1.0.0-beta.2",
"stac_extensions": [
"file"
],
"bbox": [
-70.275032,
-64.72924,
-65.087479,
-51.105831
],
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-67.071648,
-64.72924
],
[
-65.087479,
-56.674374
],
[
-68.033211,
-51.105831
],
[
-70.275032,
-59.805672
],
[
-67.071648,
-64.72924
]
]
]
},
"properties": {
"datetime": "2018-11-03T23:58:55Z"
},
"assets": {
"noises": {
"href": "./annotation/calibration/noise-s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml",
"title": "Calibration Schema",
"type": "text/xml",
"file:checksum": "90e40210a30d1711e81a4b11ef67b28744321659"
},
"calibrations": {
"href": "./annotation/calibration/calibration-s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml",
"title": "Noise Schema",
"type": "text/xml",
"file:checksum": "90e402104fc5351af67db0b8f1746efe421a05e4"
},
"products": {
"href": "./annotation/s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml",
"title": "Product Schema",
"type": "text/xml",
"file:checksum": "90e402107a7f2588a85362b9beea2a12d4514d45"
},
"measurement": {
"href": "./measurement/s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.tiff",
"title": "Measurements",
"type": "image/tiff",
"file:byte_order": "little-endian",
"file:data_type": "uint16",
"file:size": 209715200,
"file:header_size": 4096,
"file:checksum": "90e40210163700a8a6501eccd00b6d3b44ddaed0"
},
"thumbnail": {
"href": "./preview/quick-look.png",
"title": "Thumbnail",
"type": "image/png",
"file:byte_order": "big-endian",
"file:data_type": "uint8",
"file:size": 146484,
"file:checksum": "90e40210f52acd32b09769d3b1871b420789456c",
"file:nodata": []
}
},
"links": [
{
"rel": "self",
"href": "https://example.com/collections/sentinel-1/items/S1A_EW_GRDM_1SSH_20181103T235855_20181103T235955_024430_02AD5D_5616"
},
{
"rel": "parent",
"href": "https://example.com/collections/sentinel-1",
"file:checksum": "11146d97123fd2c02dec9a1b6d3b13136dbe600cf966"
},
{
"rel": "root",
"href": "https://example.com/collections",
"file:checksum": "1114fa4b9d69fdddc7c1be7bed9440621400b383b43f"
}
]
}
Loading