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
data/Rakefile CHANGED
@@ -1,19 +1,77 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
-
5
4
  require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+ require 'rdoc/task'
7
+
6
8
  RSpec::Core::RakeTask.new(:spec)
7
9
 
8
- require 'rubocop/rake_task'
9
10
  RuboCop::RakeTask.new
10
11
 
11
12
  task default: %i[spec rubocop]
12
13
 
13
- require 'rdoc/task'
14
14
  RDoc::Task.new do |rdoc|
15
15
  rdoc.rdoc_dir = 'doc'
16
16
  rdoc.title = 'stac-ruby API docs'
17
- rdoc.rdoc_files.include('README.md', 'CHANGELOG.md', 'lib/**/*.rb')
17
+ rdoc.rdoc_files.include('README.md', 'CHANGELOG.md', 'GETTING_STARTED.md', 'lib/**/*.rb')
18
18
  rdoc.main = 'README.md'
19
19
  end
20
+
21
+ namespace :fixture do
22
+ desc 'Setup spec/fixtures'
23
+ task setup: %w[
24
+ spec/fixtures/stac-spec
25
+ spec/fixtures/eo/item.json
26
+ spec/fixtures/projection/item.json
27
+ spec/fixtures/scientific/item.json
28
+ spec/fixtures/scientific/collection.json
29
+ spec/fixtures/scientific/collection-assets.json
30
+ spec/fixtures/view/item.json
31
+ ]
32
+
33
+ desc 'Remove spec/fixtures'
34
+ task :clean do
35
+ rm_r 'spec/fixtures'
36
+ end
37
+
38
+ directory 'spec/fixtures/stac-spec'
39
+
40
+ file 'spec/fixtures/stac-spec' do |t|
41
+ cp_r 'stac-spec/examples/.', t.name
42
+ end
43
+
44
+ directory 'spec/fixtures/eo'
45
+
46
+ file 'spec/fixtures/eo/item.json' => 'spec/fixtures/eo' do |t|
47
+ sh "curl https://raw.githubusercontent.com/stac-extensions/eo/v1.0.0/examples/item.json -o #{t.name} -sS"
48
+ end
49
+
50
+ directory 'spec/fixtures/projection'
51
+
52
+ file 'spec/fixtures/projection/item.json' => 'spec/fixtures/projection' do |t|
53
+ sh "curl https://raw.githubusercontent.com/stac-extensions/projection/v1.0.0/examples/item.json -o #{t.name} -sS"
54
+ end
55
+
56
+ directory 'spec/fixtures/scientific'
57
+
58
+ file 'spec/fixtures/scientific/item.json' => 'spec/fixtures/scientific' do |t|
59
+ sh "curl https://raw.githubusercontent.com/stac-extensions/scientific/v1.0.0/examples/item.json -o #{t.name} -sS"
60
+ end
61
+
62
+ file 'spec/fixtures/scientific/collection.json' => 'spec/fixtures/scientific' do |t|
63
+ sh 'curl https://raw.githubusercontent.com/stac-extensions/scientific/v1.0.0/examples/collection.json ' \
64
+ "-o #{t.name} -sS"
65
+ end
66
+
67
+ file 'spec/fixtures/scientific/collection-assets.json' => 'spec/fixtures/scientific' do |t|
68
+ sh 'curl https://raw.githubusercontent.com/stac-extensions/scientific/v1.0.0/examples/collection-assets.json ' \
69
+ "-o #{t.name} -sS"
70
+ end
71
+
72
+ directory 'spec/fixtures/view'
73
+
74
+ file 'spec/fixtures/view/item.json' => 'spec/fixtures/view' do |t|
75
+ sh "curl https://raw.githubusercontent.com/stac-extensions/view/v1.0.0/examples/item.json -o #{t.name} -sS"
76
+ end
77
+ end
data/Steepfile CHANGED
@@ -15,6 +15,8 @@ target :lib do
15
15
  hash[D::Ruby::InsufficientKeywordArguments] = :hint
16
16
  hash[D::Ruby::MethodBodyTypeMismatch] = :information
17
17
  hash[D::Ruby::MethodDefinitionMissing] = nil # To supress noisy VS Code extension message.
18
+ hash[D::Ruby::NoMethod] = :information
19
+ hash[D::Ruby::UnknownConstant] = :information
18
20
  hash[D::Ruby::UnsupportedSyntax] = :hint
19
21
  end
20
22
  end
data/lib/stac/asset.rb CHANGED
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'common_metadata'
4
+ require_relative 'hash_like'
5
+
3
6
  module STAC
4
7
  # Represents \STAC asset object, which contains a link to data associated with an Item or Collection that can be
5
8
  # downloaded or streamed.
6
9
  class Asset
10
+ include HashLike
11
+ include CommonMetadata
12
+
7
13
  class << self
8
14
  # Deserializes an Asset from a Hash.
9
15
  def from_hash(hash)
@@ -11,7 +17,7 @@ module STAC
11
17
  end
12
18
  end
13
19
 
14
- attr_accessor :href, :title, :description, :type, :roles, :extra
20
+ attr_accessor :href, :title, :description, :type, :roles
15
21
 
16
22
  def initialize(href:, title: nil, description: nil, type: nil, roles: nil, **extra)
17
23
  @href = href
data/lib/stac/catalog.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
3
4
  require_relative 'errors'
4
5
  require_relative 'stac_object'
5
6
 
@@ -8,20 +9,33 @@ module STAC
8
9
  #
9
10
  # \STAC \Catalog Specification: https://github.com/radiantearth/stac-spec/tree/master/catalog-spec
10
11
  class Catalog < STAC::STACObject
11
- self.type = 'Catalog'
12
+ @type = 'Catalog'
12
13
 
13
- attr_accessor :description, :title
14
+ class << self
15
+ def root(id:, description:, href:, links: [], title: nil, stac_extensions: [], **extra)
16
+ catalog = new(
17
+ id: id, description: description, links: links, title: title, stac_extensions: stac_extensions, **extra,
18
+ )
19
+ catalog.self_href = href
20
+ catalog.root = catalog
21
+ catalog
22
+ end
23
+ end
24
+
25
+ attr_accessor :id, :description, :title
14
26
 
15
- def initialize(id:, description:, links:, title: nil, stac_extensions: nil, **extra)
16
- super(id: id, links: links, stac_extensions: stac_extensions, **extra)
27
+ def initialize(id:, description:, links: [], title: nil, stac_extensions: [], **extra)
28
+ @id = id
17
29
  @description = description
18
30
  @title = title
31
+ super(links: links, stac_extensions: stac_extensions, **extra)
19
32
  end
20
33
 
21
34
  # Serializes self to a Hash.
22
35
  def to_h
23
36
  super.merge(
24
37
  {
38
+ 'id' => id,
25
39
  'title' => title,
26
40
  'description' => description,
27
41
  }.compact,
@@ -30,7 +44,11 @@ module STAC
30
44
 
31
45
  # Returns catalog/collection objects from rel="child" links of this catalog.
32
46
  def children
33
- links.select { |link| link.rel == 'child' }.lazy.map(&:target)
47
+ child_links.lazy.map(&:target)
48
+ end
49
+
50
+ def all_children # :nodoc:
51
+ children.chain(children.flat_map(&:all_children)).lazy
34
52
  end
35
53
 
36
54
  # Filters only collections from #children.
@@ -38,17 +56,24 @@ module STAC
38
56
  children.select { |child| child.type == 'Collection' }
39
57
  end
40
58
 
59
+ # Returns all collections from this catalog and its child catalogs/collections recursively.
60
+ def all_collections
61
+ # The last `.lazy` is not necessary with Ruby 3.1.
62
+ # But with Ruby 3.0, it is necessary because Enumerator::Lazy#chain returns Enumerator::Chain
63
+ # and RBS type check fails.
64
+ collections.chain(children.flat_map(&:all_collections)).lazy
65
+ end
66
+
41
67
  # Returns the child catalog/collection with the given ID if it exists.
42
68
  #
43
69
  # With option `recusive: true`, it will traverse all child catalogs/collections recursively.
44
70
  def find_child(id, recursive: false)
45
- targets = recursive ? children.chain(children.flat_map(&:children)) : children
46
- targets.find { |child| child.id == id }
71
+ (recursive ? all_children : children).find { |child| child.id == id }
47
72
  end
48
73
 
49
74
  # Returns item objects from rel="item" links of this catalog.
50
75
  def items
51
- links.select { |link| link.rel == 'item' }.lazy.map(&:target)
76
+ item_links.lazy.map(&:target)
52
77
  end
53
78
 
54
79
  # Returns all items from this catalog and its child catalogs/collections recursively.
@@ -56,7 +81,7 @@ module STAC
56
81
  # The last `.lazy` is not necessary with Ruby 3.1.
57
82
  # But with Ruby 3.0, it is necessary because Enumerator::Lazy#chain returns Enumerator::Chain
58
83
  # and RBS type check fails.
59
- items.chain(children.flat_map(&:items)).lazy
84
+ items.chain(children.flat_map(&:all_items)).lazy
60
85
  end
61
86
 
62
87
  # Returns the item with the given ID if it exists.
@@ -65,5 +90,52 @@ module STAC
65
90
  def find_item(id, recursive: false)
66
91
  (recursive ? all_items : items).find { |item| item.id == id }
67
92
  end
93
+
94
+ # Adds a rel="child" link to self and adds "self", "root", and "parent" links to the child catalog.
95
+ def add_child(catalog, href: "#{catalog.id}/#{catalog.type.downcase}.json", title: catalog.title)
96
+ if (base = self_href)
97
+ catalog.self_href = Pathname(base).dirname.join(href).to_s
98
+ end
99
+ catalog.root = root
100
+ catalog.parent = self
101
+ add_link(catalog, rel: 'child', type: 'application/json', title: title)
102
+ end
103
+
104
+ # Adds a rel="item" link to self and adds "self", "root", and "parent" links to the given item.
105
+ def add_item(item, href: "#{item.id}.json", title: item.properties.title)
106
+ if (base = self_href)
107
+ item.self_href = Pathname(base).dirname.join(href).to_s
108
+ end
109
+ item.root = root
110
+ item.parent = self
111
+ add_link(item, rel: 'item', type: 'application/geo+json', title: title)
112
+ end
113
+
114
+ # Exports this catalog and all its children and items to the specified dir or each self href.
115
+ def export(dest_dir = nil, writer: FileWriter.new)
116
+ dest_pathname = Pathname(dest_dir) if dest_dir
117
+ self_dest = dest_pathname.join(File.basename(self_href!)).to_s if dest_pathname
118
+ save(self_dest, writer: writer)
119
+
120
+ item_links.select(&:resolved?).each do |item_link|
121
+ item_dest = dest_pathname.join(item_link.relative_href!).to_s if dest_pathname
122
+ item_link.target.save(item_dest, writer: writer)
123
+ end
124
+
125
+ child_links.select(&:resolved?).each do |child_link|
126
+ child_dest_dir = dest_pathname.join(child_link.relative_href!).dirname.to_s if dest_pathname
127
+ child_link.target.export(child_dest_dir, writer: writer)
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ def child_links
134
+ links.select { |link| link.rel == 'child' }
135
+ end
136
+
137
+ def item_links
138
+ links.select { |link| link.rel == 'item' }
139
+ end
68
140
  end
69
141
  end
@@ -10,43 +10,46 @@ module STAC
10
10
  #
11
11
  # \STAC \Collection Specification: https://github.com/radiantearth/stac-spec/tree/master/collection-spec
12
12
  class Collection < Catalog
13
- self.type = 'Collection'
13
+ @type = 'Collection'
14
14
 
15
15
  class << self
16
16
  def from_hash(hash)
17
- h = hash.dup
18
- h['extent'] = Extent.from_hash(h.fetch('extent'))
19
- h['providers'] = h['providers']&.map { |provider| Provider.from_hash(provider) }
20
- h['assets'] = h['assets']&.transform_values { |v| Asset.from_hash(v) }
17
+ h = hash.transform_keys(&:to_sym)
18
+ h[:extent] = Extent.from_hash(h.fetch(:extent))
19
+ h[:providers] = h[:providers]&.map { |provider| Provider.from_hash(provider) }
20
+ h[:summaries] = h[:summaries]&.transform_keys(&:to_s)
21
+ h[:assets] = h[:assets]&.to_h { |k, v| [k.to_s, Asset.from_hash(v)] }
21
22
  super(h)
22
23
  rescue KeyError => e
23
24
  raise ArgumentError, "required field not found: #{e.key}"
24
25
  end
25
26
  end
26
27
 
27
- attr_accessor :license, :extent, :keywords, :providers, :summaries, :assets
28
+ attr_accessor :license, :extent, :keywords, :providers, :summaries
29
+
30
+ attr_reader :assets
28
31
 
29
32
  def initialize(
30
33
  id:,
31
34
  description:,
32
- links:,
33
35
  license:,
34
36
  extent:,
37
+ links: [],
35
38
  title: nil,
36
39
  keywords: nil,
37
40
  providers: nil,
38
41
  summaries: nil,
39
42
  assets: nil,
40
- stac_extensions: nil,
43
+ stac_extensions: [],
41
44
  **extra
42
45
  )
43
- super(id: id, description: description, links: links, title: title, stac_extensions: stac_extensions, **extra)
44
46
  @license = license
45
47
  @extent = extent
46
48
  @keywords = keywords
47
49
  @providers = providers
48
50
  @summaries = summaries
49
51
  @assets = assets
52
+ super(id: id, description: description, links: links, title: title, stac_extensions: stac_extensions, **extra)
50
53
  end
51
54
 
52
55
  # Serializes self to a Hash.
@@ -62,5 +65,36 @@ module STAC
62
65
  }.compact,
63
66
  )
64
67
  end
68
+
69
+ # Adds an asset with the given key.
70
+ #
71
+ # When the item has extendable stac_extensions, make the asset extend the extension modules.
72
+ def add_asset(key:, href:, title: nil, description: nil, type: nil, roles: nil, **extra)
73
+ asset = Asset.new(href: href, title: title, description: description, type: type, roles: roles, **extra)
74
+ extensions.each do |extension|
75
+ asset.extend(extension::Asset) if extension.const_defined?(:Asset)
76
+ end
77
+ if assets
78
+ assets[key] = asset
79
+ else
80
+ @assets = { key => asset }
81
+ end
82
+ self
83
+ end
84
+
85
+ # Adds a rel="item" link to self and adds "self", "root", "parent", and "collection" links to the given item.
86
+ def add_item(item, href: "#{item.id}.json", title: item.properties.title)
87
+ super
88
+ item.collection = self
89
+ self
90
+ end
91
+
92
+ private
93
+
94
+ def apply_extension!(extension)
95
+ super
96
+ extend(extension::Collection) if extension.const_defined?(:Collection)
97
+ assets&.each_value { |asset| asset.extend(extension::Asset) } if extension.const_defined?(:Asset)
98
+ end
65
99
  end
66
100
  end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ module STAC
6
+ # Provides read/write methods for \STAC Common Metadata.
7
+ #
8
+ # These methods are shorthand accessors of #extra Hash.
9
+ # Asset and \Item Properties include this module.
10
+ #
11
+ # Specification: https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md
12
+ module CommonMetadata
13
+ attr_reader :extra
14
+
15
+ def title
16
+ extra['title']
17
+ end
18
+
19
+ def title=(str)
20
+ extra['title'] = str
21
+ end
22
+
23
+ def description
24
+ extra['description']
25
+ end
26
+
27
+ def description=(str)
28
+ extra['description'] = str
29
+ end
30
+
31
+ def created
32
+ if (str = extra['created'])
33
+ Time.iso8601(str)
34
+ end
35
+ end
36
+
37
+ def created=(time)
38
+ extra['created'] = case time
39
+ when Time
40
+ time.iso8601
41
+ else
42
+ time
43
+ end
44
+ end
45
+
46
+ def updated
47
+ if (str = extra['updated'])
48
+ Time.iso8601(str)
49
+ end
50
+ end
51
+
52
+ def updated=(time)
53
+ extra['updated'] = case time
54
+ when Time
55
+ time.iso8601
56
+ else
57
+ time
58
+ end
59
+ end
60
+
61
+ def start_datetime
62
+ if (str = extra['start_datetime'])
63
+ Time.iso8601(str)
64
+ end
65
+ end
66
+
67
+ def start_datetime=(time)
68
+ extra['start_datetime'] = case time
69
+ when Time
70
+ time.iso8601
71
+ else
72
+ time
73
+ end
74
+ end
75
+
76
+ def end_datetime
77
+ if (str = extra['end_datetime'])
78
+ Time.iso8601(str)
79
+ end
80
+ end
81
+
82
+ def end_datetime=(time)
83
+ extra['end_datetime'] = case time
84
+ when Time
85
+ time.iso8601
86
+ else
87
+ time
88
+ end
89
+ end
90
+
91
+ # Returns a range from #start_datetime to #end_datetime.
92
+ def datetime_range
93
+ if (start = start_datetime) && (last = end_datetime)
94
+ start..last
95
+ end
96
+ end
97
+
98
+ # Sets #start_datetime and #end_datetime by the given range.
99
+ def datetime_range=(time_range)
100
+ self.start_datetime = time_range.begin
101
+ self.end_datetime = time_range.end
102
+ end
103
+
104
+ def license
105
+ extra['license']
106
+ end
107
+
108
+ def license=(str)
109
+ extra['license'] = str
110
+ end
111
+
112
+ def providers
113
+ extra.fetch('providers', []).map do |provider_hash|
114
+ Provider.from_hash(provider_hash)
115
+ end
116
+ end
117
+
118
+ def providers=(arr)
119
+ extra['providers'] = arr.map(&:to_h)
120
+ end
121
+
122
+ def platform
123
+ extra['platform']
124
+ end
125
+
126
+ def platform=(str)
127
+ extra['platform'] = str
128
+ end
129
+
130
+ def instruments
131
+ extra['instruments']
132
+ end
133
+
134
+ def instruments=(arr)
135
+ extra['instruments'] = arr
136
+ end
137
+
138
+ def constellation
139
+ extra['constellation']
140
+ end
141
+
142
+ def constellation=(str)
143
+ extra['constellation'] = str
144
+ end
145
+
146
+ def mission
147
+ extra['mission']
148
+ end
149
+
150
+ def mission=(str)
151
+ extra['mission'] = str
152
+ end
153
+
154
+ def gsd
155
+ extra['gsd']
156
+ end
157
+
158
+ def gsd=(num)
159
+ extra['gsd'] = num
160
+ end
161
+ end
162
+ end
data/lib/stac/errors.rb CHANGED
@@ -7,9 +7,6 @@ module STAC
7
7
  # Raised when unexpected "type" field was given.
8
8
  class TypeError < Error; end
9
9
 
10
- # Raised when URL with unsupported scheme was given.
11
- class UnknownURISchemeError < Error; end
12
-
13
- # Raised when a HTTP request failed.
14
- class HTTPError < Error; end
10
+ # Raised when URL with not supported scheme was given.
11
+ class NotSupportedURISchemeError < Error; end
15
12
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors'
4
+
5
+ module STAC
6
+ # Raised when an extension module does have identifier.
7
+ class ExtensionWithoutIdentifierError < Error; end
8
+
9
+ # \Extension modules must extend this module.
10
+ module Extension
11
+ # Returns extension id for `stac_extensions` field.
12
+ #
13
+ # When given an argument, sets the given value as its identifier.
14
+ def identifier(identifier = nil)
15
+ # @type self: Module
16
+ if identifier.nil?
17
+ @identifier or raise ExtensionWithoutIdentifierError, "extension module must be set identifier: #{name}"
18
+ else
19
+ @identifier = identifier
20
+ end
21
+ end
22
+
23
+ # Returns extendable \STAC Object classes.
24
+ #
25
+ # When given arguments, sets the given values as its scope.
26
+ def scope(*scope)
27
+ if scope.empty?
28
+ @scope ||= []
29
+ else
30
+ @scope = scope
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../extension'
4
+ require_relative '../item'
5
+
6
+ module STAC
7
+ module Extensions
8
+ # Utilities for Electro-Optical extension.
9
+ #
10
+ # Electro-Optical \Extension Specification: https://github.com/stac-extensions/eo/
11
+ module ElectroOptical
12
+ extend Extension
13
+
14
+ identifier 'https://stac-extensions.github.io/eo/v1.0.0/schema.json'
15
+ scope STAC::Item
16
+
17
+ module Properties # rubocop:disable Style/Documentation
18
+ attr_reader :extra
19
+
20
+ def eo_bands
21
+ extra.fetch('eo:bands', []).map { |band_hash| Band.new(band_hash) }
22
+ end
23
+
24
+ def eo_bands=(bands)
25
+ extra['eo:bands'] = bands.map(&:to_h)
26
+ end
27
+
28
+ def eo_cloud_cover
29
+ extra['eo:cloud_cover']
30
+ end
31
+
32
+ def eo_cloud_cover=(cloud_cover)
33
+ extra['eo:cloud_cover'] = cloud_cover
34
+ end
35
+ end
36
+
37
+ module Asset
38
+ include Properties
39
+ end
40
+
41
+ # Represents \Band object of Electro-Optical extension.
42
+ class Band
43
+ attr_reader :raw_hash # :nodoc:
44
+
45
+ def initialize(raw_hash)
46
+ @raw_hash = raw_hash
47
+ end
48
+
49
+ def to_h
50
+ raw_hash
51
+ end
52
+
53
+ %i[name common_name description center_wavelength full_width_half_max].each do |field|
54
+ define_method(field) do
55
+ # @type self: Band
56
+ raw_hash[field.to_s]
57
+ end
58
+
59
+ define_method("#{field}=") do |value|
60
+ # @type self: Band
61
+ raw_hash[field.to_s] = value
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../extension'
4
+ require_relative '../item'
5
+
6
+ module STAC
7
+ module Extensions
8
+ # Utilities for \Projection extension.
9
+ #
10
+ # \Projection \Extension Specification: https://github.com/stac-extensions/projection/
11
+ module Projection
12
+ extend Extension
13
+
14
+ identifier 'https://stac-extensions.github.io/projection/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
+ proj:epsg proj:wkt2 proj:projjson proj:geometry proj:bbox proj:centroid proj:shape proj:transform
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
+
37
+ module Asset
38
+ include Properties
39
+ end
40
+ end
41
+ end
42
+ end