stac 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/CHANGELOG.md +18 -0
  4. data/GETTING_STARTED.md +384 -0
  5. data/Gemfile.lock +48 -37
  6. data/README.md +16 -57
  7. data/Rakefile +62 -4
  8. data/Steepfile +2 -0
  9. data/lib/stac/asset.rb +7 -1
  10. data/lib/stac/catalog.rb +81 -9
  11. data/lib/stac/collection.rb +43 -9
  12. data/lib/stac/common_metadata.rb +162 -0
  13. data/lib/stac/errors.rb +2 -5
  14. data/lib/stac/extension.rb +34 -0
  15. data/lib/stac/extensions/electro_optical.rb +67 -0
  16. data/lib/stac/extensions/projection.rb +42 -0
  17. data/lib/stac/extensions/scientific_citation.rb +84 -0
  18. data/lib/stac/extensions/view_geometry.rb +38 -0
  19. data/lib/stac/extent.rb +39 -31
  20. data/lib/stac/file_writer.rb +31 -0
  21. data/lib/stac/hash_like.rb +74 -0
  22. data/lib/stac/item.rb +58 -22
  23. data/lib/stac/link.rb +50 -14
  24. data/lib/stac/object_resolver.rb +14 -20
  25. data/lib/stac/properties.rb +6 -1
  26. data/lib/stac/provider.rb +5 -1
  27. data/lib/stac/{default_http_client.rb → simple_http_client.rb} +9 -4
  28. data/lib/stac/stac_object.rb +142 -31
  29. data/lib/stac/version.rb +1 -1
  30. data/lib/stac.rb +18 -2
  31. data/sig/stac/asset.rbs +3 -3
  32. data/sig/stac/catalog.rbs +29 -5
  33. data/sig/stac/collection.rbs +13 -5
  34. data/sig/stac/common_metadata.rbs +34 -0
  35. data/sig/stac/errors.rbs +1 -4
  36. data/sig/stac/extension.rbs +12 -0
  37. data/sig/stac/extensions/electro_optical.rbs +40 -0
  38. data/sig/stac/extensions/projection.rbs +32 -0
  39. data/sig/stac/extensions/scientific_citation.rbs +38 -0
  40. data/sig/stac/extensions/view_geometry.rbs +22 -0
  41. data/sig/stac/extent.rbs +13 -16
  42. data/sig/stac/file_writer.rbs +13 -0
  43. data/sig/stac/hash_like.rbs +13 -0
  44. data/sig/stac/item.rbs +17 -7
  45. data/sig/stac/link.rbs +21 -12
  46. data/sig/stac/object_resolver.rbs +5 -9
  47. data/sig/stac/properties.rbs +3 -3
  48. data/sig/stac/provider.rbs +2 -3
  49. data/sig/stac/{default_http_client.rbs → simple_http_client.rbs} +5 -2
  50. data/sig/stac/stac_object.rbs +34 -11
  51. data/sig/stac.rbs +7 -1
  52. data/stac.gemspec +1 -1
  53. metadata +26 -9
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../extension'
4
+ require_relative '../item'
5
+ require_relative '../collection'
6
+
7
+ module STAC
8
+ module Extensions
9
+ # Utilities for Scientific Citation extension.
10
+ #
11
+ # Scientific Citation \Extension Specification: https://github.com/stac-extensions/scientific/
12
+ module ScientificCitation
13
+ extend Extension
14
+
15
+ identifier 'https://stac-extensions.github.io/scientific/v1.0.0/schema.json'
16
+ scope STAC::Item, STAC::Collection
17
+
18
+ module Properties # rubocop:disable Style/Documentation
19
+ attr_reader :extra
20
+
21
+ def sci_doi
22
+ extra['sci:doi']
23
+ end
24
+
25
+ def sci_doi=(doi)
26
+ extra['sci:doi'] = doi
27
+ end
28
+
29
+ def sci_citation
30
+ extra['sci:citation']
31
+ end
32
+
33
+ def sci_citation=(citation)
34
+ extra['sci:citation'] = citation
35
+ end
36
+
37
+ def sci_publications
38
+ extra.fetch('sci:publications', []).map { |hash| Publication.new(hash) }
39
+ end
40
+
41
+ def sci_publications=(publications)
42
+ extra['sci:publications'] = publications.map(&:to_h)
43
+ end
44
+ end
45
+
46
+ module Asset
47
+ include Properties
48
+ end
49
+
50
+ module Collection
51
+ include Properties
52
+ end
53
+
54
+ # Represents \Publication object of Scientific Citation extension.
55
+ class Publication
56
+ attr_reader :raw_hash # :nodoc:
57
+
58
+ def initialize(raw_hash)
59
+ @raw_hash = raw_hash
60
+ end
61
+
62
+ def to_h
63
+ raw_hash
64
+ end
65
+
66
+ def doi
67
+ raw_hash['doi']
68
+ end
69
+
70
+ def doi=(doi)
71
+ raw_hash['doi'] = doi
72
+ end
73
+
74
+ def citation
75
+ raw_hash['citation']
76
+ end
77
+
78
+ def catation=(citation)
79
+ raw_hash['citation'] = citation
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../extension'
4
+ require_relative '../item'
5
+
6
+ module STAC
7
+ module Extensions
8
+ # Utilities for View Geometry extension.
9
+ #
10
+ # View Geometry \Extension Specification: https://github.com/stac-extensions/scientific/
11
+ module ViewGeometry
12
+ extend Extension
13
+
14
+ identifier 'https://stac-extensions.github.io/view/v1.0.0/schema.json'
15
+ scope STAC::Item
16
+
17
+ module Properties # rubocop:disable Style/Documentation
18
+ attr_reader :extra
19
+
20
+ %w[
21
+ view:off_nadir view:incidence_angle view:azimuth view:sun_azimuth view:sun_elevation
22
+ ].each do |field|
23
+ method_name = field.sub(':', '_')
24
+
25
+ define_method(method_name) do
26
+ # @type self: Properties
27
+ extra[field]
28
+ end
29
+
30
+ define_method("#{method_name}=") do |value|
31
+ # @type self: Properties
32
+ extra[field] = value
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/stac/extent.rb CHANGED
@@ -1,18 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'hash_like'
4
+
3
5
  module STAC
4
- # Represents \STAC extent object, which describes the spatio-temporal extents of a Collection.
6
+ # Represents \STAC extent object, which describes the spatio-temporal extents of a collection.
5
7
  #
6
8
  # Specification: https://github.com/radiantearth/stac-spec/blob/master/collection-spec/collection-spec.md#extent-object
7
9
  class Extent
8
- # Describes the spatial extents of a Collection
10
+ include HashLike
11
+
12
+ class << self
13
+ # Deserializes an Extent from a Hash.
14
+ def from_hash(hash)
15
+ transformed = hash.transform_keys(&:to_sym)
16
+ transformed[:spatial] = Spatial.from_hash(transformed.fetch(:spatial))
17
+ transformed[:temporal] = Temporal.from_hash(transformed.fetch(:temporal))
18
+ new(**transformed)
19
+ end
20
+ end
21
+
22
+ attr_accessor :spatial, :temporal
23
+
24
+ def initialize(spatial:, temporal:, **extra)
25
+ @spatial = spatial
26
+ @temporal = temporal
27
+ @extra = extra.transform_keys(&:to_s)
28
+ end
29
+
30
+ # Serializes self to a Hash.
31
+ def to_h
32
+ {
33
+ 'spatial' => spatial.to_h,
34
+ 'temporal' => temporal.to_h,
35
+ }.merge(extra)
36
+ end
37
+
38
+ # Describes the spatial extents of a collection
9
39
  class Spatial
40
+ include HashLike
41
+
10
42
  # Deserializes a Spatial from a Hash.
11
43
  def self.from_hash(hash)
12
44
  new(**hash.transform_keys(&:to_sym))
13
45
  end
14
46
 
15
- attr_accessor :bbox, :extra
47
+ attr_accessor :bbox
16
48
 
17
49
  def initialize(bbox:, **extra)
18
50
  @bbox = bbox
@@ -27,14 +59,16 @@ module STAC
27
59
  end
28
60
  end
29
61
 
30
- # Describes the temporal extents of a Collection.
62
+ # Describes the temporal extents of a collection.
31
63
  class Temporal
64
+ include HashLike
65
+
32
66
  # Deserializes a Temporal from a Hash.
33
67
  def self.from_hash(hash)
34
68
  new(**hash.transform_keys(&:to_sym))
35
69
  end
36
70
 
37
- attr_accessor :interval, :extra
71
+ attr_accessor :interval
38
72
 
39
73
  def initialize(interval:, **extra)
40
74
  @interval = interval
@@ -48,31 +82,5 @@ module STAC
48
82
  }.merge(extra)
49
83
  end
50
84
  end
51
-
52
- class << self
53
- # Deserializes an Extent from a Hash.
54
- def from_hash(hash)
55
- transformed = hash.transform_keys(&:to_sym)
56
- transformed[:spatial] = Spatial.from_hash(transformed.fetch(:spatial))
57
- transformed[:temporal] = Temporal.from_hash(transformed.fetch(:temporal))
58
- new(**transformed)
59
- end
60
- end
61
-
62
- attr_accessor :spatial, :temporal, :extra
63
-
64
- def initialize(spatial:, temporal:, **extra)
65
- @spatial = spatial
66
- @temporal = temporal
67
- @extra = extra.transform_keys(&:to_s)
68
- end
69
-
70
- # Serializes self to a Hash.
71
- def to_h
72
- {
73
- 'spatial' => spatial.to_h,
74
- 'temporal' => temporal.to_h,
75
- }.merge(extra)
76
- end
77
85
  end
78
86
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'json'
5
+ require 'uri'
6
+ require_relative 'errors'
7
+
8
+ module STAC
9
+ # Class to write a hash as JSON on a file.
10
+ class FileWriter
11
+ def initialize(hash_to_json: ->(hash) { JSON.generate(hash) })
12
+ @hash_to_json = hash_to_json
13
+ end
14
+
15
+ # Creates a file on `dest` with the given hash as JSON.
16
+ def write(hash, dest:)
17
+ dest_uri = URI.parse(dest)
18
+ path = if dest_uri.relative?
19
+ dest
20
+ elsif dest_uri.is_a?(URI::File)
21
+ dest_uri.path.to_s
22
+ else
23
+ raise NotSupportedURISchemeError, "not supported URI scheme: #{dest}"
24
+ end
25
+
26
+ pathname = Pathname(path)
27
+ pathname.dirname.mkpath
28
+ pathname.write(@hash_to_json.call(hash))
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module STAC
6
+ # Enables included class to behave like Hash.
7
+ module HashLike
8
+ # Extra fields that do not belong to the STAC core specification.
9
+ attr_reader :extra
10
+
11
+ # When there is an attribute with the given name, returns the attribute value.
12
+ # Otherwise, calls `extra [key]`.
13
+ def [](key)
14
+ if respond_to?(key) && method(key).arity.zero?
15
+ public_send(key)
16
+ else
17
+ extra[key.to_s]
18
+ end
19
+ end
20
+
21
+ # When there is an attribute writer with the given name, assigns the value to the attribute.
22
+ # Otherwise, adds the given key-value pair to `extra` hash.
23
+ def []=(key, value)
24
+ method = "#{key}="
25
+ if respond_to?(method)
26
+ public_send(method, value)
27
+ else
28
+ extra[key.to_s] = value
29
+ end
30
+ end
31
+
32
+ # Sets the attributes (like ActiveModel::AttributeAssignment#assign_attributes)
33
+ # or merges the args into `extra` hash (like Hash#update).
34
+ def update(**options)
35
+ options.each do |key, value|
36
+ self[key] = value
37
+ end
38
+ self
39
+ end
40
+
41
+ def to_hash # :nodoc:
42
+ to_h
43
+ end
44
+
45
+ # Serializes self to a Hash.
46
+ def to_h
47
+ extra
48
+ end
49
+
50
+ # Serializes self to a JSON string.
51
+ def to_json(...)
52
+ to_h.to_json(...)
53
+ end
54
+
55
+ # Returns `true` if all of the followings are true:
56
+ # - the given object is an instance of tha same class
57
+ # - `self.to_hash == other.to_hash`
58
+ #
59
+ # Otherwise, returns `false`.
60
+ def ==(other)
61
+ other.instance_of?(self.class) && to_hash == other.to_hash
62
+ end
63
+
64
+ # Returns a copy of self by serializes self to a JSON and desirializes it by `.from_hash`.
65
+ def deep_dup
66
+ unless self.class.respond_to?(:from_hash)
67
+ raise NotImplementedError, "#{self.class} must implement `.from_hash(hash)` to use `HashLike#deep_dup`"
68
+ end
69
+
70
+ hash = JSON.parse(to_json)
71
+ self.class.from_hash(hash)
72
+ end
73
+ end
74
+ end
data/lib/stac/item.rb CHANGED
@@ -10,25 +10,27 @@ module STAC
10
10
  #
11
11
  # \STAC \Item Specification: https://github.com/radiantearth/stac-spec/tree/master/item-spec
12
12
  class Item < STAC::STACObject
13
- self.type = 'Feature'
13
+ @type = 'Feature'
14
14
 
15
15
  class << self
16
16
  def from_hash(hash)
17
- h = hash.dup
18
- h['properties'] = Properties.from_hash(h.fetch('properties'))
19
- h['assets'] = h.fetch('assets').transform_values { |v| Asset.from_hash(v) }
17
+ h = hash.transform_keys(&:to_sym)
18
+ h[:properties] = Properties.from_hash(h.fetch(:properties, {}))
19
+ h[:assets] = h.fetch(:assets, {}).transform_values { |v| Asset.from_hash(v) }
20
20
  super(h)
21
21
  rescue KeyError => e
22
22
  raise ArgumentError, "required field not found: #{e.key}"
23
23
  end
24
24
  end
25
25
 
26
- attr_accessor :geometry, :bbox, :properties, :assets, :collection_id
26
+ attr_accessor :id, :geometry, :bbox, :collection_id
27
+
28
+ attr_reader :properties, :assets
27
29
 
28
30
  def initialize(
29
- id:, geometry:, properties:, links:, assets:, bbox: nil, collection: nil, stac_extensions: nil, **extra
31
+ id:, geometry:, properties:, links: [], assets: {}, bbox: nil, collection: nil, stac_extensions: [], **extra
30
32
  )
31
- super(id: id, links: links, stac_extensions: stac_extensions, **extra)
33
+ @id = id
32
34
  @geometry = geometry
33
35
  @properties = properties
34
36
  @assets = assets
@@ -39,6 +41,16 @@ module STAC
39
41
  else
40
42
  @collection_id = collection
41
43
  end
44
+ super(links: links, stac_extensions: stac_extensions, **extra)
45
+ end
46
+
47
+ def [](key)
48
+ value = super
49
+ if value.nil?
50
+ properties.extra[key.to_s]
51
+ else
52
+ value
53
+ end
42
54
  end
43
55
 
44
56
  def to_h
@@ -48,6 +60,7 @@ module STAC
48
60
  },
49
61
  ).merge(
50
62
  {
63
+ 'id' => id,
51
64
  'bbox' => bbox,
52
65
  'properties' => properties.to_h,
53
66
  'assets' => assets.transform_values(&:to_h),
@@ -56,11 +69,6 @@ module STAC
56
69
  )
57
70
  end
58
71
 
59
- # Returns datetime from #properties.
60
- def datetime
61
- properties.datetime
62
- end
63
-
64
72
  # Returns a rel="collection" link as a collection object if it exists.
65
73
  def collection
66
74
  link = find_link(rel: 'collection')
@@ -69,17 +77,45 @@ module STAC
69
77
 
70
78
  # Overwrites rel="collection" link and #collection_id attribute.
71
79
  def collection=(collection)
72
- raise ArgumentError, 'collection must have a rel="self" link' unless (collection_href = collection.self_href)
73
-
74
80
  @collection_id = collection.id
75
- collection_link = Link.new(
76
- rel: 'collection',
77
- href: collection_href,
78
- type: 'application/json',
79
- title: collection.title,
80
- )
81
- remove_link(rel: 'collection')
82
- add_link(collection_link)
81
+ remove_links(rel: 'collection')
82
+ add_link(collection, rel: 'collection', type: 'application/json', title: collection.title)
83
+ end
84
+
85
+ # Adds an asset with the given key.
86
+ #
87
+ # When the item has extendable stac_extensions, make the asset extend the extension modules.
88
+ def add_asset(key:, href:, title: nil, description: nil, type: nil, roles: nil, **extra)
89
+ asset = Asset.new(href: href, title: title, description: description, type: type, roles: roles, **extra)
90
+ extensions.each do |extension|
91
+ asset.extend(extension::Asset) if extension.const_defined?(:Asset)
92
+ end
93
+ assets[key] = asset
94
+ self
95
+ end
96
+
97
+ private
98
+
99
+ def respond_to_missing?(symbol, include_all)
100
+ if properties.respond_to?(symbol)
101
+ true
102
+ else
103
+ super
104
+ end
105
+ end
106
+
107
+ def method_missing(symbol, *args, **options, &block)
108
+ if properties.respond_to?(symbol)
109
+ properties.public_send(symbol, *args, **options, &block)
110
+ else
111
+ super
112
+ end
113
+ end
114
+
115
+ def apply_extension!(extension)
116
+ super
117
+ properties.extend(extension::Properties) if extension.const_defined?(:Properties)
118
+ assets.each_value { |asset| asset.extend(extension::Asset) } if extension.const_defined?(:Asset)
83
119
  end
84
120
  end
85
121
  end
data/lib/stac/link.rb CHANGED
@@ -3,10 +3,23 @@
3
3
  require 'pathname'
4
4
  require 'uri'
5
5
  require_relative 'errors'
6
+ require_relative 'hash_like'
6
7
 
7
8
  module STAC
9
+ # Raised when a link does not have href or owner.
10
+ class LinkHrefError < Error
11
+ attr_reader :link
12
+
13
+ def initialize(msg = nil, link:)
14
+ super(msg)
15
+ @link = link
16
+ end
17
+ end
18
+
8
19
  # Represents \STAC link object, which describes a relationship with another entity.
9
20
  class Link
21
+ include HashLike
22
+
10
23
  class << self
11
24
  # Deserializes a Link from a Hash.
12
25
  def from_hash(hash)
@@ -14,14 +27,15 @@ module STAC
14
27
  end
15
28
  end
16
29
 
17
- attr_accessor :rel, :href, :type, :title, :extra
30
+ attr_accessor :rel, :type, :title
31
+
32
+ attr_writer :href
18
33
 
19
34
  # Owner object of this link.
20
35
  attr_accessor :owner
21
36
 
22
- attr_writer :resolver # :nodoc:
23
-
24
- def initialize(rel:, href:, type: nil, title: nil, **extra)
37
+ def initialize(target = nil, rel:, href:, type: nil, title: nil, **extra)
38
+ @target = target
25
39
  @rel = rel
26
40
  @href = href
27
41
  @type = type
@@ -39,32 +53,54 @@ module STAC
39
53
  }.merge(extra).compact
40
54
  end
41
55
 
56
+ # Determines if the link's target is a resolved STACObject.
57
+ def resolved?
58
+ !@target.nil?
59
+ end
60
+
61
+ def href
62
+ @href || @target&.self_href
63
+ end
64
+
42
65
  # Returns the absolute HREF for this link.
43
- #
44
- # When it could not assemble the absolute HREF, it returns nil.
45
66
  def absolute_href
46
- if URI(href).absolute?
47
- href
67
+ if URI(href!).absolute?
68
+ href!
69
+ elsif (base_href = owner&.self_href)
70
+ Pathname(base_href).dirname.join(href!).to_s
71
+ end
72
+ end
73
+
74
+ # Returns the relative HREF for this link.
75
+ def relative_href
76
+ if URI(href!).relative?
77
+ href!
48
78
  elsif (base_href = owner&.self_href)
49
- Pathname(base_href).dirname.join(href).to_s
79
+ Pathname(href!).relative_path_from(Pathname(base_href).dirname).to_s
50
80
  end
51
81
  end
52
82
 
53
83
  # Returns a \STAC object resolved from HREF.
54
84
  #
55
85
  # When it could not assemble the absolute HREF, it returns nil.
56
- def target
86
+ def target(http_client: owner&.http_client || STAC.default_http_client)
57
87
  @target ||= if (url = absolute_href)
58
- object = resolver.resolve(url)
88
+ object = ObjectResolver.new(http_client: http_client).resolve(url)
59
89
  object.self_href = url
60
90
  object
61
91
  end
62
92
  end
63
93
 
64
- private
94
+ def href! # :nodoc:
95
+ href or raise LinkHrefError.new('href is nil', link: self)
96
+ end
97
+
98
+ def absolute_href! # :nodoc:
99
+ absolute_href or raise LinkHrefError.new('could not assemble absolute href', link: self)
100
+ end
65
101
 
66
- def resolver
67
- @resolver ||= ObjectResolver.new
102
+ def relative_href! # :nodoc:
103
+ relative_href or raise LinkHrefError.new('could not assemble relative href', link: self)
68
104
  end
69
105
  end
70
106
  end
@@ -3,30 +3,22 @@
3
3
  require 'json'
4
4
  require_relative 'catalog'
5
5
  require_relative 'collection'
6
- require_relative 'default_http_client'
7
6
  require_relative 'errors'
8
7
  require_relative 'item'
9
8
 
10
9
  module STAC
11
10
  # Resolves a \STAC object from a URL.
12
11
  class ObjectResolver
13
- RESOLVABLES = [Catalog, Collection, Item].freeze # :nodoc:
14
-
15
12
  class << self
16
- # Sets the default HTTP client.
17
- #
18
- # HTTP client must implement method `get: (URI uri) -> String,` which fetches the URI resource through HTTP.
19
- attr_writer :default_http_client
20
-
21
- # Returns the default HTTP client.
22
- def default_http_client
23
- @default_http_client ||= DefaultHTTPClient.new
24
- end
13
+ # Resolvable classes. Default is Catalog, Collection and Item.
14
+ attr_reader :resolvables
25
15
  end
26
16
 
17
+ @resolvables = [Catalog, Collection, Item]
18
+
27
19
  attr_reader :http_client
28
20
 
29
- def initialize(http_client: self.class.default_http_client)
21
+ def initialize(http_client:)
30
22
  @http_client = http_client
31
23
  end
32
24
 
@@ -38,15 +30,16 @@ module STAC
38
30
  # - file
39
31
  #
40
32
  # Raises:
41
- # - STAC::UnknownURISchemeError when a URL with unsupported scheme was given
33
+ # - STAC::NotSupportedURISchemeError when a URL with not supported scheme was given
42
34
  # - STAC::TypeError when it could not resolve any \STAC objects
43
35
  def resolve(url)
44
- str = read(url)
45
- hash = JSON.parse(str)
46
- klass = RESOLVABLES.find { |c| c.type == hash['type'] }
36
+ hash = read(url)
37
+ klass = self.class.resolvables.find { |c| c.type == hash['type'] }
47
38
  raise TypeError, "unknown STAC object type: #{hash['type']}" unless klass
48
39
 
49
- klass.from_hash(hash)
40
+ object = klass.from_hash(hash)
41
+ object.http_client = http_client
42
+ object
50
43
  end
51
44
 
52
45
  private
@@ -57,9 +50,10 @@ module STAC
57
50
  when URI::HTTP
58
51
  http_client.get(uri)
59
52
  when URI::File
60
- File.read(uri.path.to_s)
53
+ str = File.read(uri.path.to_s)
54
+ JSON.parse(str)
61
55
  else
62
- raise UnknownURISchemeError, "unknown URI scheme: #{url}"
56
+ raise NotSupportedURISchemeError, "not supported URI scheme: #{url}"
63
57
  end
64
58
  end
65
59
  end
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'time'
4
+ require_relative 'common_metadata'
5
+ require_relative 'hash_like'
4
6
 
5
7
  module STAC
6
8
  # Represents \STAC properties object, which is additional metadata for Item.
7
9
  #
8
10
  # Specification: https://github.com/radiantearth/stac-spec/blob/master/item-spec/item-spec.md#properties-object
9
11
  class Properties
12
+ include HashLike
13
+ include CommonMetadata
14
+
10
15
  class << self
11
16
  # Deserializes a Properties from a Hash.
12
17
  def from_hash(hash)
@@ -16,7 +21,7 @@ module STAC
16
21
  end
17
22
  end
18
23
 
19
- attr_accessor :datetime, :extra
24
+ attr_accessor :datetime
20
25
 
21
26
  def initialize(datetime:, **extra)
22
27
  @datetime = datetime
data/lib/stac/provider.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'hash_like'
4
+
3
5
  module STAC
4
6
  # Represents \STAC provider object, which provides information about a provider.
5
7
  #
6
8
  # Specicication: https://github.com/radiantearth/stac-spec/blob/master/collection-spec/collection-spec.md#provider-object
7
9
  class Provider
10
+ include HashLike
11
+
8
12
  class << self
9
13
  # Deserializes a Provider from a Hash.
10
14
  def from_hash(hash)
@@ -12,7 +16,7 @@ module STAC
12
16
  end
13
17
  end
14
18
 
15
- attr_accessor :name, :description, :roles, :url, :extra
19
+ attr_accessor :name, :description, :roles, :url
16
20
 
17
21
  def initialize(name:, description: nil, roles: nil, url: nil, **extra)
18
22
  @name = name