geo_concerns 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -2
  3. data/README.md +13 -0
  4. data/app/controllers/concerns/geo_concerns/file_sets_controller_behavior.rb +15 -0
  5. data/app/controllers/concerns/geo_concerns/geoblacklight_controller_behavior.rb +29 -0
  6. data/app/models/concerns/geo_concerns/ability.rb +12 -0
  7. data/app/models/concerns/geo_concerns/file_set/derivatives.rb +6 -6
  8. data/app/models/concerns/geo_concerns/geo_file_format_behavior.rb +4 -4
  9. data/app/presenters/geo_concerns/raster_work_show_presenter.rb +1 -1
  10. data/app/processors/geo_concerns/processors/mapnik.rb +30 -0
  11. data/app/processors/geo_concerns/processors/vector/base.rb +2 -1
  12. data/app/processors/geo_concerns/processors/vector/info.rb +66 -0
  13. data/app/services/geo_concerns/authority_service.rb +29 -0
  14. data/app/services/geo_concerns/discovery.rb +4 -0
  15. data/app/services/geo_concerns/discovery/abstract_document.rb +36 -0
  16. data/app/services/geo_concerns/discovery/document_builder.rb +69 -0
  17. data/app/services/geo_concerns/discovery/document_builder/basic_metadata_builder.rb +69 -0
  18. data/app/services/geo_concerns/discovery/document_builder/composite_builder.rb +21 -0
  19. data/app/services/geo_concerns/discovery/document_builder/date_builder.rb +38 -0
  20. data/app/services/geo_concerns/discovery/document_builder/document_helper.rb +10 -0
  21. data/app/services/geo_concerns/discovery/document_builder/document_path.rb +82 -0
  22. data/app/services/geo_concerns/discovery/document_builder/layer_info_builder.rb +65 -0
  23. data/app/services/geo_concerns/discovery/document_builder/references_builder.rb +82 -0
  24. data/app/services/geo_concerns/discovery/document_builder/spatial_builder.rb +40 -0
  25. data/app/services/geo_concerns/discovery/geoblacklight_document.rb +122 -0
  26. data/app/services/geo_concerns/image_format_service.rb +6 -0
  27. data/app/services/geo_concerns/metadata_format_service.rb +6 -0
  28. data/app/services/geo_concerns/raster_format_service.rb +6 -0
  29. data/app/services/geo_concerns/vector_format_service.rb +6 -0
  30. data/app/views/curation_concerns/image_works/show.html.erb +1 -1
  31. data/app/views/curation_concerns/raster_works/show.html.erb +1 -1
  32. data/app/views/curation_concerns/vector_works/show.html.erb +1 -1
  33. data/app/views/geo_concerns/_representative_media.html.erb +5 -0
  34. data/app/views/{curation_concerns → geo_concerns}/file_sets/_form.html.erb +4 -4
  35. data/app/views/geo_concerns/file_sets/media_display/_geo.html.erb +16 -0
  36. data/app/views/geo_concerns/file_sets/new.html.erb +12 -0
  37. data/config/routes.rb +18 -0
  38. data/geo_concerns.gemspec +2 -0
  39. data/lib/generators/geo_concerns/install_generator.rb +34 -0
  40. data/lib/generators/geo_concerns/templates/config/authorities/image_formats.yml +2 -0
  41. data/lib/generators/geo_concerns/templates/config/authorities/raster_formats.yml +4 -0
  42. data/lib/generators/geo_concerns/templates/config/authorities/vector_formats.yml +2 -0
  43. data/lib/generators/geo_concerns/templates/config/discovery/geoblacklight_schema.json +168 -0
  44. data/lib/generators/geo_concerns/templates/config/mapnik.yml +24 -0
  45. data/lib/generators/geo_concerns/templates/controllers/curation_concerns/image_works_controller.rb +1 -0
  46. data/lib/generators/geo_concerns/templates/controllers/curation_concerns/raster_works_controller.rb +1 -0
  47. data/lib/generators/geo_concerns/templates/controllers/curation_concerns/vector_works_controller.rb +1 -0
  48. data/lib/geo_concerns/version.rb +1 -1
  49. data/spec/controllers/file_sets_controller_spec.rb +20 -0
  50. data/spec/controllers/vector_works_controller_spec.rb +33 -0
  51. data/spec/processors/geo_concerns/processors/mapnik_spec.rb +48 -0
  52. data/spec/processors/geo_concerns/processors/vector/base_spec.rb +1 -1
  53. data/spec/processors/geo_concerns/processors/vector/info_spec.rb +41 -0
  54. data/spec/services/geo_concerns/discovery/abstract_document_spec.rb +23 -0
  55. data/spec/services/geo_concerns/discovery/document_builder_spec.rb +186 -0
  56. data/spec/services/geo_concerns/discovery/geoblacklight_document_spec.rb +17 -0
  57. data/spec/services/raster_format_service_spec.rb +1 -1
  58. data/template.rb +2 -2
  59. metadata +70 -9
  60. data/app/services/authority_service.rb +0 -23
  61. data/app/services/image_format_service.rb +0 -4
  62. data/app/services/metadata_format_service.rb +0 -4
  63. data/app/services/raster_format_service.rb +0 -4
  64. data/app/services/vector_format_service.rb +0 -4
@@ -0,0 +1,69 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class BasicMetadataBuilder
5
+ attr_reader :geo_concern
6
+
7
+ def initialize(geo_concern)
8
+ @geo_concern = geo_concern
9
+ end
10
+
11
+ # Builds fields such as id, subject, and publisher.
12
+ # @param [AbstractDocument] discovery document
13
+ def build(document)
14
+ build_simple_attributes(document)
15
+ build_complex_attributes(document)
16
+ end
17
+
18
+ private
19
+
20
+ # Returns an array of attributes to add to document.
21
+ # @return [Array] attributes
22
+ def simple_attributes
23
+ [:id, :creator, :subject, :spatial, :temporal,
24
+ :title, :provenance, :language, :publisher]
25
+ end
26
+
27
+ # Builds simple metadata attributes.
28
+ # @param [AbstractDocument] discovery document
29
+ def build_simple_attributes(document)
30
+ simple_attributes.each do |a|
31
+ document.send("#{a}=", geo_concern.send(a.to_s))
32
+ end
33
+ end
34
+
35
+ # Builds more complex metadata attributes.
36
+ # @param [AbstractDocument] discovery document
37
+ def build_complex_attributes(document)
38
+ document.identifier = identifier
39
+ document.description = description
40
+ document.access_rights = geo_concern.solr_document.visibility
41
+ document.slug = slug
42
+ end
43
+
44
+ # Returns the work description. If none is available,
45
+ # a basic description is created.
46
+ # @return [String] description
47
+ def description
48
+ return geo_concern.description.first if geo_concern.description.first
49
+ "A #{geo_concern.human_readable_type.downcase} object."
50
+ end
51
+
52
+ # Returns the document slug for use in discovery systems.
53
+ # @return [String] document slug
54
+ def slug
55
+ return unless geo_concern.provenance
56
+ "#{geo_concern.provenance[0].downcase.tr(' ', '-')}-#{geo_concern.id}"
57
+ end
58
+
59
+ # Returns the document identifier. If there is no identifier,
60
+ # the work id is used instead.
61
+ # @return [String] document identifier
62
+ def identifier
63
+ field = geo_concern.solr_document[:identifier_tesim]
64
+ field ? field.first : geo_concern.id
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,21 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class CompositeBuilder
5
+ attr_reader :services
6
+
7
+ def initialize(*services)
8
+ @services = services.compact
9
+ end
10
+
11
+ # Runs each builder service to build a discovery document.
12
+ # @param [AbstractDocument] discovery document
13
+ def build(document)
14
+ services.each do |service|
15
+ service.build(document)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class DateBuilder
5
+ attr_reader :geo_concern
6
+
7
+ def initialize(geo_concern)
8
+ @geo_concern = geo_concern
9
+ end
10
+
11
+ # Builds date fields such as layer year and modified date.
12
+ # @param [AbstractDocument] discovery document
13
+ def build(document)
14
+ document.layer_year = layer_year
15
+ document.date_modified = date_modified
16
+ end
17
+
18
+ private
19
+
20
+ # Returns a year associated with the layer. Taken from first
21
+ # value in temporal or from date uploaded. If neither is valid,
22
+ # the current year is used.
23
+ # @return [Integer] year
24
+ def layer_year
25
+ date = geo_concern.temporal.first || geo_concern.date_uploaded
26
+ year = date.match(/(?<=\D|^)(\d{4})(?=\D|$)/)
27
+ year ? year[0].to_i : Time.current.year
28
+ end
29
+
30
+ # Returns the date the work was modified.
31
+ # @return [String] date in rfc3339 format.
32
+ def date_modified
33
+ DateTime.rfc3339(geo_concern.solr_document[:date_modified_dtsi]).to_s
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class DocumentHelper
5
+ include Rails.application.routes.url_helpers
6
+ include ActionDispatch::Routing::PolymorphicRoutes
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,82 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class DocumentPath
5
+ attr_reader :geo_concern, :ssl
6
+ def initialize(geo_concern, ssl: false)
7
+ @geo_concern = geo_concern
8
+ @ssl = ssl
9
+ end
10
+
11
+ # Returns url for geo concern show page.
12
+ # @return [String] geo concern show page url
13
+ def to_s
14
+ helper.polymorphic_url(geo_concern, protocol: protocol)
15
+ end
16
+
17
+ # Returns url for downloading the original file.
18
+ # @return [String] original file download url
19
+ def file_download
20
+ return unless file_set
21
+ helper.download_url(file_set, protocol: protocol)
22
+ end
23
+
24
+ # Returns url for downloading the metadata file.
25
+ # @param [String] metadata file format to download
26
+ # @return [String] metadata download url
27
+ def metadata_download(format)
28
+ return unless metadata_file_set
29
+ path = helper.download_url(metadata_file_set, protocol: protocol)
30
+ mime_type = metadata_file_set.solr_document[:geo_mime_type_ssim].first
31
+ path if MetadataFormatService.label(mime_type) == format
32
+ end
33
+
34
+ # Returns url for thumbnail image.
35
+ # @return [String] thumbnail url
36
+ def thumbnail
37
+ return unless file_download
38
+ "#{file_download}?file=thumbnail"
39
+ end
40
+
41
+ private
42
+
43
+ # Returns the first geo file set presenter attached to work.
44
+ # @return [FileSetPresenter] geo file set presenter
45
+ def file_set
46
+ return unless geo_concern.geo_file_set_presenters
47
+ geo_concern.geo_file_set_presenters.first
48
+ end
49
+
50
+ # Returns the first metadata file set presenter attached to work.
51
+ # @return [FileSetPresenter] metadata file set presenter
52
+ def metadata_file_set
53
+ return unless geo_concern.external_metadata_file_set_presenters
54
+ geo_concern.external_metadata_file_set_presenters.first
55
+ end
56
+
57
+ # Instantiates a DocumentHelper object.
58
+ # Used for access to url_helpers and poymorphic routes.
59
+ # @return [DocumentHelper] document helper
60
+ def helper
61
+ @helper ||= DocumentHelper.new
62
+ end
63
+
64
+ # Indicates if the ssl is enabled.
65
+ # @return [Boolean] use ssl
66
+ def ssl?
67
+ @ssl == true
68
+ end
69
+
70
+ # Returns protocol to use in url. Depends on ssl status.
71
+ # @return [Boolean] http protocol
72
+ def protocol
73
+ if ssl?
74
+ :https
75
+ else
76
+ :http
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,65 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class LayerInfoBuilder
5
+ attr_reader :geo_concern
6
+
7
+ def initialize(geo_concern)
8
+ @geo_concern = geo_concern
9
+ end
10
+
11
+ # Builds fields about the geospatial file such as geometry and format.
12
+ # @param [AbstractDocument] discovery document
13
+ def build(document)
14
+ document.geom_type = geom_type
15
+ document.format = format
16
+ end
17
+
18
+ private
19
+
20
+ # Uses parent work class to determine file geometry type.
21
+ # These geom types are used in geoblacklight documents.
22
+ # @return [String] file geometry type
23
+ def geom_type
24
+ case geo_concern.class.to_s
25
+ when "GeoConcerns::ImageWorkShowPresenter"
26
+ 'Scanned Map'
27
+ when "GeoConcerns::RasterWorkShowPresenter"
28
+ 'Raster'
29
+ when "GeoConcerns::VectorWorkShowPresenter"
30
+ vector_geom_type
31
+ end
32
+ end
33
+
34
+ # Uses file mime type to determine file format.
35
+ # @return [String] file format code
36
+ def format
37
+ if ImageFormatService.include? geo_mime_type
38
+ ImageFormatService.code(geo_mime_type)
39
+ elsif RasterFormatService.include? geo_mime_type
40
+ RasterFormatService.code(geo_mime_type)
41
+ elsif VectorFormatService.include? geo_mime_type
42
+ VectorFormatService.code(geo_mime_type)
43
+ end
44
+ end
45
+
46
+ # Returns the geometry for a vector file.
47
+ # @return [String] vector geometry
48
+ def vector_geom_type
49
+ # TODO: Get the geom type as part of a geo characterization service.
50
+ # "Point, Line, Polygon, Mixed"
51
+ 'Mixed'
52
+ end
53
+
54
+ # Returns the 'geo' mime type of the first file attached to the work.
55
+ # @return [String] file mime type
56
+ def geo_mime_type
57
+ file_sets = geo_concern.geo_file_set_presenters
58
+ return if file_sets.nil? || file_sets.empty?
59
+ return unless (mime_types = file_sets.first.solr_document[:geo_mime_type_ssim])
60
+ mime_types.first
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,82 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class ReferencesBuilder
5
+ attr_reader :geo_concern, :path
6
+
7
+ def initialize(geo_concern, path)
8
+ @geo_concern = geo_concern
9
+ @path = path
10
+ end
11
+
12
+ # Builds service reference fields such as thumbnail and download url.
13
+ # @param [AbstractDocument] discovery document
14
+ def build(document)
15
+ document.wxs_identifier = wxs_identifier
16
+ build_metadata_refs(document)
17
+ build_download_refs(document)
18
+ end
19
+
20
+ private
21
+
22
+ # Builds metadata file references.
23
+ # @param [AbstractDocument] discovery document
24
+ def build_metadata_refs(document)
25
+ document.fgdc = fgdc
26
+ document.iso19139 = iso19139
27
+ document.mods = mods
28
+ end
29
+
30
+ # Builds geospatial file download references.
31
+ # @param [AbstractDocument] discovery document
32
+ def build_download_refs(document)
33
+ document.download = download
34
+ document.url = url
35
+ document.thumbnail = thumbnail
36
+ end
37
+
38
+ # Returns the identifier to use with WMS/WFS/WCS services.
39
+ # @return [String] wxs indentifier
40
+ def wxs_identifier
41
+ geo_concern.id
42
+ end
43
+
44
+ # Returns a url to access further descriptive information.
45
+ # @return [String] work show page url
46
+ def url
47
+ path.to_s
48
+ end
49
+
50
+ # Returns a direct file download url
51
+ # @return [String] direct file url
52
+ def download
53
+ path.file_download
54
+ end
55
+
56
+ # Returns an FGDC metadata document download url
57
+ # @return [String] FGDC metadata document url
58
+ def fgdc
59
+ path.metadata_download('FGDC')
60
+ end
61
+
62
+ # Returns an ISO19139 metadata document download url
63
+ # @return [String] ISO19139 metadata document url
64
+ def iso19139
65
+ path.metadata_download('ISO19139')
66
+ end
67
+
68
+ # Returns an MODS metadata document download url
69
+ # @return [String] MODS metadata document url
70
+ def mods
71
+ path.metadata_download('MODS')
72
+ end
73
+
74
+ # Returns a thumbnail file url
75
+ # @return [String] thumbnail url
76
+ def thumbnail
77
+ path.thumbnail
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,40 @@
1
+ module GeoConcerns
2
+ module Discovery
3
+ class DocumentBuilder
4
+ class SpatialBuilder
5
+ attr_reader :geo_concern
6
+
7
+ def initialize(geo_concern)
8
+ @geo_concern = geo_concern
9
+ end
10
+
11
+ # Builds spatial fields such as bounding box and solr geometry.
12
+ # @param [AbstractDocument] discovery document
13
+ def build(document)
14
+ document.geo_rss_coverage = to_geo_rss
15
+ document.solr_coverage = to_solr
16
+ end
17
+
18
+ private
19
+
20
+ # Parses coverage field from geo work and instantiates a coverage object.
21
+ # @return [GeoConcerns::Coverage] coverage object
22
+ def coverage
23
+ @coverage ||= GeoConcerns::Coverage.parse(geo_concern.coverage.first)
24
+ end
25
+
26
+ # Returns the coverage as georss.
27
+ # @return [String] coverage in georss format
28
+ def to_geo_rss
29
+ "#{coverage.s} #{coverage.w} #{coverage.n} #{coverage.e}"
30
+ end
31
+
32
+ # Returns the coverage in solr format.
33
+ # @return [String] coverage in solr format
34
+ def to_solr
35
+ "ENVELOPE(#{coverage.w}, #{coverage.e}, #{coverage.n}, #{coverage.s})"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,122 @@
1
+ require 'json-schema'
2
+
3
+ module GeoConcerns
4
+ module Discovery
5
+ class GeoblacklightDocument < AbstractDocument
6
+ # Implements the to_hash method on the abstract document.
7
+ # @param _args [Array<Object>] arguments needed for the renderer, unused here
8
+ # @return [Hash] geoblacklight document as a hash
9
+ def to_hash(_args = nil)
10
+ document
11
+ end
12
+
13
+ # Implements the to_json method on the abstract document.
14
+ # @param _args [Array<Object>] arguments needed for the json renderer, unused here
15
+ # @return [String] geoblacklight document as a json string
16
+ def to_json(_args = nil)
17
+ document.to_json
18
+ end
19
+
20
+ private
21
+
22
+ # Builds the geoblacklight document hash.
23
+ # @return [Hash] geoblacklight document as a hash
24
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
25
+ def document_hash
26
+ {
27
+ uuid: id,
28
+ dc_identifier_s: identifier,
29
+ dc_title_s: title.first,
30
+ dc_description_s: description,
31
+ dc_rights_s: rights,
32
+ dct_provenance_s: provenance.first,
33
+ dc_creator_sm: creator,
34
+ dc_language_s: language.first,
35
+ dc_publisher_s: publisher.first,
36
+ dc_subject_sm: subject,
37
+ dct_spatial_sm: spatial,
38
+ dct_temporal_sm: temporal,
39
+ layer_slug_s: slug,
40
+ georss_box_s: geo_rss_coverage,
41
+ solr_geom: solr_coverage,
42
+ solr_year_i: layer_year,
43
+ layer_modified_dt: date_modified,
44
+ layer_id_s: wxs_identifier,
45
+ dct_references_s: clean_document(references).to_json,
46
+ layer_geom_type_s: geom_type,
47
+ dc_format_s: process_format_codes(format)
48
+ }
49
+ end
50
+ # rubocop:enable Metrics/LineLength, Metrics/AbcSize
51
+
52
+ # Builds the dct_references hash.
53
+ # @return [Hash] geoblacklight references as a hash
54
+ def references
55
+ {
56
+ 'http://schema.org/url' => url,
57
+ 'http://www.opengis.net/cat/csw/csdgm' => fgdc,
58
+ 'http://www.isotc211.org/schemas/2005/gmd/' => iso19139,
59
+ 'http://www.loc.gov/mods/v3' => mods,
60
+ 'http://schema.org/downloadUrl' => download,
61
+ 'http://schema.org/thumbnailUrl' => thumbnail
62
+ }
63
+ end
64
+
65
+ # Returns the geoblacklight rights field based on work visibility.
66
+ # @return [String] geoblacklight access rights
67
+ def rights
68
+ if access_rights == Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC
69
+ 'Public'
70
+ else
71
+ 'Restricted'
72
+ end
73
+ end
74
+
75
+ # Transforms shapfile, tiff, and arc grid format codes into geoblacklight format codes.
76
+ # @return [String] geoblacklight format codes
77
+ def process_format_codes(format)
78
+ case format
79
+ when 'ESRI Shapefile'
80
+ 'Shapefile'
81
+ when 'GTiff'
82
+ 'GeoTIFF'
83
+ when 'AIG'
84
+ 'ArcGRID'
85
+ else
86
+ format
87
+ end
88
+ end
89
+
90
+ # Returns the location of geoblacklight json schema document.
91
+ # @return [String] geoblacklight json schema document path
92
+ def schema
93
+ Rails.root.join('config', 'discovery', 'geoblacklight_schema.json').to_s
94
+ end
95
+
96
+ # Validates the geoblacklight document against the json schema.
97
+ # @return [Boolean] is the document valid?
98
+ def valid?(doc)
99
+ JSON::Validator.validate(schema, doc, validate_schema: true)
100
+ end
101
+
102
+ # Returns a hash of errors from json schema validation.
103
+ # @return [Hash] json schema validation errors
104
+ def schema_errors(doc)
105
+ { error: JSON::Validator.fully_validate(schema, doc) }
106
+ end
107
+
108
+ # Cleans the geoblacklight document hash by removing unused fields,
109
+ # then validates it again a json schema. If there are errors, an
110
+ # error hash is returned, otherwise, the cleaned doc is returned.
111
+ # @return [Hash] geoblacklight document hash or error hash
112
+ def document
113
+ clean = clean_document(document_hash)
114
+ if valid?(clean)
115
+ clean
116
+ else
117
+ schema_errors(clean)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end