iiif-presentation 1.4.2 → 1.5.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 +4 -4
- data/.github/workflows/ruby.yml +8 -1
- data/.standard.yml +1 -0
- data/Gemfile +5 -5
- data/README.md +1 -1
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/bin/standardrb +16 -0
- data/iiif-presentation.gemspec +23 -23
- data/lib/iiif/hash_behaviours.rb +29 -33
- data/lib/iiif/ordered_hash.rb +33 -33
- data/lib/iiif/presentation/abstract_resource.rb +17 -22
- data/lib/iiif/presentation/annotation.rb +9 -11
- data/lib/iiif/presentation/annotation_list.rb +8 -10
- data/lib/iiif/presentation/canvas.rb +11 -13
- data/lib/iiif/presentation/collection.rb +7 -9
- data/lib/iiif/presentation/image_resource.rb +33 -35
- data/lib/iiif/presentation/layer.rb +8 -10
- data/lib/iiif/presentation/manifest.rb +9 -12
- data/lib/iiif/presentation/range.rb +8 -10
- data/lib/iiif/presentation/resource.rb +5 -7
- data/lib/iiif/presentation/sequence.rb +8 -11
- data/lib/iiif/presentation/service.rb +1 -2
- data/lib/iiif/presentation.rb +7 -7
- data/lib/iiif/service.rb +121 -102
- data/lib/iiif/v3/abstract_resource.rb +127 -130
- data/lib/iiif/v3/presentation/annotation.rb +20 -21
- data/lib/iiif/v3/presentation/annotation_collection.rb +7 -9
- data/lib/iiif/v3/presentation/annotation_page.rb +14 -15
- data/lib/iiif/v3/presentation/canvas.rb +21 -22
- data/lib/iiif/v3/presentation/choice.rb +14 -15
- data/lib/iiif/v3/presentation/collection.rb +7 -8
- data/lib/iiif/v3/presentation/image_resource.rb +21 -22
- data/lib/iiif/v3/presentation/manifest.rb +18 -19
- data/lib/iiif/v3/presentation/nav_place.rb +125 -29
- data/lib/iiif/v3/presentation/range.rb +10 -10
- data/lib/iiif/v3/presentation/resource.rb +4 -5
- data/lib/iiif/v3/presentation/sequence.rb +10 -11
- data/lib/iiif/v3/presentation/service.rb +12 -13
- data/lib/iiif/v3/presentation.rb +8 -8
- data/spec/integration/iiif/presentation/image_resource_spec.rb +54 -58
- data/spec/integration/iiif/service_spec.rb +76 -77
- data/spec/integration/iiif/v3/abstract_resource_spec.rb +72 -73
- data/spec/integration/iiif/v3/presentation/image_resource_spec.rb +53 -56
- data/spec/spec_helper.rb +56 -64
- data/spec/unit/iiif/hash_behaviours_spec.rb +384 -387
- data/spec/unit/iiif/ordered_hash_spec.rb +86 -91
- data/spec/unit/iiif/presentation/abstract_resource_spec.rb +64 -70
- data/spec/unit/iiif/presentation/annotation_list_spec.rb +1 -3
- data/spec/unit/iiif/presentation/annotation_spec.rb +1 -3
- data/spec/unit/iiif/presentation/canvas_spec.rb +11 -14
- data/spec/unit/iiif/presentation/collection_spec.rb +26 -33
- data/spec/unit/iiif/presentation/image_resource_spec.rb +4 -6
- data/spec/unit/iiif/presentation/layer_spec.rb +16 -20
- data/spec/unit/iiif/presentation/manifest_spec.rb +45 -48
- data/spec/unit/iiif/presentation/range_spec.rb +18 -22
- data/spec/unit/iiif/presentation/resource_spec.rb +3 -6
- data/spec/unit/iiif/presentation/sequence_spec.rb +58 -63
- data/spec/unit/iiif/presentation/shared_examples/abstract_resource_only_keys.rb +4 -10
- data/spec/unit/iiif/presentation/shared_examples/any_type_keys.rb +5 -9
- data/spec/unit/iiif/presentation/shared_examples/array_only_keys.rb +9 -17
- data/spec/unit/iiif/presentation/shared_examples/int_only_keys.rb +6 -14
- data/spec/unit/iiif/presentation/shared_examples/string_only_keys.rb +6 -13
- data/spec/unit/iiif/service_spec.rb +8 -13
- data/spec/unit/iiif/v3/abstract_resource_define_methods_for_spec.rb +39 -37
- data/spec/unit/iiif/v3/abstract_resource_spec.rb +124 -131
- data/spec/unit/iiif/v3/presentation/annotation_collection_spec.rb +14 -17
- data/spec/unit/iiif/v3/presentation/annotation_page_spec.rb +71 -69
- data/spec/unit/iiif/v3/presentation/annotation_spec.rb +249 -233
- data/spec/unit/iiif/v3/presentation/canvas_spec.rb +211 -193
- data/spec/unit/iiif/v3/presentation/choice_spec.rb +81 -77
- data/spec/unit/iiif/v3/presentation/collection_spec.rb +24 -29
- data/spec/unit/iiif/v3/presentation/image_resource_spec.rb +70 -71
- data/spec/unit/iiif/v3/presentation/manifest_spec.rb +248 -235
- data/spec/unit/iiif/v3/presentation/nav_place_spec.rb +249 -49
- data/spec/unit/iiif/v3/presentation/range_spec.rb +28 -29
- data/spec/unit/iiif/v3/presentation/resource_spec.rb +83 -82
- data/spec/unit/iiif/v3/presentation/sequence_spec.rb +125 -119
- data/spec/unit/iiif/v3/presentation/service_spec.rb +123 -124
- data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +4 -8
- data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +5 -7
- data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +9 -13
- data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +9 -13
- data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +6 -10
- data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +6 -10
- data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +6 -10
- data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +9 -13
- metadata +26 -65
|
@@ -2,93 +2,293 @@ describe IIIF::V3::Presentation::NavPlace do
|
|
|
2
2
|
let(:subject) { described_class.new(coordinate_texts: coordinate_texts, base_uri: base_uri) }
|
|
3
3
|
let(:base_uri) { "https://purl.stanford.edu" }
|
|
4
4
|
let(:invalid_coordinates) { ["bogus", "stuff", "is", "here"] }
|
|
5
|
+
let(:out_of_range_coordinates) { ["W 008°00′00″--W 004°00′00″/N 059°00′00″--N 543°00′00″"] }
|
|
6
|
+
let(:wrong_axis_hemisphere_coordinates) { ["(W 117°57'--W 117°39'/E 34°50'--E 32°48')."] }
|
|
5
7
|
let(:valid_coordinates) do
|
|
6
8
|
["W 23°54'00\"--E 53°36'00\"/N 71°19'00\"--N 33°30'00\"",
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
]
|
|
9
|
+
"E 103°48ʹ/S 3°46ʹ).",
|
|
10
|
+
"X 103°48ʹ/Y 3°46ʹ).",
|
|
11
|
+
"In decimal degrees: (E 138.0--W 074.0/N 073.0--N 041.2).",
|
|
12
|
+
"23°54′00″W -- 53°36′00″E / 71°19′00″N -- 33°30′00″N"]
|
|
12
13
|
end
|
|
13
14
|
let(:nav_place) do
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
15
|
+
{id: "https://purl.stanford.edu/feature-collection/1",
|
|
16
|
+
type: "FeatureCollection",
|
|
17
|
+
features: [{id: "https://purl.stanford.edu/iiif/feature/1",
|
|
18
|
+
type: "Feature",
|
|
19
|
+
properties: {},
|
|
20
|
+
geometry: {type: "Polygon",
|
|
21
|
+
coordinates: [[[-23.9, 33.5],
|
|
22
|
+
[53.6, 33.5],
|
|
23
|
+
[53.6, 71.316667],
|
|
24
|
+
[-23.9, 71.316667],
|
|
25
|
+
[-23.9, 33.5]]]}},
|
|
26
|
+
{id: "https://purl.stanford.edu/iiif/feature/2",
|
|
27
|
+
type: "Feature",
|
|
28
|
+
properties: {},
|
|
29
|
+
geometry: {type: "Point", coordinates: [103.8, -3.766667]}},
|
|
30
|
+
{id: "https://purl.stanford.edu/iiif/feature/3",
|
|
31
|
+
type: "Feature",
|
|
32
|
+
properties: {},
|
|
33
|
+
geometry: {coordinates: [103.8, 3.766667], type: "Point"}},
|
|
34
|
+
{id: "https://purl.stanford.edu/iiif/feature/4",
|
|
35
|
+
type: "Feature",
|
|
36
|
+
properties: {},
|
|
37
|
+
geometry: {type: "MultiPolygon",
|
|
38
|
+
coordinates: [[[[138.0, 41.2],
|
|
39
|
+
[180.0, 41.2],
|
|
40
|
+
[180.0, 73.0],
|
|
41
|
+
[138.0, 73.0],
|
|
42
|
+
[138.0, 41.2]]],
|
|
43
|
+
[[[-180.0, 41.2],
|
|
44
|
+
[-74.0, 41.2],
|
|
45
|
+
[-74.0, 73.0],
|
|
46
|
+
[-180.0, 73.0],
|
|
47
|
+
[-180.0, 41.2]]]]}},
|
|
48
|
+
{id: "https://purl.stanford.edu/iiif/feature/5",
|
|
49
|
+
type: "Feature",
|
|
50
|
+
properties: {},
|
|
51
|
+
geometry: {type: "Polygon",
|
|
52
|
+
coordinates: [[[-23.9, 33.5],
|
|
53
|
+
[53.6, 33.5],
|
|
54
|
+
[53.6, 71.316667],
|
|
55
|
+
[-23.9, 71.316667],
|
|
56
|
+
[-23.9, 33.5]]]}}]}
|
|
57
|
+
end
|
|
58
|
+
let(:nav_place_without_ids) do
|
|
59
|
+
without_id = ->(resource) { resource.except(:id) }
|
|
60
|
+
without_id.call(nav_place).merge(features: nav_place[:features].map(&without_id))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe "#initialize" do
|
|
64
|
+
let(:coordinate_texts) { valid_coordinates }
|
|
65
|
+
|
|
66
|
+
["", " ", "/path", "example.com", "ftp://my.server"].each do |value|
|
|
67
|
+
context "when base_uri is #{value.inspect}" do
|
|
68
|
+
let(:base_uri) { value }
|
|
69
|
+
|
|
70
|
+
it "raises ArgumentError" do
|
|
71
|
+
expect { subject }.to raise_error(ArgumentError, /base_uri must be an absolute HTTP\(S\) URI/)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
43
75
|
end
|
|
44
76
|
|
|
45
|
-
describe
|
|
46
|
-
context
|
|
77
|
+
describe "#build" do
|
|
78
|
+
context "when coordinates are valid" do
|
|
47
79
|
let(:coordinate_texts) { valid_coordinates }
|
|
48
80
|
|
|
49
|
-
|
|
50
|
-
|
|
81
|
+
# Compare as JSON: Ruby's == treats a BigDecimal as equal to a Float, but
|
|
82
|
+
# a BigDecimal serializes as a string, which GeoJSON does not allow.
|
|
83
|
+
it "returns navPlace" do
|
|
84
|
+
expect(JSON.parse(subject.build.to_json)).to eq JSON.parse(nav_place.to_json)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context "without a base_uri" do
|
|
88
|
+
let(:subject) { described_class.new(coordinate_texts: coordinate_texts) }
|
|
89
|
+
|
|
90
|
+
it "leaves out the ids" do
|
|
91
|
+
expect(JSON.parse(subject.build.to_json)).to eq JSON.parse(nav_place_without_ids.to_json)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context "when the north and south edges are given in the reverse order" do
|
|
97
|
+
let(:coordinate_texts) { ["W 10°00ʹ--E 20°00ʹ/S 5°00ʹ--N 5°00ʹ"] }
|
|
98
|
+
|
|
99
|
+
it "still runs the ring counterclockwise" do
|
|
100
|
+
expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
|
|
101
|
+
"type" => "Polygon",
|
|
102
|
+
"coordinates" => [[[-10.0, -5.0], [20.0, -5.0], [20.0, 5.0], [-10.0, 5.0], [-10.0, -5.0]]]
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
context "when a box crosses the antimeridian" do
|
|
108
|
+
let(:coordinate_texts) { ["E 170°00ʹ--W 170°00ʹ/N 10°00ʹ--S 10°00ʹ"] }
|
|
109
|
+
|
|
110
|
+
it "splits it into a MultiPolygon, per the spec" do
|
|
111
|
+
expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
|
|
112
|
+
"type" => "MultiPolygon",
|
|
113
|
+
"coordinates" => [
|
|
114
|
+
[[[170.0, -10.0], [180.0, -10.0], [180.0, 10.0], [170.0, 10.0], [170.0, -10.0]]],
|
|
115
|
+
[[[-180.0, -10.0], [-170.0, -10.0], [-170.0, 10.0], [-180.0, 10.0], [-180.0, -10.0]]]
|
|
116
|
+
]
|
|
117
|
+
)
|
|
51
118
|
end
|
|
52
119
|
end
|
|
53
120
|
|
|
54
|
-
context
|
|
121
|
+
context "when a box has an edge on the antimeridian" do
|
|
122
|
+
let(:coordinate_texts) { ["E 170°00ʹ--W 180°00ʹ/N 10°00ʹ--S 10°00ʹ", "E 180°00ʹ--W 170°00ʹ/N 10°00ʹ--S 10°00ʹ"] }
|
|
123
|
+
|
|
124
|
+
it "makes bounds that do not require splitting" do
|
|
125
|
+
geometries = JSON.parse(subject.build.to_json)["features"].map { |feature| feature["geometry"] }
|
|
126
|
+
expect(geometries).to eq [
|
|
127
|
+
{"type" => "Polygon", "coordinates" => [[[170.0, -10.0], [180.0, -10.0], [180.0, 10.0], [170.0, 10.0], [170.0, -10.0]]]},
|
|
128
|
+
{"type" => "Polygon", "coordinates" => [[[-180.0, -10.0], [-170.0, -10.0], [-170.0, 10.0], [-180.0, 10.0], [-180.0, -10.0]]]}
|
|
129
|
+
]
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
context "with decimal degrees" do
|
|
134
|
+
let(:coordinate_texts) { ["E 138.5°/N 35.25°"] }
|
|
135
|
+
|
|
136
|
+
it "reads the whole number" do
|
|
137
|
+
expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
|
|
138
|
+
"type" => "Point", "coordinates" => [138.5, 35.25]
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context "with decimal degrees and minutes and seconds" do
|
|
144
|
+
let(:coordinate_texts) { ["W 122°15.5ʹ/N 37°26ʹ30.5″"] }
|
|
145
|
+
|
|
146
|
+
it "keeps them" do
|
|
147
|
+
expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
|
|
148
|
+
"type" => "Point", "coordinates" => [-122.258333, 37.441806]
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
context "when coordinates are not present" do
|
|
55
154
|
let(:coordinate_texts) { [] }
|
|
56
155
|
|
|
57
|
-
it
|
|
156
|
+
it "raises ArgumentError" do
|
|
58
157
|
expect { subject.build }.to raise_error(ArgumentError)
|
|
59
158
|
end
|
|
60
159
|
end
|
|
61
160
|
|
|
62
|
-
context
|
|
161
|
+
context "when coordinates are invalid" do
|
|
63
162
|
let(:coordinate_texts) { invalid_coordinates }
|
|
64
163
|
|
|
65
|
-
it
|
|
164
|
+
it "raises ArgumentError" do
|
|
66
165
|
expect { subject.build }.to raise_error(ArgumentError)
|
|
67
166
|
end
|
|
68
167
|
end
|
|
168
|
+
|
|
169
|
+
context "when coordinates are out of range" do
|
|
170
|
+
let(:coordinate_texts) { out_of_range_coordinates }
|
|
171
|
+
|
|
172
|
+
it "raises ArgumentError" do
|
|
173
|
+
expect { subject.build }.to raise_error(ArgumentError, "invalid coordinates")
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context "when a hemisphere does not belong to its axis" do
|
|
178
|
+
let(:coordinate_texts) { wrong_axis_hemisphere_coordinates }
|
|
179
|
+
|
|
180
|
+
it "raises ArgumentError" do
|
|
181
|
+
expect { subject.build }.to raise_error(ArgumentError, "invalid coordinates")
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context "when only some coordinates are unusable" do
|
|
186
|
+
let(:coordinate_texts) { out_of_range_coordinates + valid_coordinates }
|
|
187
|
+
|
|
188
|
+
it "skips them and builds the rest" do
|
|
189
|
+
expect(subject.build).to eq nav_place
|
|
190
|
+
end
|
|
191
|
+
end
|
|
69
192
|
end
|
|
70
193
|
|
|
71
|
-
describe
|
|
72
|
-
context
|
|
194
|
+
describe "#valid" do
|
|
195
|
+
context "when coordinates are valid" do
|
|
73
196
|
let(:coordinate_texts) { valid_coordinates }
|
|
74
197
|
|
|
75
|
-
it
|
|
198
|
+
it "returns true" do
|
|
76
199
|
expect(subject.valid?).to be true
|
|
77
200
|
end
|
|
78
201
|
end
|
|
79
202
|
|
|
80
|
-
context
|
|
203
|
+
context "when coordinates are not present" do
|
|
81
204
|
let(:coordinate_texts) { [] }
|
|
82
205
|
|
|
83
|
-
it
|
|
206
|
+
it "returns false" do
|
|
84
207
|
expect(subject.valid?).to be false
|
|
85
208
|
end
|
|
86
209
|
end
|
|
87
210
|
|
|
88
|
-
context
|
|
211
|
+
context "when coordinates are invalid" do
|
|
89
212
|
let(:coordinate_texts) { invalid_coordinates }
|
|
90
213
|
|
|
91
|
-
it
|
|
214
|
+
it "returns false" do
|
|
215
|
+
expect(subject.valid?).to be false
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
context "when coordinates are out of range" do
|
|
220
|
+
let(:coordinate_texts) { out_of_range_coordinates }
|
|
221
|
+
|
|
222
|
+
it "returns false instead of raising" do
|
|
223
|
+
expect(subject.valid?).to be false
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
context "when a hemisphere does not belong to its axis" do
|
|
228
|
+
let(:coordinate_texts) { wrong_axis_hemisphere_coordinates }
|
|
229
|
+
|
|
230
|
+
it "returns false instead of raising" do
|
|
231
|
+
expect(subject.valid?).to be false
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
describe "WKT coordinate texts" do
|
|
237
|
+
let(:geometries) { JSON.parse(subject.build.to_json)["features"].map { |feature| feature["geometry"] } }
|
|
238
|
+
|
|
239
|
+
context "with a point, a box, and a box split at the antimeridian" do
|
|
240
|
+
let(:coordinate_texts) do
|
|
241
|
+
["POINT(-118.243700 34.052200)",
|
|
242
|
+
"POLYGON((-23.900000 33.500000, 53.600000 33.500000, 53.600000 71.316667, -23.900000 71.316667, -23.900000 33.500000))",
|
|
243
|
+
"MULTIPOLYGON(((170.000000 -10.000000, 180.000000 -10.000000, 180.000000 10.000000, 170.000000 10.000000, 170.000000 -10.000000)), " \
|
|
244
|
+
"((-180.000000 -10.000000, -170.000000 -10.000000, -170.000000 10.000000, -180.000000 10.000000, -180.000000 -10.000000)))"]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "reads each as its GeoJSON geometry" do
|
|
248
|
+
expect(geometries).to eq [
|
|
249
|
+
{"type" => "Point", "coordinates" => [-118.2437, 34.0522]},
|
|
250
|
+
JSON.parse(nav_place[:features][0][:geometry].to_json),
|
|
251
|
+
{"type" => "MultiPolygon",
|
|
252
|
+
"coordinates" => [
|
|
253
|
+
[[[170.0, -10.0], [180.0, -10.0], [180.0, 10.0], [170.0, 10.0], [170.0, -10.0]]],
|
|
254
|
+
[[[-180.0, -10.0], [-170.0, -10.0], [-170.0, 10.0], [-180.0, 10.0], [-180.0, -10.0]]]
|
|
255
|
+
]}
|
|
256
|
+
]
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
context "when rings run the other way" do
|
|
261
|
+
let(:coordinate_texts) { ["polygon ((0 0, 0 10, 10 10, 10 0, 0 0), (2 2, 4 2, 4 4, 2 4, 2 2))"] }
|
|
262
|
+
|
|
263
|
+
it "turns the exterior counterclockwise and holes clockwise" do
|
|
264
|
+
expect(geometries).to eq [
|
|
265
|
+
{"type" => "Polygon",
|
|
266
|
+
"coordinates" => [
|
|
267
|
+
[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]],
|
|
268
|
+
[[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0], [2.0, 2.0]]
|
|
269
|
+
]}
|
|
270
|
+
]
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
context "alongside other coordinate texts" do
|
|
275
|
+
let(:coordinate_texts) { ["E 103°48ʹ/S 3°46ʹ).", "POINT(1.5 2.5)"] }
|
|
276
|
+
|
|
277
|
+
it "keeps their order" do
|
|
278
|
+
expect(geometries).to eq [
|
|
279
|
+
{"type" => "Point", "coordinates" => [103.8, -3.766667]},
|
|
280
|
+
{"type" => "Point", "coordinates" => [1.5, 2.5]}
|
|
281
|
+
]
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
context "when the WKT cannot be used" do
|
|
286
|
+
let(:coordinate_texts) do
|
|
287
|
+
["POINT(200 0)", "POINT(1 2 3)", "POINT EMPTY", "POLYGON((0 0, 1 1))", "POLYGON((0 0, 1 0, 1 1, 0 1, 0 2))",
|
|
288
|
+
"POLYGON((0 0, 1 0, 1 1, 0 0", "MULTIPOLYGON()", "LINESTRING(0 0, 1 1)"]
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it "skips it" do
|
|
92
292
|
expect(subject.valid?).to be false
|
|
93
293
|
end
|
|
94
294
|
end
|
|
@@ -1,71 +1,70 @@
|
|
|
1
1
|
describe IIIF::V3::Presentation::Range do
|
|
2
|
-
|
|
3
2
|
let(:fixed_values) do
|
|
4
3
|
{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
"id" => "http://www.example.org/iiif/book1/range/r1",
|
|
5
|
+
"type" => "Range",
|
|
6
|
+
"label" => "Introduction",
|
|
7
|
+
"members" => [
|
|
9
8
|
{
|
|
10
|
-
"id" =>
|
|
9
|
+
"id" => "http://www.example.org/iiif/book1/range/r1-1",
|
|
11
10
|
"type" => "Range"
|
|
12
11
|
},
|
|
13
12
|
{
|
|
14
|
-
"id" =>
|
|
13
|
+
"id" => "http://www.example.org/iiif/book1/range/r1-2",
|
|
15
14
|
"type" => "Range"
|
|
16
15
|
},
|
|
17
16
|
{
|
|
18
|
-
"id" =>
|
|
17
|
+
"id" => "http://www.example.org/iiif/book1/canvas/p1",
|
|
19
18
|
"type" => "Canvas"
|
|
20
19
|
},
|
|
21
20
|
{
|
|
22
|
-
"id" =>
|
|
21
|
+
"id" => "http://www.example.org/iiif/book1/canvas/p2",
|
|
23
22
|
"type" => "Canvas"
|
|
24
23
|
},
|
|
25
24
|
{
|
|
26
|
-
"id" =>
|
|
25
|
+
"id" => "http://www.example.org/iiif/book1/canvas/p3#xywh=0,0,750,300",
|
|
27
26
|
"type" => "Canvas"
|
|
28
27
|
}
|
|
29
28
|
]
|
|
30
29
|
}
|
|
31
30
|
end
|
|
32
31
|
|
|
33
|
-
describe
|
|
34
|
-
it
|
|
35
|
-
expect(subject[
|
|
32
|
+
describe "#initialize" do
|
|
33
|
+
it "sets type to Range by default" do
|
|
34
|
+
expect(subject["type"]).to eq "Range"
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
describe "#{described_class}.define_methods_for_array_only_keys" do
|
|
40
|
-
it_behaves_like
|
|
39
|
+
it_behaves_like "it has the appropriate methods for array-only keys v3"
|
|
41
40
|
end
|
|
42
41
|
|
|
43
42
|
describe "#{described_class}.define_methods_for_string_only_keys" do
|
|
44
|
-
it_behaves_like
|
|
43
|
+
it_behaves_like "it has the appropriate methods for string-only keys v3"
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
describe "#{described_class}.define_methods_for_any_type_keys" do
|
|
48
|
-
it_behaves_like
|
|
47
|
+
it_behaves_like "it has the appropriate methods for any-type keys v3"
|
|
49
48
|
end
|
|
50
49
|
|
|
51
|
-
describe
|
|
50
|
+
describe "#validate" do
|
|
52
51
|
let(:bad_val_msg) do
|
|
53
|
-
"All entries in the (items or canvases) array must be one of #{IIIF::V3::Presentation::Range::VALID_ITEM_TYPES.join(
|
|
52
|
+
"All entries in the (items or canvases) array must be one of #{IIIF::V3::Presentation::Range::VALID_ITEM_TYPES.join(", ")}"
|
|
54
53
|
end
|
|
55
|
-
describe
|
|
56
|
-
it
|
|
57
|
-
subject[
|
|
58
|
-
subject[
|
|
59
|
-
subject[
|
|
60
|
-
|
|
54
|
+
describe "items" do
|
|
55
|
+
it "raises IllegalValueError for items entry that is not valid type" do
|
|
56
|
+
subject["id"] = "test_range"
|
|
57
|
+
subject["label"] = {"en" => ["Test Range"]}
|
|
58
|
+
subject["items"] = [IIIF::V3::Presentation::Range.new("id" => "child_range", "label" => ["Child Label"]),
|
|
59
|
+
IIIF::V3::Presentation::AnnotationPage.new]
|
|
61
60
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
|
|
62
61
|
end
|
|
63
62
|
end
|
|
64
|
-
describe
|
|
65
|
-
it
|
|
66
|
-
subject[
|
|
67
|
-
subject[
|
|
68
|
-
subject[
|
|
63
|
+
describe "canvases" do
|
|
64
|
+
it "raises IllegalValueError for canvases entry that is not valid type" do
|
|
65
|
+
subject["id"] = "test_range"
|
|
66
|
+
subject["label"] = {"en" => ["Test Range"]}
|
|
67
|
+
subject["canvases"] = [IIIF::V3::Presentation::Canvas.new, IIIF::V3::Presentation::AnnotationPage.new]
|
|
69
68
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
|
|
70
69
|
end
|
|
71
70
|
end
|