geo_concerns 0.0.4 → 0.0.5

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.
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