stac 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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