iiif-presentation 1.2.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a766530b4d9f912c49a41e9c6d5982282671c4188a26b27e5a4fb99176fae90c
4
- data.tar.gz: bbb25e477c3563ab2a65ec0de5bfc634acffa93c00e7504992dfe539f0e89ad2
3
+ metadata.gz: 55260d04809482b5ebd267655c07543d4c97e2d5166bdca7b26eff5b22725fe6
4
+ data.tar.gz: 616cb247a11b55e56aac745508e5aa5000cae11c47b3e384d37ca2011b187c55
5
5
  SHA512:
6
- metadata.gz: ac981b561c8e99f41891f2e5246bf46e6de9766abde2269e5eb46e35baeabb4a74404366b8e83ff2eef4e8656ddcb3e8946cd07bfcf14a96c40ba10b29f2fab7
7
- data.tar.gz: 126391967ebc6b1e3db349daed94e3aad1ba43f8083729c49b0c976131f5e3c85f8f507713d0f04721f98c494b0193b761a714f1129b9b044d0dcf181e21e112
6
+ metadata.gz: e83acf715f8b6bf0513d1211ce5797237f28e9cead4c2cf3446b150ba496df81e16ba7b0d7ffae04aebe9cb40a4b84098c8d184e493f1259419cd0f9d99fae0b
7
+ data.tar.gz: 2221b5bcfc51cabfefc039584a58b93bd16fa95f7729e7921017bdac46b6d71e8cea6cbcc687e38f96a903386d4b7daf56d66693868e719b7cb6944ccc1067a3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.3.0
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency 'json'
26
26
  spec.add_dependency 'activesupport', '>= 3.2.18'
27
27
  spec.add_dependency 'faraday', '~> 2.7'
28
+ spec.add_dependency 'geo_coord'
28
29
  end
@@ -0,0 +1,109 @@
1
+ require 'geo/coord'
2
+
3
+ module IIIF
4
+ module V3
5
+ module Presentation
6
+ class NavPlace < IIIF::V3::AbstractResource
7
+ Rect = Struct.new(:coord1, :coord2)
8
+
9
+ COORD_REGEX = /(?<hemisphere>[NSEW]) (?<degrees>\d+)[°⁰*] ?(?<minutes>\d+)?[ʹ']? ?(?<seconds>\d+)?[ʺ"]?/
10
+
11
+ def initialize(coordinate_texts:, base_uri:)
12
+ @coordinate_texts = coordinate_texts
13
+ @base_uri = base_uri
14
+ end
15
+
16
+ # @return [Boolean] indicates if coordinate_texts passed in are valid
17
+ def valid?
18
+ !(coordinates.nil? || coordinates.empty?)
19
+ end
20
+
21
+ def build
22
+ raise ArgumentError.new('invalid coordinates') unless valid?
23
+
24
+ {
25
+ id: "#{base_uri}/feature-collection/1",
26
+ type: 'FeatureCollection',
27
+ features: features
28
+ }
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :coordinate_texts, :base_uri
34
+
35
+ def coordinates
36
+ @coordinates ||= coordinate_texts.map do |coordinate_text|
37
+ coordinate_parts = coordinate_text.split(%r{ ?--|/})
38
+ case coordinate_parts.length
39
+ when 2
40
+ coord_for(coordinate_parts[0], coordinate_parts[1])
41
+ when 4
42
+ rect_for(coordinate_parts)
43
+ end
44
+ end.compact
45
+ end
46
+
47
+ def coord_for(long_str, lat_str)
48
+ long_matcher = long_str.match(COORD_REGEX)
49
+ lat_matcher = lat_str.match(COORD_REGEX)
50
+ return unless long_matcher && lat_matcher
51
+
52
+ Geo::Coord.new(latd: lat_matcher[:degrees], latm: lat_matcher[:minutes], lats: lat_matcher[:seconds], lath: lat_matcher[:hemisphere],
53
+ lngd: long_matcher[:degrees], lngm: long_matcher[:minutes], lngs: long_matcher[:seconds], lngh: long_matcher[:hemisphere])
54
+ end
55
+
56
+ def rect_for(coordinate_parts)
57
+ coord1 = coord_for(coordinate_parts[0], coordinate_parts[2])
58
+ coord2 = coord_for(coordinate_parts[1], coordinate_parts[3])
59
+ return if coord1.nil? || coord2.nil?
60
+
61
+ Rect.new(coord1, coord2)
62
+ end
63
+
64
+ def features
65
+ coordinates.map.with_index(1) do |coordinate, index|
66
+ {
67
+ id: "#{base_uri}/iiif/feature/#{index}",
68
+ type: 'Feature',
69
+ properties: {},
70
+ geometry: coordinate.is_a?(Rect) ? polygon_geometry(coordinate) : point_geometry(coordinate)
71
+ }
72
+ end
73
+ end
74
+
75
+ def point_geometry(coord)
76
+ {
77
+ type: 'Point',
78
+ coordinates: [format(coord.lng), format(coord.lat)]
79
+ }
80
+ end
81
+
82
+ def polygon_geometry(rect)
83
+ {
84
+ type: 'Polygon',
85
+ coordinates: [
86
+ [
87
+ [format(rect.coord1.lng), format(rect.coord1.lat)],
88
+ [format(rect.coord2.lng), format(rect.coord1.lat)],
89
+ [format(rect.coord2.lng), format(rect.coord2.lat)],
90
+ [format(rect.coord1.lng), format(rect.coord2.lat)],
91
+ [format(rect.coord1.lng), format(rect.coord1.lat)]
92
+ ]
93
+ ]
94
+ }
95
+ end
96
+
97
+ # @param [BigDecimal] coordinate value from geocoord gem
98
+ # @return [String] string formatted with max 6 digits after the decimal point
99
+ # The to_f ensures removal of scientific notation of BigDecimal before converting to a string.
100
+ # examples:
101
+ # input value is BigDecimal("-23.9") or "0.239e2", output value is "-23.9" as string
102
+ # input value is BigDecimal("23.9424213434") or "0.239424213434e2", output value is "23.942421" as string
103
+ def format(decimal)
104
+ decimal.truncate(6).to_f.to_s
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -11,6 +11,7 @@ require_relative '../ordered_hash'
11
11
  choice
12
12
  collection
13
13
  manifest
14
+ nav_place
14
15
  resource
15
16
  image_resource
16
17
  sequence
@@ -0,0 +1,80 @@
1
+ describe IIIF::V3::Presentation::NavPlace do
2
+ let(:subject) { described_class.new(coordinate_texts: coordinate_texts, base_uri: base_uri) }
3
+ let(:base_uri) { "https://purl.stanford.edu" }
4
+ let(:invalid_coordinates) { ["bogus", "stuff", "is", "here"] }
5
+ let(:valid_coordinates) do
6
+ ["W 23°54'00\"--E 53°36'00\"/N 71°19'00\"--N 33°30'00\"",
7
+ 'E 103°48ʹ/S 3°46ʹ).',
8
+ 'X 103°48ʹ/Y 3°46ʹ).',
9
+ 'In decimal degrees: (E 138.0--W 074.0/N 073.0--N 041.2).']
10
+ end
11
+ let(:nav_place) do
12
+ { id: 'https://purl.stanford.edu/feature-collection/1',
13
+ type: 'FeatureCollection',
14
+ features: [{ id: 'https://purl.stanford.edu/iiif/feature/1',
15
+ type: 'Feature',
16
+ properties: {},
17
+ geometry: { type: 'Polygon',
18
+ coordinates: [[['-23.9', '71.316666'],
19
+ ['53.6', '71.316666'],
20
+ ['53.6', '33.5'],
21
+ ['-23.9', '33.5'],
22
+ ['-23.9', '71.316666']]] } },
23
+ { id: 'https://purl.stanford.edu/iiif/feature/2',
24
+ type: 'Feature',
25
+ properties: {},
26
+ geometry: { type: 'Point', coordinates: ['103.8', '-3.766666'] } }] }
27
+ end
28
+
29
+ describe '#build' do
30
+ context 'when coordinates are valid' do
31
+ let(:coordinate_texts) { valid_coordinates }
32
+
33
+ it 'returns navPlace' do
34
+ expect(subject.build).to eq nav_place
35
+ end
36
+ end
37
+
38
+ context 'when coordinates are not present' do
39
+ let(:coordinate_texts) { [] }
40
+
41
+ it 'raises ArgumentError' do
42
+ expect { subject.build }.to raise_error(ArgumentError)
43
+ end
44
+ end
45
+
46
+ context 'when coordinates are invalid' do
47
+ let(:coordinate_texts) { invalid_coordinates }
48
+
49
+ it 'raises ArgumentError' do
50
+ expect { subject.build }.to raise_error(ArgumentError)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#valid' do
56
+ context 'when coordinates are valid' do
57
+ let(:coordinate_texts) { valid_coordinates }
58
+
59
+ it 'returns true' do
60
+ expect(subject.valid?).to be true
61
+ end
62
+ end
63
+
64
+ context 'when coordinates are not present' do
65
+ let(:coordinate_texts) { [] }
66
+
67
+ it 'returns false' do
68
+ expect(subject.valid?).to be false
69
+ end
70
+ end
71
+
72
+ context 'when coordinates are invalid' do
73
+ let(:coordinate_texts) { invalid_coordinates }
74
+
75
+ it 'returns false' do
76
+ expect(subject.valid?).to be false
77
+ end
78
+ end
79
+ end
80
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iiif-presentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Stroop
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-25 00:00:00.000000000 Z
11
+ date: 2023-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '2.7'
153
+ - !ruby/object:Gem::Dependency
154
+ name: geo_coord
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  description: API for working with IIIF Presentation manifests.
154
168
  email:
155
169
  - jpstroop@gmail.com
@@ -192,6 +206,7 @@ files:
192
206
  - lib/iiif/v3/presentation/collection.rb
193
207
  - lib/iiif/v3/presentation/image_resource.rb
194
208
  - lib/iiif/v3/presentation/manifest.rb
209
+ - lib/iiif/v3/presentation/nav_place.rb
195
210
  - lib/iiif/v3/presentation/range.rb
196
211
  - lib/iiif/v3/presentation/resource.rb
197
212
  - lib/iiif/v3/presentation/sequence.rb
@@ -238,6 +253,7 @@ files:
238
253
  - spec/unit/iiif/v3/presentation/collection_spec.rb
239
254
  - spec/unit/iiif/v3/presentation/image_resource_spec.rb
240
255
  - spec/unit/iiif/v3/presentation/manifest_spec.rb
256
+ - spec/unit/iiif/v3/presentation/nav_place_spec.rb
241
257
  - spec/unit/iiif/v3/presentation/range_spec.rb
242
258
  - spec/unit/iiif/v3/presentation/resource_spec.rb
243
259
  - spec/unit/iiif/v3/presentation/sequence_spec.rb
@@ -269,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
269
285
  - !ruby/object:Gem::Version
270
286
  version: '0'
271
287
  requirements: []
272
- rubygems_version: 3.4.20
288
+ rubygems_version: 3.4.21
273
289
  signing_key:
274
290
  specification_version: 4
275
291
  summary: API for working with IIIF Presentation manifests.
@@ -316,6 +332,7 @@ test_files:
316
332
  - spec/unit/iiif/v3/presentation/collection_spec.rb
317
333
  - spec/unit/iiif/v3/presentation/image_resource_spec.rb
318
334
  - spec/unit/iiif/v3/presentation/manifest_spec.rb
335
+ - spec/unit/iiif/v3/presentation/nav_place_spec.rb
319
336
  - spec/unit/iiif/v3/presentation/range_spec.rb
320
337
  - spec/unit/iiif/v3/presentation/resource_spec.rb
321
338
  - spec/unit/iiif/v3/presentation/sequence_spec.rb