iiif-presentation 1.2.0 → 1.3.0

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