Skip to content

Latest commit

 

History

History
384 lines (294 loc) · 12.8 KB

GETTING_STARTED.md

File metadata and controls

384 lines (294 loc) · 12.8 KB

Getting Started

Reading STAC Objects

You can read any STAC objects from URL by STAC.from_url:

catalog = STAC.from_url('https://raw.githubusercontent.com/radiantearth/stac-spec/master/examples/catalog.json')
catalog.class #=> STAC::Catalog
catalog.type #=> "Catalog"

collection = STAC.from_url('https://raw.githubusercontent.com/radiantearth/stac-spec/master/examples/collection.json')
collection.class #=> STAC::Collection
collection.type #=> "Collection"

item = STAC.from_item('https://raw.githubusercontent.com/radiantearth/stac-spec/master/examples/core-item.json')
item.class #=> STAC::Item
item.type #=> "Feature"

Also, from a local file by STAC.from_file:

catalog = STAC.from_file('/path/to/local/catalog.json')
collection = STAC.from_file('/path/to/local/collection.json')
item = STAC.from_file('/path/to/local/item.json')

STAC Object Classes

There are 3 core STAC object classes: STAC::Catalog, STAC::Collection, and STAC::Item.

They have a class method from_hash(hash), which returns its instance converted from the given Hash.

catalog = STAC::Catalog.from_hash(
  {
    'stac_version' => '1.0.0',
    'type' => 'Catalog',
    'id' => '20201211_223832_CS2',
    'description' => 'A simple catalog example',
    'links' => [],
  }
)

And they have the following instance methods in common:

  • extra: -> Hash[String, untyped] returns extra fields that do not belong to the core specification like STAC extensions fields
  • to_h: -> Hash[String, untyped]
  • to_json: -> String
  • deep_dup: -> self returns a deep copy of self.
  • stac_extensions: -> Array[String]
  • add_extension: (String extension_id) -> self | (Module extension) -> self (see Extensions)
  • links: -> Array[STAC::Link]
  • find_link: (rel: String, ?type: String?) -> STAC::Link?
  • add_link: (rel: String, href: String, ?type: String?, ?title: String?) -> self

Followings are shorthand methods for core links:

  • self_href: -> String? returns rel="self" link's href value
  • self_href=: (String absolute_href) -> void adds rel="self" link with the given href
  • root: -> Catalog? returns rel="root" link as a catalog object
  • root=: (Catalog? catalog) -> void overwrites rel="root" link
  • parent: -> Catalog? returns rel="parent" link as a catalog object
  • parent=: (Catalog? catalog) -> void overwrites rel="parent" link

STAC::Catalog

STAC::Catalog has the following attributes:

  • attr_accessor id: String
  • attr_accessor description: String
  • attr_accessor title: String?
catalog.id = 'awesome_catalog'
catalog.id #=> "awesome_catalog"

See STAC Catalog Specification for the details.

Crawling Catalog Links

STAC::Catalog also has methods to crawl its links:

  • children: -> Enumerator::Lazy[STAC::Catalog, void] returns catalog/collection objects from rel="child" links
  • collections: -> Enumerator::Lazy[STAC::Collection, void] filters only collections from #children
  • all_collections: -> Enumerator::Lazy[STAC::Collection, void] returns all collections from the catalog and its children recursively
  • items: -> Enumerator::Lazy[STAC::Item, void] returns item objects from rel="items" links
  • all_items: -> Enumerator::Lazy[STAC::Item, void] returns all items from the catalog and its children recursively
  • find_child: (String id, ?recursive: bool) -> STAC::Catalog?
  • find_item: (String id, ?recursive: bool) -> STAC::Item?

Note that the first 5 methods return Enumerator::Lazy. This is to prevent making a large number of HTTP requests when calling the methods.

catalog.collections.each do |collection|
  puts collection.id
end
collection = catalog.find_child('awesome_collection')

catalog.all_items.first(100).each do |item|
  puts item.id
end
item = catalog.find_item('awesome_item', recursive: true)

Exporting Catalog

Furthermore, STAC::Catalog has methods to create a new static STAC catalog:

  • add_child: (Catalog catalog, ?href: String, ?title: String?) -> self
  • add_item: (Item item, ?href: String, ?title: String?) -> self
  • export: (?String? dest_dir) -> void
catalog = STAC::Catalog.new(id: 'root', description: 'The root catalog')
catalog.self_href = 'https://example.com/catalog.json'
catalog.root = catalog

item = STAC::Item.from_hash(
  {
    'stac_version' => '1.0.0',
    'type' => 'Feature',
    'id' => 'item',
    'geometry' => nil,
    'properties' => {
      'datetime' => Time.now.iso8601
    },
    'links' => [],
    'assets' => {}
  }
)

# Add rel="item" link to catalog with href="#{item.id}.json" (based on best practice).
# And add rel="self", rel="root", and rel="parent" links to item.
catalog.add_item(item)

item.links.each { |l| puts "#{l.rel}: #{l.href}" }
# self: https://example.com/item.json
# root: https://example.com/catalog.json
# parent: https://example.com/catalog.json

sub_catalog = STAC::Catalog.new(id: 'sub-catalog', description: 'The sub catalog')

# Add rel="child" link to catalog with href="#{catalog.id}/catalog.json".
# Also add rel="self", rel="root", and rel="parent" links to sub-catalog.
catalog.add_child(sub_catalog)

sub_catalog.add_item(item.deep_dup.update(id: 'sub-item'))
sub_catalog.links.each { |l| puts "#{l.rel}: #{l.href}" }
# self: https://example.com/sub-catalog/catalog.json
# root: https://example.com/catalog.json
# parent: https://example.com/catalog.json
# item: https://example.com/sub-catalog/sub-item.json

catalog.links.each { |l| puts "#{l.rel}: #{l.href}" }
# self: https://example.com/catalog.json
# root: https://example.com/catalog.json
# item: https://example.com/item.json
# child: https://example.com/sub-catalog/catalog.json

# Exports the catalog and its children/item recursively to the given directory.
catalog.export('path/to/dest')

# $ tree path/to/dest
# path/to/dest
# ├── catalog.json
# ├── item.json
# └── sub-catalog
#     ├── catalog.json
#     └── sub-item.json

STAC::Collection

STAC::Collection inherits STAC::Catalog.

The followings are STAC::Collection specific attributes:

  • attr_accessor license: String
  • attr_accessor extent: STAC::Extent
  • attr_accessor keywords: Array[String]?
  • attr_accessor providers: Array[STAC::Provider]?
  • attr_accessor summaries: Hash[String, untyped]?
  • attr_reader assets: Hash[String, STAC::Asset]?

To add an asset, use:

  • add_asset: (key: String, href: String, ?title: String?, ?description: String?, ?type: String?, ?roles: Array[String]?) -> self

STAC::Extent, STAC::Provider, and STAC::Asset provide accessors for the corresponding object's fields.

collection.extent.spatial.bbox = [-180, -90, 180, 90]
collection.extent.spatial.bbox #=> [-180, -90, 180, 90]
collection.extent.temporal.interval = ['2022-12-01T00:00:00Z', null]
collection.extent.temporal.interval #=> ["2022-12-01T00:00:00Z", null]

collection.providers << STAC::Provider.new(name: 'sankichi92')
collection.providers.last.name #=> "sankichi92"

See STAC Collection Specification for the details.

STAC::Item

STAC::Item has the following attributes:

  • attr_accessor id: String
  • attr_reader properties: STAC::Properties
  • attr_reader assets: Hash[String, STAC::Asset]
  • attr_accessor geometry: Hash[String, untyped]?
  • attr_accessor bbox: Array[Numeric]?
  • attr_accessor collection_id: String?

And has the following methods:

  • add_asset: (key: String, href: String, ?title: String?, ?description: String?, ?type: String?, ?roles: Array[String]?) -> self
  • collection: -> Collection? returns a rel="collection" link as a collection object
  • collection=: (Collection collection) -> void overwrites rel="collection" link and collection_id attribute

See STAC Item Specification for the details.

Common Metadata

STAC::Properties and STAC::Asset includes STAC::CommonMetadata, which provides read/write methods for STAC Common Metadata fields:

  • title: -> String?
  • title=: (String) -> void
  • description: -> String?
  • description=: (String) -> void
  • created: -> Time?
  • created=: (Time | String) -> void
  • updated: -> Time?
  • updated=: (Time | String) -> void
  • start_datetime: -> Time?
  • start_datetime=: (Time | String) -> void
  • end_datetime: -> Time?
  • end_datetime=: (Time | String) -> void
  • datetime_range: -> Range[Time]? returns a range from #start_datetime to #end_datetime
  • datetime_range=: (Range[Time]) -> void sets #start_datetime and #end_datetime by the given range
  • license: -> String?
  • license=: (String) -> void
  • providers: -> Array[Provider]
  • providers=: (Array[Provider | Hash[String, untyped]]) -> void
  • platform: -> String?
  • platform=: (String) -> void
  • instruments: -> Array[String]?
  • instruments=: (Array[String]) -> void
  • constellation: -> String?
  • constellation=: (String) -> void
  • mission: -> String?
  • mission=: (String) -> void
  • gsd: -> Numeric
  • gsd=: (Numeric) -> void

These methods are shorthand accessors of #extra hash:

item.properties.extra #=> {}
item.properties.title = 'Awesome Item' # same as `item.properties.extra['title'] = 'Awesome Item'`
item.properties.title #=> "Awesome Item"
item.properties.extra #=> {"title"=>"Awesome Item"}

Extensions

When an extension is added to a STAC object, methods corresponding to the extension will be added dynamically by Object#extend.

These methods are also shorthand accessors of extra hash same as STAC::CommonMetadata.

item.stac_extensions #=> []
item.properties.extra #=> {}
item.properties.eo_cloud_cover #=> raises NoMethodError

item.add_extension('https://stac-extensions.github.io/eo/v1.0.0/schema.json')
# same as `item.add_extension(STAC::Extensions::ElectroOptical)`

item.stac_extensions #=> ["https://stac-extensions.github.io/eo/v1.0.0/schema.json"]
item.properties.eo_cloud_cover = 1.2
item.properties.eo_cloud_cover #=> 1.2
item.properties.extra #=> {"eo:cloud_cover"=>1.2}

# item.properties extends STAC::Extensions::ElectroOptical::Properties
item.properties.is_a?(STAC::Extensions::ElectroOptical::Properties) #=> true

Currently, only 4 stable extensions have been implemented:

puts STAC::STACObject.extendables
# STAC::Extensions::ElectroOptical
# STAC::Extensions::Projection
# STAC::Extensions::ScientificCitation
# STAC::Extensions::ViewGeometry

Adding Extensions

You can add custom extensions.

Extension modules must extend STAC::Extension and set identifier and scope. And you must register it by STAC::STACObject.add_extendable(extension_module).

module MyExtension
  extend STAC::Extension

  identifier 'https://sankichi92.github.io/my_extension/v1.0.0/schema.json'
  scope STAC::Item, STAC::Collection
end

STAC::STACObject.add_extendable(MyExtension)

Then you can add methods to STAC::Properties, STAC::Asset, and STAC::Collection by defining modules with corresponding names under the extension namespace:

module MyExtension
  module Properties
    def my_field
      extra['my:field']
    end

    def my_field(val)
      extra['my:field'] = val
    end
  end

  module Asset
    include Properties
  end
end

See lib/stac/extensions for examples.

Using Custom HTTP Client

Custom HTTP client class must implement get: (URI | String url) -> Hash[String, untyped], which returns response JSON as a Hash:

class MyHTTPClient
  def get(url)
    # ...
  end
end

You can pass the custom HTTP client to STAC.from_url for a specific STAC object instance:

catalog = STAC.from_url('https://example.com/catalog.json', http_client: MyHTTPClient.new)

Or you can set it as the global default by STAC.default_http_client=:

STAC.default_http_client = MyHTTPClient.new

If you want to only add custom HTTP headers, you can use STAC::SimpleHTTPClient:

http_client = STAC::SimpleHTTPClient.new({ 'X-Foo' => 'Bar' })
catalog = STAC.from_url('https://example.com/catalog.json', http_client:)
STAC.default_http_client = http_client