geo_combine 0.2.0 → 0.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
  SHA1:
3
- metadata.gz: f0afe886cbbf89998cabcdad7c4daa67842f454d
4
- data.tar.gz: bf8ea5de5455e02536043c038bcd96c0eae804c1
3
+ metadata.gz: 74c51ad5fb69544aa07855842e01fc048b40390f
4
+ data.tar.gz: 5e96161fcb7c86ce471ea0d0748a64321dcad021
5
5
  SHA512:
6
- metadata.gz: 8e3155d7e08bd953c81f8ae501a691406e5ab606d66749a6cb7bd30262f7d21094a3db830af11c9ed65000d81cf88b0fcb5f8868b3f878f22f8d8ee701511dec
7
- data.tar.gz: 90c54a422198f1c960550a19dcf993b8083e13cf8d477870485bb196535a90393f5bbd83ed8a94a810905b1b537838e671c4cd406e1aa014acf5a6603254226f
6
+ metadata.gz: 192f2a4cc67d8d8c92a82f92cccb6dae9fa0f820a27d4cbe331e2b43793327148c5279fda201e5505f3d11a7dd857d7bd069cf82bd7ec8a5b036d92a65e1e86a
7
+ data.tar.gz: c9b07f711c7196e11f16f199cb87e636556194093c2b1e5d00b0292a2ec9007f520ee249465cceefabbff5e980f6cf1fb225d61eb666baa7b695924b794a50de
@@ -0,0 +1,71 @@
1
+ module GeoCombine
2
+ class BoundingBox
3
+ attr_reader :west, :south, :east, :north
4
+
5
+ ##
6
+ # @param [String, Integer, Float] west
7
+ # @param [String, Integer, Float] south
8
+ # @param [String, Integer, Float] east
9
+ # @param [String, Integer, Float] north
10
+ def initialize(west:, south:, east:, north:)
11
+ @west = west.to_f
12
+ @south = south.to_f
13
+ @east = east.to_f
14
+ @north = north.to_f
15
+ end
16
+
17
+ ##
18
+ # Returns a bounding box in ENVELOPE syntax
19
+ # @return [String]
20
+ def to_envelope
21
+ "ENVELOPE(#{west}, #{east}, #{north}, #{south})"
22
+ end
23
+
24
+ def valid?
25
+ [south, north].map do |coord|
26
+ next if (-90..90).cover?(coord)
27
+ raise GeoCombine::Exceptions::InvalidGeometry,
28
+ "#{coord} should be in range -90 90"
29
+ end
30
+ [east, west].map do |coord|
31
+ next if (-180..180).cover?(coord)
32
+ raise GeoCombine::Exceptions::InvalidGeometry,
33
+ "#{coord} should be in range -180 180"
34
+ end
35
+ if west > east
36
+ raise GeoCombine::Exceptions::InvalidGeometry,
37
+ "east #{east} should be greater than or equal to west #{west}"
38
+ end
39
+ if south > north
40
+ raise GeoCombine::Exceptions::InvalidGeometry,
41
+ "north #{north} should be greater than or equal to south #{south}"
42
+ end
43
+ true
44
+ end
45
+
46
+ def self.from_envelope(envelope)
47
+ return if envelope.nil?
48
+ envelope = envelope[/.*ENVELOPE\(([^\)]*)/, 1].split(',')
49
+ new(
50
+ west: envelope[0],
51
+ south: envelope[3],
52
+ east: envelope[1],
53
+ north: envelope[2]
54
+ )
55
+ end
56
+
57
+ ##
58
+ # @param [String] spatial w,s,e,n or w s e n
59
+ # @param [String] delimiter "," or " "
60
+ def self.from_string_delimiter(spatial, delimiter: ',')
61
+ return if spatial.nil?
62
+ spatial = spatial.split(delimiter)
63
+ new(
64
+ west: spatial[0],
65
+ south: spatial[1],
66
+ east: spatial[2],
67
+ north: spatial[3]
68
+ )
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,112 @@
1
+ module GeoCombine
2
+ class CkanMetadata
3
+ MAX_STRING_LENGTH = 32765 # Solr limit
4
+
5
+ attr_reader :metadata
6
+ def initialize(metadata)
7
+ @metadata = JSON.parse(metadata)
8
+ end
9
+
10
+ ##
11
+ # Creates and returns a Geoblacklight schema object from this metadata
12
+ # @return [GeoCombine::Geoblacklight]
13
+ def to_geoblacklight
14
+ GeoCombine::Geoblacklight.new(geoblacklight_terms.to_json)
15
+ end
16
+
17
+ ##
18
+ # Builds a Geoblacklight Schema type hash from Esri Open Data portal
19
+ # metadata
20
+ # @return [Hash]
21
+ def geoblacklight_terms
22
+ {
23
+ dc_identifier_s: @metadata['id'],
24
+ dc_title_s: @metadata['title'],
25
+ dc_rights_s: 'Public',
26
+ layer_geom_type_s: 'Not Specified',
27
+ dct_provenance_s: organization['title'],
28
+ dc_description_s: @metadata['notes'].respond_to?(:[]) ? @metadata['notes'][0..MAX_STRING_LENGTH] : nil,
29
+ layer_slug_s: @metadata['name'],
30
+ solr_geom: envelope,
31
+ dc_subject_sm: subjects,
32
+ dct_references_s: external_references.to_json.to_s,
33
+ dc_format_s: downloadable? ? 'ZIP' : nil # TODO: we only allow direct ZIP file downloads
34
+ }.select { |_k, v| !v.nil? }
35
+ end
36
+
37
+ def organization
38
+ @metadata['organization'] || { title: '' }
39
+ end
40
+
41
+ def envelope
42
+ return envelope_from_bbox unless envelope_from_bbox.nil?
43
+ return envelope_from_spatial(',') unless envelope_from_spatial(',').nil?
44
+ return envelope_from_spatial(' ') unless envelope_from_spatial(' ').nil?
45
+ end
46
+
47
+ def envelope_from_bbox
48
+ bbox = GeoCombine::BoundingBox.new(
49
+ west: extras('bbox-west-long'),
50
+ south: extras('bbox-south-lat'),
51
+ east: extras('bbox-east-long'),
52
+ north: extras('bbox-north-lat')
53
+ )
54
+ begin
55
+ return bbox.to_envelope if bbox.valid?
56
+ rescue GeoCombine::Exceptions::InvalidGeometry
57
+ return nil
58
+ end
59
+ end
60
+
61
+ def envelope_from_spatial(delimiter)
62
+ bbox = GeoCombine::BoundingBox.from_string_delimiter(
63
+ extras('spatial'),
64
+ delimiter: delimiter
65
+ )
66
+ begin
67
+ return bbox.to_envelope if bbox.valid?
68
+ rescue GeoCombine::Exceptions::InvalidGeometry
69
+ return nil
70
+ end
71
+ end
72
+
73
+ def subjects
74
+ extras('tags').split(',').map(&:strip)
75
+ end
76
+
77
+ def extras(key)
78
+ if @metadata['extras']
79
+ @metadata['extras'].select { |h| h['key'] == key }.collect { |v| v['value'] }[0] || ''
80
+ else
81
+ ''
82
+ end
83
+ end
84
+
85
+ def external_references
86
+ h = {
87
+ 'http://schema.org/url' => resource_urls('information').first
88
+ }
89
+
90
+ if downloadable?
91
+ h['http://schema.org/downloadUrl'] = resource_urls('download').first
92
+ end
93
+
94
+ h.select { |_k, v| !v.nil? }
95
+ end
96
+
97
+ def downloadable?
98
+ resource_urls('download').first =~ /.*\.zip/im
99
+ end
100
+
101
+ def resources(type)
102
+ return [] if @metadata['resources'].nil?
103
+ @metadata['resources'].select { |resource| resource['resource_locator_function'] == type }
104
+ end
105
+
106
+ def resource_urls(type)
107
+ resources(type).collect do |resource|
108
+ resource['url'] if resource.respond_to?(:[])
109
+ end
110
+ end
111
+ end
112
+ end
@@ -2,5 +2,7 @@ module GeoCombine
2
2
  module Exceptions
3
3
  class InvalidDCTReferences < StandardError
4
4
  end
5
+ class InvalidGeometry < StandardError
6
+ end
5
7
  end
6
8
  end
@@ -45,7 +45,9 @@ module GeoCombine
45
45
  # @return [Boolean]
46
46
  def valid?
47
47
  @schema ||= JSON.parse(open("https://raw.githubusercontent.com/geoblacklight/geoblacklight/#{GEOBLACKLIGHT_VERSION}/schema/geoblacklight-schema.json").read)
48
- JSON::Validator.validate!(@schema, to_json, fragment: '#/properties/layer') && dct_references_validate!
48
+ JSON::Validator.validate!(@schema, to_json, fragment: '#/properties/layer') &&
49
+ dct_references_validate! &&
50
+ spatial_validate!
49
51
  end
50
52
 
51
53
  ##
@@ -62,6 +64,10 @@ module GeoCombine
62
64
  end
63
65
  end
64
66
 
67
+ def spatial_validate!
68
+ GeoCombine::BoundingBox.from_envelope(metadata['solr_geom']).valid?
69
+ end
70
+
65
71
  private
66
72
 
67
73
  ##
@@ -1,3 +1,3 @@
1
1
  module GeoCombine
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/geo_combine.rb CHANGED
@@ -58,12 +58,14 @@ require 'geo_combine/geometry_types'
58
58
 
59
59
  # Require helper mixins
60
60
  require 'geo_combine/formatting'
61
+ require 'geo_combine/bounding_box'
61
62
 
62
63
  # Require additional classes
63
64
  require 'geo_combine/fgdc'
64
65
  require 'geo_combine/geoblacklight'
65
66
  require 'geo_combine/iso19139'
66
67
  require 'geo_combine/esri_open_data'
68
+ require 'geo_combine/ckan_metadata'
67
69
 
68
70
  # Require gem files
69
71
  require 'geo_combine/version'
@@ -0,0 +1,456 @@
1
+ {
2
+ "license_title": null,
3
+ "maintainer": null,
4
+ "relationships_as_object": [
5
+
6
+ ],
7
+ "private": false,
8
+ "maintainer_email": null,
9
+ "num_tags": 0,
10
+ "id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
11
+ "metadata_created": "2015-09-26T20:11:41.272887",
12
+ "metadata_modified": "2016-10-03T21:32:38.295427",
13
+ "author": null,
14
+ "author_email": null,
15
+ "state": "active",
16
+ "version": null,
17
+ "creator_user_id": "47303a9e-1187-4290-85a3-1fc02dc49e4a",
18
+ "type": "dataset",
19
+ "resources": [
20
+ {
21
+ "cache_last_updated": null,
22
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
23
+ "webstore_last_updated": null,
24
+ "id": "76eea1e9-525a-4f70-8f70-8a8164f24446",
25
+ "size": null,
26
+ "state": "active",
27
+ "resource_locator_function": "information",
28
+ "hash": "",
29
+ "description": "Navigate directly to the URL for a descriptive web page with download links.",
30
+ "format": "Originator data format",
31
+ "tracking_summary": {
32
+ "total": 0,
33
+ "recent": 0
34
+ },
35
+ "mimetype_inner": null,
36
+ "url_type": null,
37
+ "resource_locator_protocol": "HTTP",
38
+ "mimetype": null,
39
+ "cache_url": null,
40
+ "name": "Descriptive Information",
41
+ "created": "2016-10-03T17:32:38.603178",
42
+ "url": "https://accession.nodc.noaa.gov/8600315",
43
+ "webstore_url": null,
44
+ "last_modified": null,
45
+ "position": 0,
46
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
47
+ "resource_type": null
48
+ },
49
+ {
50
+ "cache_last_updated": null,
51
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
52
+ "webstore_last_updated": null,
53
+ "id": "95be096e-48e2-4faa-8236-21bb1875bafc",
54
+ "size": null,
55
+ "state": "active",
56
+ "resource_locator_function": "information",
57
+ "hash": "",
58
+ "description": "Navigate directly to the URL for a descriptive web page with download links.",
59
+ "format": "Originator data format",
60
+ "tracking_summary": {
61
+ "total": 0,
62
+ "recent": 0
63
+ },
64
+ "mimetype_inner": null,
65
+ "url_type": null,
66
+ "resource_locator_protocol": "HTTP",
67
+ "mimetype": null,
68
+ "cache_url": null,
69
+ "name": "Descriptive Information",
70
+ "created": "2016-10-03T17:32:38.603199",
71
+ "url": "https://accession.nodc.noaa.gov/oas/8600315",
72
+ "webstore_url": null,
73
+ "last_modified": null,
74
+ "position": 1,
75
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
76
+ "resource_type": null
77
+ },
78
+ {
79
+ "cache_last_updated": null,
80
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
81
+ "webstore_last_updated": null,
82
+ "id": "a4b99005-faa3-462f-8efb-fa82f5faa651",
83
+ "size": null,
84
+ "state": "active",
85
+ "resource_locator_function": "download",
86
+ "hash": "",
87
+ "description": "Navigate directly to the URL for data access and direct download.",
88
+ "format": "Originator data format",
89
+ "tracking_summary": {
90
+ "total": 0,
91
+ "recent": 0
92
+ },
93
+ "mimetype_inner": null,
94
+ "url_type": null,
95
+ "resource_locator_protocol": "HTTP",
96
+ "mimetype": null,
97
+ "cache_url": null,
98
+ "name": "HTTP",
99
+ "created": "2016-10-03T17:32:38.603208",
100
+ "url": "https://accession.nodc.noaa.gov/download/8600315",
101
+ "webstore_url": null,
102
+ "last_modified": null,
103
+ "position": 2,
104
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
105
+ "resource_type": null
106
+ },
107
+ {
108
+ "cache_last_updated": null,
109
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
110
+ "webstore_last_updated": null,
111
+ "id": "60855295-ca99-45a0-bec7-898ecf52ef4a",
112
+ "size": null,
113
+ "state": "active",
114
+ "resource_locator_function": "download",
115
+ "hash": "",
116
+ "description": "These data are available through the File Transfer Protocol (FTP). You may use any FTP client to download these data.",
117
+ "format": "Originator data format",
118
+ "tracking_summary": {
119
+ "total": 0,
120
+ "recent": 0
121
+ },
122
+ "mimetype_inner": null,
123
+ "url_type": null,
124
+ "resource_locator_protocol": "FTP",
125
+ "mimetype": null,
126
+ "cache_url": null,
127
+ "name": "FTP",
128
+ "created": "2016-10-03T17:32:38.603216",
129
+ "url": "ftp://ftp.nodc.noaa.gov/nodc/archive/arc0001/8600315/",
130
+ "webstore_url": null,
131
+ "last_modified": null,
132
+ "position": 3,
133
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
134
+ "resource_type": null
135
+ },
136
+ {
137
+ "cache_last_updated": null,
138
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
139
+ "webstore_last_updated": null,
140
+ "id": "7544a4f9-aa0f-46b6-8e7b-fccb1f52397a",
141
+ "size": null,
142
+ "state": "active",
143
+ "resource_locator_function": "information",
144
+ "hash": "",
145
+ "description": "Main NCEI website providing links to access data and data services.",
146
+ "format": "",
147
+ "tracking_summary": {
148
+ "total": 51,
149
+ "recent": 8
150
+ },
151
+ "mimetype_inner": null,
152
+ "url_type": null,
153
+ "resource_locator_protocol": "HTTP",
154
+ "mimetype": null,
155
+ "cache_url": null,
156
+ "name": "NOAA National Centers for Environmental Information website",
157
+ "created": "2016-10-03T17:32:38.603224",
158
+ "url": "https://www.ncei.noaa.gov/",
159
+ "webstore_url": null,
160
+ "last_modified": null,
161
+ "position": 4,
162
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
163
+ "resource_type": null
164
+ },
165
+ {
166
+ "cache_last_updated": null,
167
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
168
+ "webstore_last_updated": null,
169
+ "id": "4ed6ee47-42cb-46a2-91b9-8d702c229988",
170
+ "size": null,
171
+ "state": "active",
172
+ "resource_locator_function": "information",
173
+ "hash": "",
174
+ "description": "Main NCEI website providing links to access data and data services.",
175
+ "format": "",
176
+ "tracking_summary": {
177
+ "total": 51,
178
+ "recent": 8
179
+ },
180
+ "mimetype_inner": null,
181
+ "url_type": null,
182
+ "resource_locator_protocol": "HTTP",
183
+ "mimetype": null,
184
+ "cache_url": null,
185
+ "name": "NOAA National Centers for Environmental Information website",
186
+ "created": "2016-10-03T17:32:38.603241",
187
+ "url": "https://www.ncei.noaa.gov/",
188
+ "webstore_url": null,
189
+ "last_modified": null,
190
+ "position": 5,
191
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
192
+ "resource_type": null
193
+ },
194
+ {
195
+ "cache_last_updated": null,
196
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
197
+ "webstore_last_updated": null,
198
+ "id": "14f71eee-29ae-4641-8440-c1cac6570cc5",
199
+ "size": null,
200
+ "state": "active",
201
+ "resource_locator_function": "information",
202
+ "hash": "",
203
+ "description": "Overview of the NASA GCMD Keywords.",
204
+ "format": "HTML",
205
+ "tracking_summary": {
206
+ "total": 163,
207
+ "recent": 14
208
+ },
209
+ "mimetype_inner": null,
210
+ "url_type": null,
211
+ "resource_locator_protocol": "HTTP",
212
+ "mimetype": null,
213
+ "cache_url": null,
214
+ "name": "NASA GCMD Keyword Community Page",
215
+ "created": "2016-10-03T17:32:38.603254",
216
+ "url": "http://gcmd.gsfc.nasa.gov/learn/keywords.html",
217
+ "webstore_url": null,
218
+ "last_modified": null,
219
+ "position": 6,
220
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
221
+ "resource_type": null
222
+ },
223
+ {
224
+ "cache_last_updated": null,
225
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
226
+ "webstore_last_updated": null,
227
+ "id": "9d46c3f8-5933-4001-9352-5a0fff917a51",
228
+ "size": null,
229
+ "state": "active",
230
+ "resource_locator_function": "information",
231
+ "hash": "",
232
+ "description": "Overview of the NASA GCMD Keywords.",
233
+ "format": "HTML",
234
+ "tracking_summary": {
235
+ "total": 163,
236
+ "recent": 14
237
+ },
238
+ "mimetype_inner": null,
239
+ "url_type": null,
240
+ "resource_locator_protocol": "HTTP",
241
+ "mimetype": null,
242
+ "cache_url": null,
243
+ "name": "NASA GCMD Keyword Community Page",
244
+ "created": "2016-10-03T17:32:38.603262",
245
+ "url": "http://gcmd.gsfc.nasa.gov/learn/keywords.html",
246
+ "webstore_url": null,
247
+ "last_modified": null,
248
+ "position": 7,
249
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
250
+ "resource_type": null
251
+ },
252
+ {
253
+ "cache_last_updated": null,
254
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
255
+ "webstore_last_updated": null,
256
+ "id": "c9824b30-b5ac-459d-ba50-188403b3b775",
257
+ "size": null,
258
+ "state": "active",
259
+ "resource_locator_function": "information",
260
+ "hash": "",
261
+ "description": "Overview of the NASA GCMD Keywords.",
262
+ "format": "HTML",
263
+ "tracking_summary": {
264
+ "total": 163,
265
+ "recent": 14
266
+ },
267
+ "mimetype_inner": null,
268
+ "url_type": null,
269
+ "resource_locator_protocol": "HTTP",
270
+ "mimetype": null,
271
+ "cache_url": null,
272
+ "name": "NASA GCMD Keyword Community Page",
273
+ "created": "2016-10-03T17:32:38.603269",
274
+ "url": "http://gcmd.gsfc.nasa.gov/learn/keywords.html",
275
+ "webstore_url": null,
276
+ "last_modified": null,
277
+ "position": 8,
278
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
279
+ "resource_type": null
280
+ },
281
+ {
282
+ "cache_last_updated": null,
283
+ "package_id": "0009a4da-24ad-4d86-917b-1d1a1b1a67f3",
284
+ "webstore_last_updated": null,
285
+ "id": "0aee2404-4087-4a09-9c5e-ea4bf08ba6a6",
286
+ "size": null,
287
+ "state": "active",
288
+ "resource_locator_function": "information",
289
+ "hash": "",
290
+ "description": "Overview of the NASA GCMD Keywords.",
291
+ "format": "HTML",
292
+ "tracking_summary": {
293
+ "total": 163,
294
+ "recent": 14
295
+ },
296
+ "mimetype_inner": null,
297
+ "url_type": null,
298
+ "resource_locator_protocol": "HTTP",
299
+ "mimetype": null,
300
+ "cache_url": null,
301
+ "name": "NASA GCMD Keyword Community Page",
302
+ "created": "2016-10-03T17:32:38.603277",
303
+ "url": "http://gcmd.gsfc.nasa.gov/learn/keywords.html",
304
+ "webstore_url": null,
305
+ "last_modified": null,
306
+ "position": 9,
307
+ "revision_id": "48f8ab67-468b-4354-a450-35f2cb8e51a7",
308
+ "resource_type": null
309
+ }
310
+ ],
311
+ "num_resources": 10,
312
+ "tags": [
313
+
314
+ ],
315
+ "tracking_summary": {
316
+ "total": 2,
317
+ "recent": 2
318
+ },
319
+ "groups": [
320
+
321
+ ],
322
+ "license_id": null,
323
+ "relationships_as_subject": [
324
+
325
+ ],
326
+ "organization": {
327
+ "description": "",
328
+ "created": "2013-03-14T03:44:40.434269",
329
+ "title": "National Oceanic and Atmospheric Administration, Department of Commerce",
330
+ "name": "noaa-gov",
331
+ "is_organization": true,
332
+ "state": "active",
333
+ "image_url": "https://fortress.wa.gov/dfw/score/score/images/noaa_logo.png",
334
+ "revision_id": "c6d43080-22e1-405e-a83d-f2a512d9e867",
335
+ "type": "organization",
336
+ "id": "e811f0b4-451f-4896-9e8f-fc6802837819",
337
+ "approval_status": "approved"
338
+ },
339
+ "name": "water-depth-and-other-data-from-thomas-g-thompson-and-other-platforms-from-gulf-of-alaska-and-o",
340
+ "isopen": false,
341
+ "url": null,
342
+ "notes": "",
343
+ "owner_org": "e811f0b4-451f-4896-9e8f-fc6802837819",
344
+ "extras": [
345
+ {
346
+ "key": "bbox-east-long",
347
+ "value": "-105.7"
348
+ },
349
+ {
350
+ "key": "resource-type",
351
+ "value": "dataset"
352
+ },
353
+ {
354
+ "key": "bbox-north-lat",
355
+ "value": "59.2"
356
+ },
357
+ {
358
+ "key": "coupled-resource",
359
+ "value": "[]"
360
+ },
361
+ {
362
+ "key": "guid",
363
+ "value": "gov.noaa.nodc:8600315"
364
+ },
365
+ {
366
+ "key": "spatial_harvester",
367
+ "value": true
368
+ },
369
+ {
370
+ "key": "bbox-south-lat",
371
+ "value": "8.9"
372
+ },
373
+ {
374
+ "key": "spatial-reference-system",
375
+ "value": ""
376
+ },
377
+ {
378
+ "key": "spatial",
379
+ "value": "{\"type\": \"Polygon\", \"coordinates\": [[[-158.2, 8.9], [-105.7, 8.9], [-105.7, 59.2], [-158.2, 59.2], [-158.2, 8.9]]]}"
380
+ },
381
+ {
382
+ "key": "progress",
383
+ "value": "completed"
384
+ },
385
+ {
386
+ "key": "access_constraints",
387
+ "value": "[\"Cite as: University of Washington (2015). Cloud amount/frequency, NITRATE and other data from THOMAS G. THOMPSON in the NW Pacific, Gulf of Alaska and NE Pacific from 1967-02-05 to 1975-10-09 (NCEI Accession 8600315). Version 2.2. NOAA National Centers for Environmental Information. Dataset. [access date]\", \"NOAA and NCEI cannot provide any warranty as to the accuracy, reliability, or completeness of furnished data. Users assume responsibility to determine the usability of these data. The user is responsible for the results of any application of this data for other than its intended purpose.\"]"
388
+ },
389
+ {
390
+ "key": "temporal-extent-begin",
391
+ "value": "1967-02-05"
392
+ },
393
+ {
394
+ "key": "contact-email",
395
+ "value": "NCEI.Info@noaa.gov"
396
+ },
397
+ {
398
+ "key": "bbox-west-long",
399
+ "value": "-158.2"
400
+ },
401
+ {
402
+ "key": "metadata-date",
403
+ "value": "2016-01-07T14:52:07"
404
+ },
405
+ {
406
+ "key": "dataset-reference-date",
407
+ "value": "[{\"type\": \"publication\", \"value\": \"2015-11-30\"}]"
408
+ },
409
+ {
410
+ "key": "frequency-of-update",
411
+ "value": "asNeeded"
412
+ },
413
+ {
414
+ "key": "licence",
415
+ "value": "[\"accessLevel: Public\"]"
416
+ },
417
+ {
418
+ "key": "metadata_type",
419
+ "value": "geospatial"
420
+ },
421
+ {
422
+ "key": "responsible-party",
423
+ "value": "[{\"name\": \"DOC/NOAA/NESDIS/NCEI > National Centers for Environmental Information, NESDIS, NOAA, U.S. Department of Commerce\", \"roles\": [\"pointOfContact\"]}]"
424
+ },
425
+ {
426
+ "key": "temporal-extent-end",
427
+ "value": "1975-10-09"
428
+ },
429
+ {
430
+ "key": "spatial-data-service-type",
431
+ "value": ""
432
+ },
433
+ {
434
+ "key": "metadata-language",
435
+ "value": "eng"
436
+ },
437
+ {
438
+ "key": "tags",
439
+ "value": "ocean temperature, atmosphere, ocean waves, conductivity, temperature, depth, pacific ocean, ocean optics, doc/noaa/nesdis/ncei, ocean, national oceanographic data center, nesdis, noaa, u.s. department of commerce, cloud properties, earth science, oceanography, photometers, density, atmospheric pressure, in situ/laboratory instruments, acoustic velocity, wind speed/wind direction, cloud types, wave period, atmospheric temperature, nitrate, ph, conductivity, attenuation/transmission, national centers for environmental information, nesdis, noaa, u.s. department of commerce, oceans, wave height, ocean acoustics, ocean color, surface winds, silicate, earth remote sensing instruments, passive remote sensing, water temperature, doc/noaa/nesdis/nodc, salinity/density, atmospheric winds, clouds, oxygen, cloud frequency, secchi disks, surface temperature, gulf of alaska, transmissometers, air temperature, salinity, ctd, profilers/sounders, phosphate, secchi depth, north pacific ocean, photon/optical detectors, hydrostatic pressure, ocean chemistry"
440
+ },
441
+ {
442
+ "key": "harvest_object_id",
443
+ "value": "1e5fbc3e-a6ab-489d-be34-5b676f2dd29d"
444
+ },
445
+ {
446
+ "key": "harvest_source_id",
447
+ "value": "2aed8e29-fc5b-4cde-aa66-fb1118fd705e"
448
+ },
449
+ {
450
+ "key": "harvest_source_title",
451
+ "value": "NOAA CSW Harvest Source"
452
+ }
453
+ ],
454
+ "title": "Cloud amount/frequency, NITRATE and other data from THOMAS G. THOMPSON in the NW Pacific, Gulf of Alaska and NE Pacific from 1967-02-05 to 1975-10-09 (NCEI Accession 8600315)",
455
+ "revision_id": "76e8f157-2773-48aa-b02c-23e45facd25c"
456
+ }
@@ -18,4 +18,8 @@ module JsonDocs
18
18
  def esri_opendata_metadata
19
19
  File.read(File.join(File.dirname(__FILE__), './docs/esri_open_data.json'))
20
20
  end
21
+
22
+ def ckan_metadata
23
+ File.read(File.join(File.dirname(__FILE__), './docs/ckan.json'))
24
+ end
21
25
  end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GeoCombine::BoundingBox do
4
+ subject(:valid) do
5
+ described_class.new(west: -180, east: 180, north: 90, south: -90)
6
+ end
7
+ describe '#to_envelope' do
8
+ it 'returns a valid envelope syntax' do
9
+ expect(valid.to_envelope).to eq 'ENVELOPE(-180.0, 180.0, 90.0, -90.0)'
10
+ end
11
+ end
12
+ describe '#valid?' do
13
+ context 'when valid' do
14
+ it { valid.valid? }
15
+ end
16
+ context 'when south > north' do
17
+ subject(:invalid) do
18
+ described_class.new(west: -180, east: 180, north: 33, south: 34)
19
+ end
20
+ it { expect { invalid.valid? }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
21
+ end
22
+ context 'when west > east' do
23
+ subject(:invalid) do
24
+ described_class.new(west: 10, east: -20, north: 90, south: -90)
25
+ end
26
+ it { expect { invalid.valid? }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
27
+ end
28
+ context 'when west out of range' do
29
+ subject(:invalid) do
30
+ described_class.new(west: -181, east: 180, north: 90, south: -90)
31
+ end
32
+ it { expect { invalid.valid? }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
33
+ end
34
+ context 'when east out of range' do
35
+ subject(:invalid) do
36
+ described_class.new(west: -180, east: 181, north: 90, south: -90)
37
+ end
38
+ it { expect { invalid.valid? }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
39
+ end
40
+ context 'when north out of range' do
41
+ subject(:invalid) do
42
+ described_class.new(west: -180, east: 180, north: 91, south: -90)
43
+ end
44
+ it { expect { invalid.valid? }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
45
+ end
46
+ context 'when south out of range' do
47
+ subject(:invalid) do
48
+ described_class.new(west: -180, east: 180, north: 90, south: -91)
49
+ end
50
+ it { expect { invalid.valid? }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
51
+ end
52
+ end
53
+ describe '.from_envelope' do
54
+ it { expect(described_class.from_envelope(valid.to_envelope).to_envelope).to eq valid.to_envelope }
55
+ end
56
+ describe '.from_string_delimiter' do
57
+ it { expect(described_class.from_string_delimiter('-180, -90, 180, 90').to_envelope).to eq valid.to_envelope }
58
+ end
59
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GeoCombine::CkanMetadata do
4
+ include JsonDocs
5
+ let(:ckan_sample) { GeoCombine::CkanMetadata.new(ckan_metadata) }
6
+ describe '#to_geoblacklight' do
7
+ it 'returns a GeoCombine::Geoblacklight' do
8
+ expect(ckan_sample.to_geoblacklight).to be_an GeoCombine::Geoblacklight
9
+ end
10
+ end
11
+ describe '#geoblacklight_terms' do
12
+ describe 'builds a hash which maps metadata' do
13
+ let(:metadata) { ckan_sample.instance_variable_get(:@metadata) }
14
+ it 'with dc_identifier_s' do
15
+ expect(ckan_sample.geoblacklight_terms).to include(dc_identifier_s: metadata['id'])
16
+ end
17
+ it 'with dc_title_s' do
18
+ expect(ckan_sample.geoblacklight_terms).to include(dc_title_s: metadata['title'])
19
+ end
20
+ it 'with dc_rights_s' do
21
+ expect(ckan_sample.geoblacklight_terms).to include(dc_rights_s: 'Public')
22
+ end
23
+ it 'with dct_provenance_s' do
24
+ expect(ckan_sample.geoblacklight_terms).to include(dct_provenance_s: metadata['organization']['title'])
25
+ end
26
+ it 'with layer_slug_s' do
27
+ expect(ckan_sample.geoblacklight_terms).to include(layer_slug_s: metadata['name'])
28
+ end
29
+ it 'with solr_geom' do
30
+ expect(ckan_sample.geoblacklight_terms).to include(solr_geom: 'ENVELOPE(-158.2, -105.7, 59.2, 8.9)')
31
+ end
32
+ it 'with dc_subject_sm' do
33
+ expect(ckan_sample.geoblacklight_terms[:dc_subject_sm].length).to eq 63
34
+ end
35
+ context 'with no information resources' do
36
+ let(:ckan_sample) do
37
+ ckan = GeoCombine::CkanMetadata.new(ckan_metadata)
38
+ metadata = ckan.instance_variable_get('@metadata')
39
+ metadata['resources'].delete_if { |resource| resource['resource_locator_function'] == 'information' }
40
+ ckan
41
+ end
42
+ it 'has no url (home page) in dct_references_s' do
43
+ expect(JSON.parse(ckan_sample.geoblacklight_terms[:dct_references_s])).not_to include('http://schema.org/url')
44
+ end
45
+ end
46
+ context 'with no download resources' do
47
+ let(:ckan_sample) do
48
+ ckan = GeoCombine::CkanMetadata.new(ckan_metadata)
49
+ metadata = ckan.instance_variable_get('@metadata')
50
+ metadata['resources'].delete_if { |resource| resource['resource_locator_function'] == 'download' }
51
+ ckan
52
+ end
53
+ it 'has no downloadUrl in dct_references_s' do
54
+ expect(ckan_sample.downloadable?).to be_falsey
55
+ expect(JSON.parse(ckan_sample.geoblacklight_terms[:dct_references_s])).not_to include('http://schema.org/downloadUrl')
56
+ end
57
+ end
58
+ context 'with a ZIP download' do
59
+ let(:ckan_sample) do
60
+ ckan = GeoCombine::CkanMetadata.new(ckan_metadata)
61
+ metadata = ckan.instance_variable_get('@metadata')
62
+ metadata['resources'] = [
63
+ {
64
+ 'resource_locator_function' => 'download',
65
+ 'format' => 'ZIP',
66
+ 'url' => 'https://example.com/layer.zip'
67
+ }
68
+ ]
69
+ ckan
70
+ end
71
+ it 'has a format and a download URL' do
72
+ expect(ckan_sample.downloadable?).to be_truthy
73
+ expect(ckan_sample.geoblacklight_terms).to include(dc_format_s: 'ZIP')
74
+ expect(JSON.parse(ckan_sample.geoblacklight_terms[:dct_references_s])).to include('http://schema.org/downloadUrl' => 'https://example.com/layer.zip')
75
+ end
76
+ end
77
+ context 'without any resources' do
78
+ let(:ckan_sample) do
79
+ ckan = GeoCombine::CkanMetadata.new(ckan_metadata)
80
+ metadata = ckan.instance_variable_get('@metadata')
81
+ metadata.delete('resources')
82
+ ckan
83
+ end
84
+ it 'has no urls in dct_references_s' do
85
+ expect(ckan_sample.downloadable?).to be_falsey
86
+ expect(JSON.parse(ckan_sample.geoblacklight_terms[:dct_references_s])).not_to include('http://schema.org/url')
87
+ expect(JSON.parse(ckan_sample.geoblacklight_terms[:dct_references_s])).not_to include('http://schema.org/downloadUrl')
88
+ end
89
+ end
90
+ context 'with very long descriptions' do
91
+ let(:ckan_sample) do
92
+ ckan = GeoCombine::CkanMetadata.new(ckan_metadata)
93
+ metadata = ckan.instance_variable_get('@metadata')
94
+ metadata['notes'] = 'x' * 50000
95
+ ckan
96
+ end
97
+ it 'restricts the size' do
98
+ expect(ckan_sample.geoblacklight_terms[:dc_description_s].length).to eq GeoCombine::CkanMetadata::MAX_STRING_LENGTH + 1
99
+ end
100
+ end
101
+ context 'with no descriptions' do
102
+ let(:ckan_sample) do
103
+ ckan = GeoCombine::CkanMetadata.new(ckan_metadata)
104
+ metadata = ckan.instance_variable_get('@metadata')
105
+ metadata['notes'] = nil
106
+ ckan
107
+ end
108
+ it 'omits the description' do
109
+ expect(ckan_sample.geoblacklight_terms).not_to include(:dc_description_s)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -35,9 +35,6 @@ RSpec.describe GeoCombine::Geoblacklight do
35
35
  let(:enhanced_geobl) { GeoCombine::Geoblacklight.new(basic_geoblacklight, 'layer_geom_type_s' => 'esriGeometryPolygon') }
36
36
  before { enhanced_geobl.enhance_metadata }
37
37
  describe '#enhance_metadata' do
38
- it 'calls enhancement methods to validate document' do
39
- expect(enhanced_geobl.valid?).to be true
40
- end
41
38
  it 'enhances the dc_subject_sm field' do
42
39
  expect(enhanced_geobl.metadata['dc_subject_sm']).to include 'Boundaries', 'Inland Waters'
43
40
  end
@@ -113,6 +110,11 @@ RSpec.describe GeoCombine::Geoblacklight do
113
110
  expect(enhanced_geobl).to receive(:dct_references_validate!)
114
111
  enhanced_geobl.valid?
115
112
  end
113
+ it 'validates spatial bounding box' do
114
+ expect(JSON::Validator).to receive(:validate!).and_return true
115
+ expect { basic_geobl.valid? }
116
+ .to raise_error GeoCombine::Exceptions::InvalidGeometry
117
+ end
116
118
  end
117
119
  describe '#dct_references_validate!' do
118
120
  context 'with valid document' do
@@ -151,4 +153,12 @@ RSpec.describe GeoCombine::Geoblacklight do
151
153
  end
152
154
  end
153
155
  end
156
+ describe 'spatial_validate!' do
157
+ context 'when valid' do
158
+ it { full_geobl.spatial_validate! }
159
+ end
160
+ context 'when invalid' do
161
+ it { expect { basic_geobl.spatial_validate! }.to raise_error GeoCombine::Exceptions::InvalidGeometry }
162
+ end
163
+ end
154
164
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geo_combine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jack Reed
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-05 00:00:00.000000000 Z
11
+ date: 2017-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rsolr
@@ -155,6 +155,8 @@ files:
155
155
  - bin/geocombine
156
156
  - geo_combine.gemspec
157
157
  - lib/geo_combine.rb
158
+ - lib/geo_combine/bounding_box.rb
159
+ - lib/geo_combine/ckan_metadata.rb
158
160
  - lib/geo_combine/cli.rb
159
161
  - lib/geo_combine/esri_open_data.rb
160
162
  - lib/geo_combine/exceptions.rb
@@ -187,11 +189,14 @@ files:
187
189
  - spec/features/fgdc2html_spec.rb
188
190
  - spec/features/iso2html_spec.rb
189
191
  - spec/fixtures/docs/basic_geoblacklight.json
192
+ - spec/fixtures/docs/ckan.json
190
193
  - spec/fixtures/docs/esri_open_data.json
191
194
  - spec/fixtures/docs/full_geoblacklight.json
192
195
  - spec/fixtures/json_docs.rb
193
196
  - spec/fixtures/xml_docs.rb
194
197
  - spec/helpers.rb
198
+ - spec/lib/geo_combine/bounding_box_spec.rb
199
+ - spec/lib/geo_combine/ckan_metadata_spec.rb
195
200
  - spec/lib/geo_combine/esri_open_data_spec.rb
196
201
  - spec/lib/geo_combine/fgdc_spec.rb
197
202
  - spec/lib/geo_combine/formatting_spec.rb
@@ -219,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
224
  version: '0'
220
225
  requirements: []
221
226
  rubyforge_project:
222
- rubygems_version: 2.4.5.1
227
+ rubygems_version: 2.5.2
223
228
  signing_key:
224
229
  specification_version: 4
225
230
  summary: A Ruby toolkit for managing geospatial metadata
@@ -227,11 +232,14 @@ test_files:
227
232
  - spec/features/fgdc2html_spec.rb
228
233
  - spec/features/iso2html_spec.rb
229
234
  - spec/fixtures/docs/basic_geoblacklight.json
235
+ - spec/fixtures/docs/ckan.json
230
236
  - spec/fixtures/docs/esri_open_data.json
231
237
  - spec/fixtures/docs/full_geoblacklight.json
232
238
  - spec/fixtures/json_docs.rb
233
239
  - spec/fixtures/xml_docs.rb
234
240
  - spec/helpers.rb
241
+ - spec/lib/geo_combine/bounding_box_spec.rb
242
+ - spec/lib/geo_combine/ckan_metadata_spec.rb
235
243
  - spec/lib/geo_combine/esri_open_data_spec.rb
236
244
  - spec/lib/geo_combine/fgdc_spec.rb
237
245
  - spec/lib/geo_combine/formatting_spec.rb