geoblacklight 4.1.0 → 4.2.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +3 -11
  3. data/.gitignore +12 -0
  4. data/.solr_wrapper +0 -1
  5. data/Gemfile +14 -0
  6. data/Procfile.dev +3 -0
  7. data/README.md +5 -30
  8. data/app/assets/images/blacklight/geoblacklight-icons.json +1857 -966
  9. data/app/assets/javascripts/geoblacklight/basemaps.js +1 -1
  10. data/app/assets/javascripts/geoblacklight/modules/util.js +8 -3
  11. data/app/assets/javascripts/geoblacklight/templates/index_map_info.hbs +22 -1
  12. data/app/assets/javascripts/geoblacklight/viewers/cog.js +5 -0
  13. data/app/assets/javascripts/geoblacklight/viewers/pmtiles.js +5 -0
  14. data/app/controllers/download_controller.rb +1 -21
  15. data/app/frontend/clover/clover_initializer.js +29 -0
  16. data/app/frontend/entrypoints/application.js +28 -0
  17. data/app/frontend/entrypoints/clover.js +5 -0
  18. data/app/frontend/entrypoints/ol.js +5 -0
  19. data/app/frontend/openlayers/basemaps.js +47 -0
  20. data/app/frontend/openlayers/ol_initializer.js +105 -0
  21. data/app/frontend/stylesheets/openlayers.css +1 -0
  22. data/app/helpers/geoblacklight/application_helper.rb +8 -0
  23. data/app/helpers/geoblacklight_helper.rb +57 -0
  24. data/app/views/catalog/_citation.html.erb +35 -10
  25. data/app/views/catalog/_show_default_viewer_container.html.erb +1 -2
  26. data/app/views/catalog/_web_services_default.html.erb +6 -1
  27. data/app/views/catalog/_web_services_wfs.html.erb +6 -1
  28. data/app/views/catalog/_web_services_wms.html.erb +6 -1
  29. data/app/views/catalog/web_services.html.erb +16 -0
  30. data/bin/vite +29 -0
  31. data/config/locales/geoblacklight.en.yml +12 -2
  32. data/config/vite.json +14 -0
  33. data/geoblacklight.gemspec +8 -6
  34. data/lib/generators/geoblacklight/install_generator.rb +19 -4
  35. data/lib/generators/geoblacklight/templates/base.html.erb +52 -0
  36. data/lib/generators/geoblacklight/templates/catalog_controller.rb +0 -2
  37. data/lib/generators/geoblacklight/templates/settings.gbl_v1.yml +2 -2
  38. data/lib/generators/geoblacklight/templates/settings.yml +6 -2
  39. data/lib/generators/geoblacklight/templates/vite.json +16 -0
  40. data/lib/geoblacklight/constants.rb +3 -1
  41. data/lib/geoblacklight/engine.rb +24 -0
  42. data/lib/geoblacklight/faraday_middleware/follow_redirects.rb +3 -1
  43. data/lib/geoblacklight/item_viewer.rb +13 -1
  44. data/lib/geoblacklight/metadata_transformer/base.rb +1 -1
  45. data/lib/geoblacklight/references.rb +1 -1
  46. data/lib/geoblacklight/version.rb +1 -1
  47. data/package.json +8 -12
  48. data/spec/features/citations_spec.rb +12 -0
  49. data/spec/features/download_layer_spec.rb +1 -2
  50. data/spec/features/full_screen_controll_spec.rb +8 -1
  51. data/spec/features/home_page_spec.rb +3 -0
  52. data/spec/features/layer_inspection_spec.rb +1 -1
  53. data/spec/features/saved_searches_spec.rb +1 -1
  54. data/spec/features/search_results_map_spec.rb +1 -0
  55. data/spec/features/tilejson_spec.rb +4 -2
  56. data/spec/features/web_services_modal_spec.rb +32 -2
  57. data/spec/features/wmts_spec.rb +5 -3
  58. data/spec/fixtures/index_maps/index-map-point.geojson +949 -0
  59. data/spec/fixtures/index_maps/index-map-polygon-no-downloadurl.geojson +970 -0
  60. data/spec/fixtures/index_maps/index-map-polygon.geojson +970 -0
  61. data/spec/fixtures/index_maps/index-map-stanford.geojson +390 -0
  62. data/spec/fixtures/index_maps/index-map-v1-complex.geojson +12249 -0
  63. data/spec/fixtures/manifests/tilejson.json +1 -1
  64. data/spec/fixtures/manifests/wmts-single.xml +5 -5
  65. data/spec/fixtures/solr_documents/README.md +1 -0
  66. data/spec/fixtures/solr_documents/b1g_iiif_manifest.json +64 -0
  67. data/spec/fixtures/solr_documents/index-map-polygon-no-downloadurl.json +1 -1
  68. data/spec/fixtures/solr_documents/index-map-polygon.json +1 -1
  69. data/spec/fixtures/solr_documents/index-map-stanford.json +1 -1
  70. data/spec/fixtures/solr_documents/index-map-v1-complex.json +56 -0
  71. data/spec/fixtures/solr_documents/index_map_point.json +1 -1
  72. data/spec/fixtures/solr_documents/public_cog_princeton.json +57 -0
  73. data/spec/fixtures/solr_documents/public_pmtiles_princeton.json +41 -0
  74. data/spec/fixtures/solr_documents/tilejson.json +1 -1
  75. data/spec/fixtures/solr_documents/wmts-single-layer.json +1 -1
  76. data/spec/helpers/geoblacklight_helper_spec.rb +42 -0
  77. data/spec/spec_helper.rb +7 -11
  78. data/spec/test_app_templates/Gemfile.extra +0 -1
  79. data/spec/test_app_templates/lib/generators/test_app_generator.rb +7 -17
  80. data/vite.config.ts +8 -0
  81. metadata +80 -26
  82. data/spec/test_app_templates/solr_documents +0 -1
  83. /data/app/assets/images/blacklight/{berkeley.svg → university-of-california-berkeley.svg} +0 -0
  84. /data/spec/{test_app_templates → fixtures}/metadata/fgdc.html +0 -0
  85. /data/spec/{test_app_templates → fixtures}/metadata/fgdc.xml +0 -0
  86. /data/spec/{test_app_templates → fixtures}/metadata/iso.html +0 -0
  87. /data/spec/{test_app_templates → fixtures}/metadata/iso.xml +0 -0
@@ -74,7 +74,7 @@ GeoBlacklight.Basemaps = {
74
74
  }
75
75
  ),
76
76
  openstreetmapStandard: L.tileLayer(
77
- 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
77
+ 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
78
78
  attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
79
79
  maxZoom: 19,
80
80
  worldCopyJump: true,
@@ -26,9 +26,14 @@ GeoBlacklight.Util = {
26
26
  });
27
27
  if (data.iiifUrl && !data.thumbnailUrl) {
28
28
  var manifest = $.getJSON(data.iiifUrl, function(manifestResponse) {
29
- if (manifestResponse.thumbnail['@id'] !== null) {
30
- data.thumbnailUrl = manifestResponse.thumbnail['@id'];
31
- thumbDeferred.resolve();
29
+ try {
30
+ if (manifestResponse.thumbnail['@id'] !== null) {
31
+ data.thumbnailUrl = manifestResponse.thumbnail['@id'];
32
+ thumbDeferred.resolve();
33
+ }
34
+ }
35
+ catch(err) {
36
+ cb(HandlebarsTemplates["index_map_info"](data));
32
37
  }
33
38
  });
34
39
  } else {
@@ -8,12 +8,21 @@
8
8
  {{#if thumbnailUrl}}
9
9
  {{#if websiteUrl}}
10
10
  <a href="{{websiteUrl}}">
11
- <img src="{{thumbnailUrl}}" class="img-responsive">
11
+ <img src="{{thumbnailUrl}}" class="img-fluid">
12
12
  </a>
13
13
  {{else}}
14
14
  <img src="{{thumbnailUrl}}">
15
15
  {{/if}}
16
16
  {{/if}}
17
+ {{#if thumbUrl}}
18
+ {{#if websiteUrl}}
19
+ <a href="{{websiteUrl}}">
20
+ <img src="{{thumbUrl}}" class="img-fluid">
21
+ </a>
22
+ {{else}}
23
+ <img src="{{thumbUrl}}">
24
+ {{/if}}
25
+ {{/if}}
17
26
  <dl class="row dl-invert document-metadata">
18
27
  {{#if websiteUrl}}
19
28
  <dt class="col-sm-3">Website:</dt>
@@ -23,10 +32,22 @@
23
32
  <dt class="col-sm-3">Download:</dt>
24
33
  <dd class="col-sm-9"><a href="{{downloadUrl}}">{{downloadUrl}}</a></dd>
25
34
  {{/if}}
35
+ {{#if download}}
36
+ <dt class="col-sm-3">Download:</dt>
37
+ <dd class="col-sm-9"><a href="{{download}}">{{download}}</a></dd>
38
+ {{/if}}
39
+ {{#if iiifUrl}}
40
+ <dt class="col-sm-3">iiif URL:</dt>
41
+ <dd class="col-sm-9"><a href="{{iiifUrl}}">{{iiifUrl}}</a></dd>
42
+ {{/if}}
26
43
  {{#if recordIdentifier}}
27
44
  <dt class="col-sm-3">Record Identifier:</dt>
28
45
  <dd class="col-sm-9">{{recordIdentifier}}</dd>
29
46
  {{/if}}
47
+ {{#if recId}}
48
+ <dt class="col-sm-3">Record Identifier:</dt>
49
+ <dd class="col-sm-9">{{recId}}</dd>
50
+ {{/if}}
30
51
  {{#if label}}
31
52
  <dt class="col-sm-3">Label:</dt>
32
53
  <dd class="col-sm-9">{{label}}</dd>
@@ -0,0 +1,5 @@
1
+ //= require geoblacklight/viewers/viewer
2
+
3
+ GeoBlacklight.Viewer.Cog = GeoBlacklight.Viewer.extend({
4
+ load: function() {}
5
+ });
@@ -0,0 +1,5 @@
1
+ //= require geoblacklight/viewers/viewer
2
+
3
+ GeoBlacklight.Viewer.Pmtiles = GeoBlacklight.Viewer.extend({
4
+ load: function() {}
5
+ });
@@ -7,7 +7,7 @@ class DownloadController < ApplicationController
7
7
  rescue_from Geoblacklight::Exceptions::ExternalDownloadFailed do |exception|
8
8
  Geoblacklight.logger.error exception.message + " " + exception.url
9
9
  flash[:danger] = view_context
10
- .tag.span(flash_error_message(exception),
10
+ .tag.span(t("geoblacklight.download.error"),
11
11
  data: {
12
12
  download: "error",
13
13
  download_id: params[:id],
@@ -55,26 +55,6 @@ class DownloadController < ApplicationController
55
55
  end
56
56
  end
57
57
 
58
- protected
59
-
60
- ##
61
- # Creates an error flash message with failed download url
62
- # @param [Geoblacklight::Exceptions::ExternalDownloadFailed] Download failed
63
- # exception
64
- # @return [String] error message to display in flash
65
- def flash_error_message(exception)
66
- if exception.url.present?
67
- t("geoblacklight.download.error_with_url",
68
- link: view_context
69
- .link_to(exception.url,
70
- exception.url,
71
- target: "blank"))
72
- .html_safe
73
- else
74
- t("geoblacklight.download.error")
75
- end
76
- end
77
-
78
58
  private
79
59
 
80
60
  def download_file_path_and_name
@@ -0,0 +1,29 @@
1
+ import { createElement } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import Viewer from '@samvera/clover-iiif/viewer'
4
+
5
+ export default class CloverInitializer {
6
+ constructor () {
7
+ this.element = document.getElementById('clover-viewer')
8
+ if (this.element) {
9
+ this.iiif_content = this.element.attributes['iiif_content'].value
10
+ }
11
+ }
12
+
13
+ run () {
14
+ if (!this.iiif_content) return false
15
+ const root = createRoot(document.getElementById('clover-viewer'))
16
+ root.render(createElement(Viewer, { id: this.iiif_content, options: this.config }))
17
+ }
18
+
19
+ get config () {
20
+ return {
21
+ showTitle: false,
22
+ showIIIFBadge: false,
23
+ informationPanel: {
24
+ renderToggle: false,
25
+ renderAbout: false
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,28 @@
1
+ // To see this message, add the following to the `<head>` section in your
2
+ // views/layouts/application.html.erb
3
+ //
4
+ // <%= vite_client_tag %>
5
+ // <%= vite_javascript_tag 'application' %>
6
+ console.log('Vite ⚡️ Rails')
7
+
8
+ // If using a TypeScript entrypoint file:
9
+ // <%= vite_typescript_tag 'application' %>
10
+ //
11
+ // If you want to use .jsx or .tsx, add the extension:
12
+ // <%= vite_javascript_tag 'application.jsx' %>
13
+
14
+ console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify.app/guide/rails')
15
+
16
+ // Example: Load Rails libraries in Vite.
17
+ //
18
+ // import * as Turbo from '@hotwired/turbo'
19
+ // Turbo.start()
20
+ //
21
+ // import ActiveStorage from '@rails/activestorage'
22
+ // ActiveStorage.start()
23
+ //
24
+ // // Import all channels.
25
+ // const channels = import.meta.globEager('./**/*_channel.js')
26
+
27
+ // Example: Import a stylesheet in app/frontend/index.css
28
+ // import '~/index.css'
@@ -0,0 +1,5 @@
1
+ import CloverInitializer from '@/clover/clover_initializer'
2
+
3
+ document.addEventListener('DOMContentLoaded', () => {
4
+ new CloverInitializer().run()
5
+ })
@@ -0,0 +1,5 @@
1
+ import OlInitializer from '@/openlayers/ol_initializer'
2
+
3
+ document.addEventListener('DOMContentLoaded', () => {
4
+ new OlInitializer().run()
5
+ })
@@ -0,0 +1,47 @@
1
+ export const openLayersBasemaps = {
2
+ darkMatter: {
3
+ url:'https://{a-d}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
4
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
5
+ maxZoom: 18
6
+ },
7
+ positron: {
8
+ url: 'https://{a-d}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
9
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
10
+ maxZoom: 18
11
+ },
12
+ positronLite: {
13
+ url: 'https://{a-d}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
14
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
15
+ maxZoom: 18
16
+ },
17
+ worldAntique: {
18
+ url: 'https://cartocdn_{a-d}.global.ssl.fastly.net/base-antique/{z}/{x}/{y}.png',
19
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
20
+ maxZoom: 18
21
+ },
22
+ worldEco: {
23
+ url: 'https://cartocdn_{a-d}.global.ssl.fastly.net/base-eco/{z}/{x}/{y}.png',
24
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
25
+ maxZoom: 18
26
+ },
27
+ flatBlue: {
28
+ url: 'https://cartocdn_{a-d}.global.ssl.fastly.net/base-flatblue/{z}/{x}/{y}.png',
29
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
30
+ maxZoom: 18
31
+ },
32
+ midnightCommander: {
33
+ url: 'https://cartocdn_{a-d}.global.ssl.fastly.net/base-midnight/{z}/{x}/{y}.png',
34
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
35
+ maxZoom: 18
36
+ },
37
+ openstreetmapHot: {
38
+ url: 'https://{a-c}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
39
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, Tiles courtesy of <a href="http://hot.openstreetmap.org/" target="_blank">Humanitarian OpenStreetMap Team</a>',
40
+ maxZoom: 19
41
+ },
42
+ openstreetmapStandard: {
43
+ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
44
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
45
+ maxZoom: 19
46
+ }
47
+ }
@@ -0,0 +1,105 @@
1
+ import '@/stylesheets/openlayers.css'
2
+ import { Map, View } from 'ol'
3
+ import TileLayer from 'ol/layer/Tile'
4
+ import VectorTile from 'ol/layer/VectorTile'
5
+ import XYZ from 'ol/source/XYZ'
6
+ import GeoJSON from 'ol/format/GeoJSON'
7
+ import { useGeographic } from 'ol/proj'
8
+ import {
9
+ Style, Stroke, Fill, Circle
10
+ } from 'ol/style'
11
+ import GeoTIFF from 'ol/source/GeoTIFF'
12
+ import WebGLTileLayer from 'ol/layer/WebGLTile'
13
+ import { FullScreen, defaults as defaultControls } from 'ol/control'
14
+ import { PMTilesVectorSource } from 'ol-pmtiles'
15
+ import { openLayersBasemaps } from './basemaps'
16
+
17
+ export default class OlInitializer {
18
+ constructor () {
19
+ this.element = document.getElementById('ol-map')
20
+ if (this.element) {
21
+ this.data = this.element.dataset
22
+ this.extent = new GeoJSON().readFeatures(this.data.mapGeom)[0].getGeometry().getExtent()
23
+ }
24
+ }
25
+
26
+ run () {
27
+ if (!this.element) return false
28
+ if (this.data.protocol === 'Pmtiles') {
29
+ this.initializePmtiles()
30
+ } else if (this.data.protocol === 'Cog') {
31
+ this.initializeCog()
32
+ }
33
+ }
34
+
35
+ baseLayer () {
36
+ const basemap = openLayersBasemaps[this.data.basemap]
37
+ const layer = new TileLayer({
38
+ source: new XYZ({
39
+ attributions: basemap["attribution"],
40
+ url: basemap["url"],
41
+ maxZoom: basemap["maxZoom"]
42
+ })
43
+ })
44
+ return layer
45
+ }
46
+
47
+ initializePmtiles () {
48
+ const vectorLayer = new VectorTile({
49
+ declutter: true,
50
+ source: new PMTilesVectorSource({
51
+ url: this.data.url
52
+ }),
53
+ style: new Style({
54
+ stroke: new Stroke({
55
+ color: '#7070B3',
56
+ width: 1
57
+ }),
58
+ fill: new Fill({
59
+ color: '#FFFFFF'
60
+ }),
61
+ image: new Circle({
62
+ radius: 7,
63
+ fill: new Fill({
64
+ color: '#7070B3'
65
+ }),
66
+ stroke: new Stroke({
67
+ color: '#FFFFFF',
68
+ width: 2
69
+ })
70
+ })
71
+ })
72
+ })
73
+
74
+ useGeographic()
75
+ const map = new Map({
76
+ controls: defaultControls().extend([new FullScreen()]),
77
+ layers: [this.baseLayer(), vectorLayer],
78
+ target: 'ol-map'
79
+ })
80
+ map.getView().fit(this.extent, map.getSize())
81
+ }
82
+
83
+ initializeCog () {
84
+ const source = new GeoTIFF({
85
+ sources: [{ url: this.data.url }]
86
+ })
87
+
88
+ source.getView().then((view) => {
89
+ const map = new Map({
90
+ controls: defaultControls().extend([new FullScreen()]),
91
+ target: 'ol-map',
92
+ layers: [
93
+ this.baseLayer(),
94
+ new WebGLTileLayer({
95
+ source
96
+ })
97
+ ],
98
+ view: new View({
99
+ center: view.center
100
+ })
101
+ })
102
+ map.getView().fit(view.extent, map.getSize())
103
+ })
104
+ }
105
+ }
@@ -0,0 +1 @@
1
+ @import "node_modules/ol/ol.css";
@@ -0,0 +1,8 @@
1
+ module Geoblacklight
2
+ module ApplicationHelper
3
+ # Override: Returns the engine assets manifest.
4
+ def vite_manifest
5
+ Geoblacklight::Engine.vite_ruby.manifest
6
+ end
7
+ end
8
+ end
@@ -76,6 +76,8 @@ module GeoblacklightHelper
76
76
  ##
77
77
  # Returns an SVG icon or empty HTML span element
78
78
  # @return [SVG or HTML tag]
79
+ # standard:disable Style/ArgumentsForwarding
80
+ # TODO: Remove linter disable after lowest supported Ruby version >= 3.2
79
81
  def geoblacklight_icon(name, **args)
80
82
  icon_name = name ? name.to_s.parameterize : "none"
81
83
  begin
@@ -84,6 +86,7 @@ module GeoblacklightHelper
84
86
  tag.span class: "icon-missing geoblacklight-none"
85
87
  end
86
88
  end
89
+ # standard:enable Style/ArgumentsForwarding
87
90
 
88
91
  ##
89
92
  # Looks up properly formatted names for formats
@@ -242,4 +245,58 @@ module GeoblacklightHelper
242
245
  "index"
243
246
  end
244
247
  end
248
+
249
+ def viewer_container
250
+ if openlayers_container?
251
+ ol_viewer
252
+ elsif iiif_manifest_container?
253
+ iiif_manifest_viewer
254
+ else
255
+ leaflet_viewer
256
+ end
257
+ end
258
+
259
+ def openlayers_container?
260
+ return false unless @document
261
+ @document.item_viewer.pmtiles || @document.item_viewer.cog
262
+ end
263
+
264
+ def leaflet_viewer
265
+ tag.div(nil,
266
+ id: "map",
267
+ data: {
268
+ :map => "item", :protocol => @document.viewer_protocol.camelize,
269
+ :url => @document.viewer_endpoint,
270
+ "layer-id" => @document.wxs_identifier,
271
+ "map-geom" => @document.geometry.geojson,
272
+ "catalog-path" => search_catalog_path,
273
+ :available => document_available?,
274
+ :basemap => geoblacklight_basemap,
275
+ :leaflet_options => leaflet_options
276
+ })
277
+ end
278
+
279
+ def ol_viewer
280
+ tag.div(nil,
281
+ id: "ol-map",
282
+ data: {
283
+ :map => "item", :protocol => @document.viewer_protocol.camelize,
284
+ :url => @document.viewer_endpoint,
285
+ "layer-id" => @document.wxs_identifier,
286
+ "map-geom" => @document.geometry.geojson,
287
+ "catalog-path" => search_catalog_path,
288
+ :available => document_available?,
289
+ :basemap => geoblacklight_basemap,
290
+ :leaflet_options => leaflet_options
291
+ })
292
+ end
293
+
294
+ def iiif_manifest_container?
295
+ return false unless @document
296
+ @document&.item_viewer&.viewer_preference&.key?(:iiif_manifest)
297
+ end
298
+
299
+ def iiif_manifest_viewer
300
+ tag.div(nil, id: "clover-viewer", iiif_content: @document.viewer_endpoint)
301
+ end
245
302
  end
@@ -1,11 +1,36 @@
1
- <div class="modal-header">
2
- <h1><%= t('blacklight.tools.citation') %></h1>
3
- <button type="button" class="blacklight-modal-close close" data-dismiss="modal" aria-label="<%= t('blacklight.modal.close') %>">
4
- <span aria-hidden="true">&times;</span>
5
- </button>
6
- </div>
7
- <div class="modal-body">
8
- <% @documents&.each do |document| %>
9
- <%= document.geoblacklight_citation(solr_document_url(document)) %>
10
- <% end %>
1
+ <div data-controller="clipboard">
2
+ <div class="modal-header">
3
+ <h1><%= t('blacklight.tools.citation') %></h1>
4
+ <button type="button" class="blacklight-modal-close close" data-dismiss="modal" aria-label="<%= t('blacklight.modal.close') %>">
5
+ <span aria-hidden="true">&times;</span>
6
+ </button>
7
+ </div>
8
+ <div class="modal-body" data-clipboard-target="source">
9
+ <div class="alert alert-info d-none" role="alert">
10
+ <%= t('geoblacklight.clipboard.citation') %>
11
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
12
+ <span aria-hidden="true">&times;</span>
13
+ </button>
14
+ </div>
15
+
16
+ <%= t('blacklight.tools.citation_info') %>
17
+ <% @documents&.each do |document| %>
18
+ <%= document.geoblacklight_citation(solr_document_url(document)) %>
19
+ <% end %>
20
+ </div>
21
+ <div class="modal-footer">
22
+ <button type="button" class="btn btn-primary" data-action="copyToClipboard"><%= t('blacklight.modal.copy-citation') %></button>
23
+ <button type="button" class="btn btn-secondary" data-dismiss="modal"><%= t('blacklight.modal.close') %></button>
24
+ </div>
11
25
  </div>
26
+
27
+ <script>
28
+ $('[data-action="copyToClipboard"]').on('click', function() {
29
+ let text = $('[data-clipboard-target="source"]').text().trim()
30
+ navigator.clipboard.writeText(text)
31
+ .then(() => {
32
+ $('.alert').removeClass('d-none');
33
+ })
34
+ });
35
+ </script>
36
+
@@ -23,7 +23,6 @@
23
23
  </div>
24
24
  <% end %>
25
25
 
26
- <%= content_tag :div, id: 'map', aria: { label: t('geoblacklight.map.label') }, data: { map: 'item', protocol: document.viewer_protocol.camelize, url: document.viewer_endpoint, 'layer-id' => document.wxs_identifier, 'map-geom' => document.geometry.geojson, 'catalog-path'=> search_catalog_path, available: document_available?, inspect: show_attribute_table?, basemap: geoblacklight_basemap, leaflet_options: leaflet_options } do %>
27
- <% end %>
26
+ <%= viewer_container %>
28
27
  </div>
29
28
  </div>
@@ -4,5 +4,10 @@
4
4
  <label for='<%= reference.type%>_webservice'>
5
5
  <%= "#{formatted_name_reference(reference.type)}" %>
6
6
  </label>
7
- <input type='text' id='<%= reference.type%>_webservice' value='<%= reference.endpoint %>' readonly='readonly' class='form-control'>
7
+ <div class='input-group'>
8
+ <input type='text' id='<%= reference.type%>_webservice' value='<%= reference.endpoint %>' readonly='readonly' class='form-control'>
9
+ <div class="input-group-append">
10
+ <button button type="button" class="btn btn-primary" data-action="copyToClipboard" data-clipboard-value='<%= reference.endpoint %>'><%= t('blacklight.modal.copy') %></button>
11
+ </div>
12
+ </div>
8
13
  </div>
@@ -4,5 +4,10 @@
4
4
 
5
5
  <div class="form-group form-inline">
6
6
  <label for="wfs_abv_webservice" class="mr-2"><%= t('geoblacklight.references.wfs_abv')%> <code><%= t('geoblacklight.references.wfs_label')%></code></label>
7
- <input id="wfs_abv_webservice" type='text' value='<%= document.wxs_identifier %>' readonly='readonly' class="form-control">
7
+ <div class='input-group'>
8
+ <input id="wfs_abv_webservice" type='text' value='<%= document.wxs_identifier %>' readonly='readonly' class="form-control">
9
+ <div class="input-group-append">
10
+ <button button type="button" class="btn btn-primary" data-action="copyToClipboard" data-clipboard-value='<%= document.wxs_identifier %>'><%= t('blacklight.modal.copy') %></button>
11
+ </div>
12
+ </div>
8
13
  </div>
@@ -4,5 +4,10 @@
4
4
 
5
5
  <div class="form-group form-inline">
6
6
  <label for="wms_abv_webservice" class="mr-2"><%= t('geoblacklight.references.wms_abv')%> <code><%= t('geoblacklight.references.wms_label')%></code></label>
7
- <input id="wms_abv_webservice" type='text' value='<%= document.wxs_identifier %>' readonly='readonly' class="form-control">
7
+ <div class='input-group'>
8
+ <input id="wms_abv_webservice" type='text' value='<%= document.wxs_identifier %>' readonly='readonly' class="form-control" data-action="copyToClipboard" data-clipboard-value='<%= document.wxs_identifier %>'>
9
+ <div class="input-group-append">
10
+ <button button type="button" class="btn btn-primary" data-action="copyToClipboard" data-clipboard-value='<%= document.wxs_identifier %>'><%= t('blacklight.modal.copy') %></button>
11
+ </div>
12
+ </div>
8
13
  </div>
@@ -5,8 +5,24 @@
5
5
  </button>
6
6
  </div>
7
7
  <div class="modal-body web-services-modal-body">
8
+ <div class="alert alert-info d-none" role="alert">
9
+ <%= t('geoblacklight.clipboard.web_service') %>
10
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
11
+ <span aria-hidden="true">&times;</span>
12
+ </button>
13
+ </div>
8
14
  <%= render partial: 'web_services' %>
9
15
  </div>
10
16
  <div class="modal-footer">
11
17
  <button type="button" class="btn btn-default hide-without-js" data-dismiss="modal"><%= t('geoblacklight.references.services_close')%></button>
12
18
  </div>
19
+
20
+ <script>
21
+ $('[data-action="copyToClipboard"]').on('click', function() {
22
+ let text = $(this).data('clipboard-value');
23
+ navigator.clipboard.writeText(text)
24
+ .then(() => {
25
+ $('.alert').removeClass('d-none');
26
+ })
27
+ });
28
+ </script>
data/bin/vite ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'vite' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("vite_ruby", "vite")
@@ -4,6 +4,9 @@ en:
4
4
  location: 'Location'
5
5
  citation:
6
6
  retrieved_from: 'Retrieved from %{document_url}'
7
+ clipboard:
8
+ citation: Citation copied to clipboard
9
+ web_service: Web service copied to clipboard
7
10
  download:
8
11
  download: 'Download'
9
12
  download_link: 'Original %{download_format}'
@@ -14,8 +17,7 @@ en:
14
17
  hgl_close: 'Close'
15
18
  hgl_instructions: 'Please enter your email address. You will receive a link to download the layer when it is ready.' ## new variable
16
19
  hgl_email: 'Email'
17
- error: 'Sorry, the requested file could not be downloaded'
18
- error_with_url: 'Sorry, the requested file could not be downloaded. Try downloading it directly from: %{link}'
20
+ error: 'Sorry, the requested file could not be downloaded.'
19
21
  export_formats: 'Export Formats'
20
22
  export_link: 'Export %{download_format}'
21
23
  export_shapefile_link: 'Shapefile'
@@ -70,6 +72,8 @@ en:
70
72
  tilejson: 'TileJSON Document'
71
73
  xyz: 'XYZ Tiles'
72
74
  wmts: 'Web Map Tile Service'
75
+ pmtiles: 'PMTiles Layer'
76
+ cog: 'COG Layer'
73
77
  relations:
74
78
  member_of_ancestors: 'Belongs to collection...'
75
79
  member_of_descendants: 'Collection records...'
@@ -98,6 +102,9 @@ en:
98
102
  click_map: 'Click on map to inspect values'
99
103
  help_text:
100
104
  viewer_protocol:
105
+ cog:
106
+ title: COG Layer
107
+ content: A Cloud Optimized GeoTIFF
101
108
  dynamic_map_layer:
102
109
  title: ArcGIS Dynamic Map Layer
103
110
  content: An ArcGIS Dynamic Map Layer represents vector data (points, lines, and polygons). Map image layers are dynamically rendered image tiles.
@@ -119,6 +126,9 @@ en:
119
126
  oembed:
120
127
  title: Embedded Content
121
128
  content: This content is embedded through a third-party viewer.
129
+ pmtiles:
130
+ title: PMTiles Layer
131
+ content: A PMTiles layer is a single-file archive format for tiled data.
122
132
  tiled_map_layer:
123
133
  title: ArcGIS Tiled Map Layer
124
134
  content: An ArcGIS Tiled Map Layer Service displays set of web-accessible tiles that reside on a server.
data/config/vite.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "all": {
3
+ "sourceCodeDir": "app/frontend",
4
+ "publicOutputDir": "geoblacklight-vite",
5
+ "watchAdditionalPaths": []
6
+ },
7
+ "development": {
8
+ "autoBuild": true,
9
+ "port": 3037
10
+ },
11
+ "test": {
12
+ "autoBuild": true
13
+ }
14
+ }