blacklight_allmaps 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +109 -0
- data/Rakefile +98 -0
- data/app/assets/config/blacklight_allmaps_manifest.js +2 -0
- data/app/assets/images/blacklight/allmaps/allmaps-logo.svg +39 -0
- data/app/assets/images/blacklight/allmaps/blacklight-logo.png +0 -0
- data/app/assets/images/blacklight/allmaps/geoblacklight-logo.png +0 -0
- data/app/assets/images/blacklight/allmaps/logo.svg +39 -0
- data/app/assets/stylesheets/blacklight_allmaps/application.css +15 -0
- data/app/controllers/blacklight/allmaps/application_controller.rb +6 -0
- data/app/helpers/blacklight/allmaps/application_helper.rb +17 -0
- data/app/javascripts/map_controller.js +39 -0
- data/app/jobs/blacklight/allmaps/application_job.rb +6 -0
- data/app/jobs/blacklight/allmaps/store_sidecar_annotation.rb +39 -0
- data/app/mailers/blacklight/allmaps/application_mailer.rb +8 -0
- data/app/models/blacklight/allmaps/application_record.rb +7 -0
- data/app/models/blacklight/allmaps/sidecar.rb +23 -0
- data/app/models/concerns/blacklight/allmaps/solr_document.rb +14 -0
- data/app/views/allmaps/show/_blacklight.html.erb +213 -0
- data/app/views/allmaps/show/_geoblacklight.html.erb +47 -0
- data/app/views/allmaps/sidebar/_allmaps.html.erb +55 -0
- data/app/views/catalog/_show_default_viewer_container.html.erb +52 -0
- data/app/views/catalog/_show_main_content.html.erb +58 -0
- data/app/views/catalog/_show_sidebar.html.erb +5 -0
- data/app/views/catalog/_show_sidebar_blacklight.html.erb +10 -0
- data/app/views/catalog/_show_sidebar_geoblacklight.html.erb +20 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20240307155110_create_solr_document_sidecars.rb +15 -0
- data/lib/blacklight/allmaps/engine.rb +6 -0
- data/lib/blacklight/allmaps/rake_task.rb +7 -0
- data/lib/blacklight/allmaps/tasks/index.rake +77 -0
- data/lib/blacklight/allmaps/tasks/sidecars.rake +50 -0
- data/lib/blacklight/allmaps/version.rb +5 -0
- data/lib/blacklight/allmaps.rb +7 -0
- data/lib/generators/blacklight/allmaps/config_generator.rb +54 -0
- data/lib/generators/blacklight/allmaps/install_generator.rb +27 -0
- data/lib/generators/blacklight/allmaps/models_generator.rb +46 -0
- data/lib/generators/blacklight/allmaps/templates/manifest.js +5 -0
- metadata +229 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# ActiveRecord appendage for SolrDocuments
|
5
|
+
module Blacklight
|
6
|
+
module Allmaps
|
7
|
+
class Sidecar < ApplicationRecord
|
8
|
+
self.table_name = "blacklight_allmaps_sidecars"
|
9
|
+
|
10
|
+
alias_attribute :georeferenced, :annotated
|
11
|
+
|
12
|
+
before_save :set_allmaps_id
|
13
|
+
|
14
|
+
def solr_document
|
15
|
+
document_type.constantize.find(solr_document_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_allmaps_id
|
19
|
+
self.allmaps_id = Digest::SHA1.hexdigest(manifest_id)[0..15] if manifest_id.present?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Extends Blacklight::Solr::Document for Blacklight::Allmaps specific functionality
|
4
|
+
module Blacklight
|
5
|
+
module Allmaps
|
6
|
+
module SolrDocument
|
7
|
+
# Blacklight
|
8
|
+
# @TODO: Make this configurable
|
9
|
+
def iiif_manifest_url
|
10
|
+
self["iiif_manifest_url_ssi"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
<!-- Georeferenced Map -->
|
2
|
+
<h3 class="h6">Georeferenced Map</h3>
|
3
|
+
<div id="allmaps-map" class="mt-3 mb-3" style="height: 400px;"></div>
|
4
|
+
|
5
|
+
<!-- Leaflet -->
|
6
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.css">
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js"></script>
|
8
|
+
|
9
|
+
<!-- Leaflet Fullscreen -->
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/leaflet-fullscreen@1.0.2/dist/Leaflet.fullscreen.min.js"></script>
|
11
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-fullscreen@1.0.2/dist/leaflet.fullscreen.min.css">
|
12
|
+
|
13
|
+
<!-- Leaflet Layer Opacity -->
|
14
|
+
<script>
|
15
|
+
// Adopts the Mapbox opacity control into a Leaflet plugin
|
16
|
+
|
17
|
+
!function(global) {
|
18
|
+
'use strict';
|
19
|
+
|
20
|
+
L.Control.LayerOpacity = L.Control.extend({
|
21
|
+
initialize: function(layer) {
|
22
|
+
var options = { position: 'topleft' };
|
23
|
+
|
24
|
+
// check if layer is actually a layer group
|
25
|
+
if (typeof layer.getLayers !== 'undefined') {
|
26
|
+
|
27
|
+
// add first layer from layer group to options
|
28
|
+
options.layer = layer.getLayers()[0];
|
29
|
+
} else {
|
30
|
+
|
31
|
+
// add layer to options
|
32
|
+
options.layer = layer;
|
33
|
+
}
|
34
|
+
|
35
|
+
L.Util.setOptions(this, options);
|
36
|
+
},
|
37
|
+
|
38
|
+
onAdd: function(map) {
|
39
|
+
var container = L.DomUtil.create('div', 'opacity-control unselectable'),
|
40
|
+
controlArea = L.DomUtil.create('div', 'opacity-area', container),
|
41
|
+
handle = L.DomUtil.create('div', 'opacity-handle', container),
|
42
|
+
handleArrowUp = L.DomUtil.create('div', 'opacity-arrow-up', handle),
|
43
|
+
handleText = L.DomUtil.create('div', 'opacity-text', handle),
|
44
|
+
handleArrowDown = L.DomUtil.create('div', 'opacity-arrow-down', handle),
|
45
|
+
bottom = L.DomUtil.create('div', 'opacity-bottom', container);
|
46
|
+
|
47
|
+
L.DomEvent.stopPropagation(container);
|
48
|
+
L.DomEvent.disableClickPropagation(container);
|
49
|
+
|
50
|
+
this.setListeners(handle, bottom, handleText);
|
51
|
+
handle.style.top = handle.offsetTop - 13 + 50 + 'px';
|
52
|
+
handleText.innerHTML = parseInt(this.options.layer.options.opacity * 100) + '%';
|
53
|
+
return container;
|
54
|
+
},
|
55
|
+
|
56
|
+
setListeners: function(handle, bottom, handleText) {
|
57
|
+
var _this = this,
|
58
|
+
start = false,
|
59
|
+
startTop;
|
60
|
+
|
61
|
+
L.DomEvent.on(document, 'mousemove', function(e) {
|
62
|
+
if (!start) return;
|
63
|
+
var percentInverse = Math.max(0, Math.min(200, startTop + parseInt(e.clientY, 10) - start)) / 2;
|
64
|
+
handle.style.top = ((percentInverse * 2) - 13) + 'px';
|
65
|
+
handleText.innerHTML = Math.round((1 - (percentInverse / 100)) * 100) + '%';
|
66
|
+
bottom.style.height = Math.max(0, (((100 - percentInverse) * 2) - 13)) + 'px';
|
67
|
+
bottom.style.top = Math.min(200, (percentInverse * 2) + 13) + 'px';
|
68
|
+
_this.options.layer.setOpacity(1 - (percentInverse / 100));
|
69
|
+
});
|
70
|
+
|
71
|
+
L.DomEvent.on(handle, 'mousedown', function(e) {
|
72
|
+
start = parseInt(e.clientY, 10);
|
73
|
+
startTop = handle.offsetTop - 12;
|
74
|
+
return false;
|
75
|
+
});
|
76
|
+
|
77
|
+
L.DomEvent.on(document, 'mouseup', function(e) {
|
78
|
+
start = null;
|
79
|
+
});
|
80
|
+
}
|
81
|
+
});
|
82
|
+
}(this);
|
83
|
+
</script>
|
84
|
+
<style>
|
85
|
+
.leaflet-control.opacity-control {
|
86
|
+
background-color: #a9acb1;
|
87
|
+
border-radius: 15px;
|
88
|
+
color: black;
|
89
|
+
font: bold 18px 'Lucida Console', Monaco, monospace;
|
90
|
+
display: block;
|
91
|
+
height: 200px;
|
92
|
+
left: 11px;
|
93
|
+
position: relative;
|
94
|
+
top: 15px;
|
95
|
+
width: 5px;
|
96
|
+
|
97
|
+
.opacity-handle {
|
98
|
+
background-color: #fff;
|
99
|
+
border-radius: 4px;
|
100
|
+
border: 1px solid #eee;
|
101
|
+
cursor: ns-resize;
|
102
|
+
font-size: 10px;
|
103
|
+
height: 26px;
|
104
|
+
left: -11px;
|
105
|
+
line-height: 26px;
|
106
|
+
position: absolute;
|
107
|
+
text-align: center;
|
108
|
+
top: 0;
|
109
|
+
width: 26px;
|
110
|
+
@include map-control-shadow;
|
111
|
+
|
112
|
+
&:hover {
|
113
|
+
background-color: #f4f4f4;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
.opacity-arrow-up {
|
118
|
+
color: #aaa;
|
119
|
+
position: absolute;
|
120
|
+
top: -11px;
|
121
|
+
text-align: center;
|
122
|
+
width: 100%;
|
123
|
+
|
124
|
+
&:before {
|
125
|
+
content: '=';
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
.opacity-arrow-down {
|
130
|
+
bottom: -10px;
|
131
|
+
color: #aaa;
|
132
|
+
position: absolute;
|
133
|
+
text-align: center;
|
134
|
+
width: 100%;
|
135
|
+
|
136
|
+
&:before {
|
137
|
+
content: '=';
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
.opacity-bottom {
|
142
|
+
background-color: #017afd;
|
143
|
+
border-radius: 15px;
|
144
|
+
display: block;
|
145
|
+
height: 137px;
|
146
|
+
left: 0px;
|
147
|
+
position: relative;
|
148
|
+
top: 63px;
|
149
|
+
width: 5px;
|
150
|
+
}
|
151
|
+
|
152
|
+
// Area underneath slider to prevent unintentioned map clicks
|
153
|
+
.opacity-area {
|
154
|
+
padding: 14px;
|
155
|
+
cursor: default;
|
156
|
+
height: 200px;
|
157
|
+
left: -11px;
|
158
|
+
position: absolute;
|
159
|
+
top: 0px;
|
160
|
+
width: 20px;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
.opacity-control.unselectable {
|
165
|
+
-webkit-touch-callout: none;
|
166
|
+
-webkit-user-select: none;
|
167
|
+
-khtml-user-select: none;
|
168
|
+
-moz-user-select: none;
|
169
|
+
-ms-user-select: none;
|
170
|
+
user-select: none;
|
171
|
+
}
|
172
|
+
</style>
|
173
|
+
|
174
|
+
<!-- Allmaps -->
|
175
|
+
<script src="https://cdn.jsdelivr.net/npm/@allmaps/leaflet/dist/bundled/allmaps-leaflet-1.9.umd.js"></script>
|
176
|
+
|
177
|
+
<script>
|
178
|
+
document.addEventListener("DOMContentLoaded", () => {
|
179
|
+
const element = document.getElementById("allmaps-map");
|
180
|
+
|
181
|
+
const map = L.map("allmaps-map", {
|
182
|
+
center: [0, 0],
|
183
|
+
zoom: 15,
|
184
|
+
zoomAnimationThreshold: 1
|
185
|
+
});
|
186
|
+
|
187
|
+
// Basemap and Attribution
|
188
|
+
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
189
|
+
attribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors",
|
190
|
+
maxZoom: 18
|
191
|
+
}).addTo(map);
|
192
|
+
|
193
|
+
// Fullscreen control
|
194
|
+
map.addControl(new L.Control.Fullscreen({
|
195
|
+
position: "topright"
|
196
|
+
}));
|
197
|
+
|
198
|
+
const annotationUrl = "https://annotations.allmaps.org/manifests/<%= document.sidecar_allmaps.allmaps_id %>";
|
199
|
+
const warpedMapLayer = new Allmaps.WarpedMapLayer(annotationUrl)
|
200
|
+
.addTo(map);
|
201
|
+
|
202
|
+
// Layer opacity control
|
203
|
+
map.addControl(new L.Control.LayerOpacity(warpedMapLayer));
|
204
|
+
|
205
|
+
map.on(
|
206
|
+
"warpedmapadded",
|
207
|
+
(event) => {
|
208
|
+
map.fitBounds(warpedMapLayer.getBounds());
|
209
|
+
},
|
210
|
+
map
|
211
|
+
);
|
212
|
+
});
|
213
|
+
</script>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<!-- Georeferenced Map -->
|
2
|
+
<h3 class="h6">Georeferenced Map</h3>
|
3
|
+
<div id="allmaps-map" data-map-geom="<%= document.geometry.geojson %>" style="height: 400px;"></div>
|
4
|
+
|
5
|
+
<script src="https://cdn.jsdelivr.net/npm/@allmaps/leaflet/dist/bundled/allmaps-leaflet-1.9.umd.js"></script>
|
6
|
+
<script>
|
7
|
+
document.addEventListener("DOMContentLoaded", () => {
|
8
|
+
const element = document.getElementById("allmaps-map");
|
9
|
+
const value = element.getAttribute("data-map-geom");
|
10
|
+
const layer = L.geoJSON();
|
11
|
+
layer.addData(JSON.parse(value));
|
12
|
+
const bounds = layer.getBounds();
|
13
|
+
const annotationUrl = "https://annotations.allmaps.org/manifests/<%= document.sidecar_allmaps.allmaps_id %>";
|
14
|
+
const warpedMapLayer = new Allmaps.WarpedMapLayer(annotationUrl);
|
15
|
+
const geoTab = document.getElementById("georeferenced-tab");
|
16
|
+
const map = L.map("allmaps-map");
|
17
|
+
|
18
|
+
// Basemap and Attribution
|
19
|
+
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
20
|
+
attribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors",
|
21
|
+
maxZoom: 18
|
22
|
+
}).addTo(map);
|
23
|
+
|
24
|
+
// Fullscreen control
|
25
|
+
map.addControl(new L.Control.Fullscreen({
|
26
|
+
position: "topright"
|
27
|
+
}));
|
28
|
+
|
29
|
+
// Layer opacity control
|
30
|
+
map.addControl(new L.Control.LayerOpacity(warpedMapLayer));
|
31
|
+
|
32
|
+
// Leaflet: Multiple maps on the same page is a challenge...
|
33
|
+
// 1. Need to watch the page DOM mutate
|
34
|
+
// 2. When the georeferenced tab is visible, invalidate the map size
|
35
|
+
// 3. Add the WarpedMapLayer to the map
|
36
|
+
// 4. Fit the map to the GeoJSON bounds
|
37
|
+
const observer = new MutationObserver(function(){
|
38
|
+
if(geoTab.style.display !== "none"){
|
39
|
+
map.invalidateSize();
|
40
|
+
warpedMapLayer.addTo(map);
|
41
|
+
map.fitBounds(bounds);
|
42
|
+
}
|
43
|
+
});
|
44
|
+
|
45
|
+
observer.observe(geoTab, { attributes: true });
|
46
|
+
});
|
47
|
+
</script>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<div class="card mb-3 mt-3" id="allmaps-sidebar" data-iiif-manifest="<%=@document.iiif_manifest_url %>">
|
2
|
+
<div class="card-header">
|
3
|
+
<h2 class="mb-0 h6"><%= t('home.georeferencing') %></h2>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div class="card-body" id="georeferencing">
|
7
|
+
<% if @document.sidecar_allmaps.georeferenced? %>
|
8
|
+
<div id="georef_present">
|
9
|
+
<%= link_to 'View this georeferenced item', 'https://viewer.allmaps.org/?url=https://annotations.allmaps.org/?url=' + @document.iiif_manifest_url, target: '_blank' %>
|
10
|
+
</div>
|
11
|
+
<% else %>
|
12
|
+
<%# content will be updated via updateGeorefLinks(); %>
|
13
|
+
<div id="georef_loading">
|
14
|
+
Loading...
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="card-footer text-muted">
|
20
|
+
<small>
|
21
|
+
<a href="https://allmaps.org" class="text-decoration-none text-dark">
|
22
|
+
<%= image_tag 'blacklight/allmaps/allmaps-logo.svg', alt: 'Allmaps', height:"20" %>
|
23
|
+
Allmaps
|
24
|
+
</a>
|
25
|
+
</small>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<script type="text/javascript">
|
30
|
+
document.addEventListener('DOMContentLoaded', () => {
|
31
|
+
const updateGeorefLinks = async () => {
|
32
|
+
const manifestUrl = document.getElementById('allmaps-sidebar').getAttribute('data-iiif-manifest');
|
33
|
+
const georefDiv = document.getElementById('georeferencing');
|
34
|
+
const annotationUrlByIIIFUri = `https://annotations.allmaps.org/?url=${manifestUrl}`;
|
35
|
+
|
36
|
+
try {
|
37
|
+
const response = await fetch(annotationUrlByIIIFUri);
|
38
|
+
if (!response.ok) {
|
39
|
+
georefDiv.innerHTML = `<a href="https://editor.allmaps.org/#/collection?url=${manifestUrl}">Georeference this item</a>`;
|
40
|
+
} else {
|
41
|
+
const annotationUrl = response.url;
|
42
|
+
georefDiv.innerHTML = `<a href="https://viewer.allmaps.org/?url=${annotationUrl}">View this georeferenced item</a>`;
|
43
|
+
|
44
|
+
// TODO: Ping Blacklight::Allmaps::AnnotationsController to save the annotation data
|
45
|
+
|
46
|
+
|
47
|
+
}
|
48
|
+
} catch (error) {
|
49
|
+
console.error("Fetch error:", error);
|
50
|
+
}
|
51
|
+
};
|
52
|
+
|
53
|
+
updateGeorefLinks();
|
54
|
+
});
|
55
|
+
</script>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<% document ||= @document %>
|
2
|
+
<div class='row'>
|
3
|
+
<div id='viewer-container' class="col-md-12">
|
4
|
+
|
5
|
+
<span class="sr-only">Georeferenced: <%= document.sidecar_allmaps.georeferenced? %></span>
|
6
|
+
|
7
|
+
<ul class="nav nav-tabs mt-3" id="myTab" role="tablist">
|
8
|
+
<li class="nav-item" role="presentation">
|
9
|
+
<button class="nav-link active" id="item-viewer" data-toggle="tab" data-target="#item-viewer-tab" type="button" role="tab" aria-controls="home" aria-selected="true">Item Viewer</button>
|
10
|
+
</li>
|
11
|
+
<% if document.sidecar_allmaps.georeferenced? %>
|
12
|
+
<li class="nav-item" role="presentation">
|
13
|
+
<button class="nav-link" id="georeferenced-viewer" data-toggle="tab" data-target="#georeferenced-tab" type="button" role="tab" aria-controls="profile" aria-selected="false">Georeferenced Map</button>
|
14
|
+
</li>
|
15
|
+
<% end %>
|
16
|
+
</ul>
|
17
|
+
|
18
|
+
<div class="tab-content mt-3">
|
19
|
+
<div class="tab-pane fade show active" id="item-viewer-tab" role="tabpanel" aria-labelledby="item-viewer-tab">
|
20
|
+
<% if show_help_text?('viewer_protocol', document.viewer_protocol) %>
|
21
|
+
<%= render_help_text_entry('viewer_protocol', document.viewer_protocol) %>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<% if document.item_viewer.index_map %>
|
25
|
+
<div class="index-map-legend">
|
26
|
+
<div class="index-map-legend-info">
|
27
|
+
<span class="index-map-legend-default"></span>
|
28
|
+
<p><span class="sr-only">Green tile indicates </span>Map held by collection</p>
|
29
|
+
</div>
|
30
|
+
<div class="index-map-legend-info">
|
31
|
+
<span class="index-map-legend-unavailable"></span>
|
32
|
+
<p><span class="sr-only">Yellow tile indicates </span>Map not held by collection</p>
|
33
|
+
</div>
|
34
|
+
<div class="index-map-legend-info">
|
35
|
+
<span class="index-map-legend-selected"></span>
|
36
|
+
<p><span class="sr-only">Blue tile indicates </span>Selected map tile</p>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
<% end %>
|
40
|
+
|
41
|
+
<%= 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 %>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<% if document.sidecar_allmaps.georeferenced? %>
|
46
|
+
<div class="tab-pane fade" id="georeferenced-tab" role="tabpanel" aria-labelledby="georeferenced-tab">
|
47
|
+
<%= render partial: 'allmaps/show/geoblacklight', locals: { document: document } %>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<% if Blacklight::VERSION.to_i > 8 %>
|
2
|
+
<%= render blacklight_config.track_search_session.item_pagination_component.new(search_context: @search_context, search_session: search_session, current_document: @document) if blacklight_config.track_search_session.item_pagination_component %>
|
3
|
+
<% @page_title = t('blacklight.search.show.title', document_title: document_presenter(@document).html_title, application_name: application_name).html_safe %>
|
4
|
+
<% content_for(:head) { render_link_rel_alternates } %>
|
5
|
+
|
6
|
+
<% document_component = blacklight_config.view_config(:show).document_component -%>
|
7
|
+
<%= render (document_component).new(document_component.collection_parameter => document_presenter(@document), component: :div, show: true, partials: blacklight_config.view_config(:show).partials) do |component| %>
|
8
|
+
<% component.with_title(as: 'h1', classes: '', link_to_document: false, actions: false) %>
|
9
|
+
<% component.with_footer do %>
|
10
|
+
|
11
|
+
<% unless defined?(Geoblacklight) %>
|
12
|
+
<% if @document.sidecar_allmaps.georeferenced? %>
|
13
|
+
<%= render partial: 'allmaps/show/blacklight', locals: { document: @document } %>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% if @document.respond_to?(:export_as_openurl_ctx_kev) %>
|
18
|
+
<!-- COinS, for Zotero among others. -->
|
19
|
+
<span class="Z3988" title="<%= @document.export_as_openurl_ctx_kev(document_presenter(@document).display_type) %>"></span>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<% else %>
|
25
|
+
<%= render(Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session)) if search_session['document_id'] == @document.id %>
|
26
|
+
|
27
|
+
<% @page_title = t('blacklight.search.show.title', document_title: Deprecation.silence(Blacklight::BlacklightHelperBehavior) { document_show_html_title }, application_name: application_name).html_safe %>
|
28
|
+
<% content_for(:head) { render_link_rel_alternates } %>
|
29
|
+
|
30
|
+
<%= render (blacklight_config.view_config(:show).document_component || Blacklight::DocumentComponent).new(presenter: document_presenter(@document), component: :div, title_component: :h1, show: true) do |component| %>
|
31
|
+
<% component.with_footer do %>
|
32
|
+
|
33
|
+
<% unless defined?(Geoblacklight) %>
|
34
|
+
<% if @document.sidecar_allmaps.georeferenced? %>
|
35
|
+
<%= render partial: 'allmaps/show/blacklight', locals: { document: @document } %>
|
36
|
+
<% end %>
|
37
|
+
<% end %>
|
38
|
+
|
39
|
+
<% if @document.respond_to?(:export_as_openurl_ctx_kev) %>
|
40
|
+
<!--
|
41
|
+
// COinS, for Zotero among others.
|
42
|
+
// This document_partial_name(@document) business is not quite right,
|
43
|
+
// but has been there for a while.
|
44
|
+
-->
|
45
|
+
<span class="Z3988" title="<%= @document.export_as_openurl_ctx_kev(Deprecation.silence(Blacklight::RenderPartialsHelperBehavior) { document_partial_name(@document) }) %>"></span>
|
46
|
+
<% end %>
|
47
|
+
<% end %>
|
48
|
+
|
49
|
+
<%# Use :body for complete backwards compatibility (overriding the component body markup),
|
50
|
+
but if the app explicitly opted-in to components, make the partials data available as :partials to ease migrations pain %>
|
51
|
+
<% component.public_send(blacklight_config.view_config(:show).document_component.blank? && blacklight_config.view_config(:show).partials.any? ? :with_body : :with_partial) do %>
|
52
|
+
<div id="doc_<%= @document.id.to_s.parameterize %>">
|
53
|
+
<%= render_document_partials @document, blacklight_config.view_config(:show).partials, component: component %>
|
54
|
+
</div>
|
55
|
+
<% end %>
|
56
|
+
<% end %>
|
57
|
+
<% end %>
|
58
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% if Blacklight::VERSION.to_i > 8 %>
|
2
|
+
<% presenter = document_presenter(document) %>
|
3
|
+
<%= render presenter.view_config.sidebar_component.new(presenter: presenter) %>
|
4
|
+
<% else %>
|
5
|
+
<%= render 'show_tools', document: @document %>
|
6
|
+
<%= render(Blacklight::Document::MoreLikeThisComponent.new(document: @document)) %>
|
7
|
+
<% if georeferenceable? %>
|
8
|
+
<%= render :partial => "allmaps/sidebar/allmaps" %>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= render :partial => 'show_sidebar_static_map' if render_sidebar_map?(@document) %>
|
2
|
+
<%= render :partial => 'show_tools' %>
|
3
|
+
|
4
|
+
<% if georeferenceable? %>
|
5
|
+
<%= render :partial => "allmaps/sidebar/allmaps" %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<div class="sidebar-buttons">
|
9
|
+
<%= render :partial => "show_web_services" %>
|
10
|
+
<%= render :partial => "show_downloads" %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<% unless @document.more_like_this.empty? %>
|
14
|
+
<div class="card">
|
15
|
+
<div class="card-header">More Like This</div>
|
16
|
+
<div class="card-body">
|
17
|
+
<%= render :collection => @document.more_like_this, :partial => 'show_more_like_this', :as => :document %>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateSolrDocumentSidecars < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :blacklight_allmaps_sidecars do |t|
|
4
|
+
t.string :solr_document_id, index: true
|
5
|
+
t.string :document_type, default: "SolrDocument"
|
6
|
+
t.string :manifest_id, index: true
|
7
|
+
t.boolean :annotated, default: false
|
8
|
+
t.string :allmaps_id, index: true
|
9
|
+
t.text :iiif_manifest
|
10
|
+
t.text :allmaps_annotation
|
11
|
+
t.bigint :solr_version
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "blacklight"
|
4
|
+
|
5
|
+
namespace :blacklight_allmaps do
|
6
|
+
namespace :index do
|
7
|
+
desc "Index - add Allmaps fixture data to Blacklight solr"
|
8
|
+
task :bl_fixtures do
|
9
|
+
# @TODO: JSON works when pasted into Solr, but fails here?
|
10
|
+
docs = Dir["spec/fixtures/solr_documents/bl_*.json"].map { |f| JSON.parse File.read(f) }.flatten
|
11
|
+
Blacklight.default_index.connection.add docs
|
12
|
+
Blacklight.default_index.connection.commit
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Index - add Allmaps fixture data to GeoBlacklight solr"
|
16
|
+
task :gbl_fixtures do
|
17
|
+
docs = Dir["spec/fixtures/solr_documents/gbl_*.json"].map { |f| JSON.parse File.read(f) }.flatten
|
18
|
+
Blacklight.default_index.connection.add docs
|
19
|
+
Blacklight.default_index.connection.commit
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Index - add Allmaps facet data to GeoBlacklight solr"
|
23
|
+
task gbl_georeferenced_facet: [:environment] do
|
24
|
+
# Steps
|
25
|
+
# 1. Use cursor to paginate all documents in Solr
|
26
|
+
# 2. Determine which documents have georeferenced data
|
27
|
+
# 3. Clean JSON for re-indexing
|
28
|
+
# 4. Add gbl_georeferenced_b values
|
29
|
+
# 5. Re-index the georeferenced documents
|
30
|
+
|
31
|
+
# 1. Get all the documents from Solr
|
32
|
+
cursor_mark = "*"
|
33
|
+
loop do
|
34
|
+
response = Blacklight.default_index.connection.get(
|
35
|
+
"select", params: {
|
36
|
+
q: "*:*", # all docs
|
37
|
+
fl: "*", # all fields
|
38
|
+
cursorMark: cursor_mark, # use the cursor mark to handle paging
|
39
|
+
rows: 1000,
|
40
|
+
sort: "id asc" # must sort by id to use the cursor mark
|
41
|
+
}
|
42
|
+
)
|
43
|
+
|
44
|
+
response["response"]["docs"].each do |doc|
|
45
|
+
# 2. Determine which documents have georeferenced data
|
46
|
+
solr_document = SolrDocument.find(doc["id"])
|
47
|
+
if solr_document.sidecar.present? && solr_document.sidecar.annotated?
|
48
|
+
|
49
|
+
# 3. Clean JSON for re-indexing
|
50
|
+
keys_for_deletion = %w[
|
51
|
+
_version_
|
52
|
+
timestamp
|
53
|
+
solr_bboxtype
|
54
|
+
solr_bboxtype__minX
|
55
|
+
solr_bboxtype__minY
|
56
|
+
solr_bboxtype__maxX
|
57
|
+
solr_bboxtype__maxY
|
58
|
+
]
|
59
|
+
|
60
|
+
cleaned_doc = doc.except!(*keys_for_deletion)
|
61
|
+
|
62
|
+
# 4. Add gbl_georeferenced_b value
|
63
|
+
# @TODO: add allmaps_id?
|
64
|
+
cleaned_doc["gbl_georeferenced_b"] = true
|
65
|
+
|
66
|
+
# 5. Re-index the georeferenced documents
|
67
|
+
Blacklight.default_index.connection.add cleaned_doc
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
break if response["nextCursorMark"] == cursor_mark # this means the result set is finished
|
72
|
+
cursor_mark = response["nextCursorMark"]
|
73
|
+
end
|
74
|
+
Blacklight.default_index.connection.commit
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :blacklight_allmaps do
|
4
|
+
namespace :sidecars do
|
5
|
+
namespace :harvest do
|
6
|
+
desc "Sidecars - Harvest: Crawl SolrDocuments to store Allmaps data locally"
|
7
|
+
task allmaps: [:environment] do
|
8
|
+
cursor_mark = "*"
|
9
|
+
loop do
|
10
|
+
response = Blacklight.default_index.connection.get(
|
11
|
+
"select", params: {
|
12
|
+
q: "*:*", # all docs
|
13
|
+
fl: "id", # just id field
|
14
|
+
cursorMark: cursor_mark, # use the cursor mark to handle paging
|
15
|
+
rows: 1000,
|
16
|
+
sort: "id asc" # must sort by id to use the cursor mark
|
17
|
+
}
|
18
|
+
)
|
19
|
+
|
20
|
+
response["response"]["docs"].each do |doc|
|
21
|
+
puts "Harvesting Allmaps data for #{doc["id"]}"
|
22
|
+
Blacklight::Allmaps::StoreSidecarAnnotation.perform_later(doc["id"])
|
23
|
+
end
|
24
|
+
|
25
|
+
break if response["nextCursorMark"] == cursor_mark # this means the result set is finished
|
26
|
+
cursor_mark = response["nextCursorMark"]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Sidecars - Purage all: Destroy all harvested images and sidecar AR objects"
|
32
|
+
task purge_all: [:environment] do
|
33
|
+
Blacklight::Allmaps::Sidecar.destroy_all
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Sidecars - Purge orphans: Destroy orphaned sidecar Active Record objects"
|
37
|
+
# When a Sidecar object exists,
|
38
|
+
# but it's corresponding SolrDocument is no longer in the Solr index.
|
39
|
+
task purge_orphans: [:environment] do
|
40
|
+
# Remove all sidecars that have no corresponding SolrDocument
|
41
|
+
sidecars = Blacklight::Allmaps::Sidecar.all
|
42
|
+
sidecars.each do |sc|
|
43
|
+
::SolrDocument.find(sc.document_id)
|
44
|
+
rescue
|
45
|
+
sc.destroy
|
46
|
+
puts "orphaned / #{sc.document_id} / destroyed"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|