blacklight-maps 0.0.1 → 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 +4 -4
- data/.travis.yml +5 -1
- data/Gemfile +10 -2
- data/README.md +19 -9
- data/app/assets/javascripts/blacklight-maps/blacklight-maps-browse.js +129 -118
- data/app/helpers/blacklight_maps_helper.rb +22 -32
- data/app/views/catalog/_document_maps.html.erb +3 -3
- data/lib/blacklight/maps.rb +2 -0
- data/lib/blacklight/maps/engine.rb +5 -4
- data/lib/blacklight/maps/export.rb +111 -0
- data/lib/blacklight/maps/geometry.rb +35 -0
- data/lib/blacklight/maps/version.rb +1 -1
- data/solr_conf/conf/schema.xml +50 -41
- data/solr_conf/conf/solrconfig.xml +40 -40
- data/spec/features/maps_spec.rb +76 -47
- data/spec/fixtures/sample_solr_documents.yml +39 -21
- data/spec/helpers/blacklight_maps_helper_spec.rb +48 -14
- data/spec/lib/blacklight/maps/export_spec.rb +69 -0
- data/spec/lib/blacklight/maps/geometry_spec.rb +19 -0
- data/spec/spec_helper.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5cede804f84631bbe7ec2c80370c8f0cbf88c31
|
4
|
+
data.tar.gz: 853cd08e29850b439c5d7773c48118b835216d28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4181770fa39430c201156ab3b9d83cffc0208a58c530e6209fea4d5f4ce56b3e52d04ee7a49059be6cd84ae65208589afdc00f6e80de51a082652b1f8d33cb9e
|
7
|
+
data.tar.gz: defd9c711b0f083449e952726548532d078b639c822fc606ab4fd3d79f3eaf3abeb5658a022a52041dca3981a2363eecfbb284dcaaa37f534e89d27b5057a0d7
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -6,11 +6,19 @@ gemspec
|
|
6
6
|
gem 'simplecov', require: false
|
7
7
|
gem 'coveralls', require: false
|
8
8
|
|
9
|
-
|
9
|
+
# If we don't specify 2.11.0 we'll end up with sprockets 2.12.0 in the main
|
10
|
+
# Gemfile.lock but since sass-rails gets generated (rails new) into the test app
|
11
|
+
# it'll want sprockets 2.11.0 and we'll have a conflict
|
12
|
+
gem 'sprockets', '2.11.0'
|
13
|
+
|
14
|
+
# # If we don't specify 3.2.15 we'll end up with sass 3.3.2 in the main
|
15
|
+
# # Gemfile.lock but since sass-rails gets generated (rails new) into the test app
|
16
|
+
# # it'll want sass 3.2.0 and we'll have a conflict
|
17
|
+
gem 'sass', '~> 3.2.0'
|
10
18
|
|
11
19
|
|
12
20
|
file = File.expand_path("Gemfile", ENV['ENGINE_CART_DESTINATION'] || ENV['RAILS_ROOT'] || File.expand_path("../spec/internal", __FILE__))
|
13
21
|
if File.exists?(file)
|
14
22
|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
|
15
23
|
instance_eval File.read(file)
|
16
|
-
end
|
24
|
+
end
|
data/README.md
CHANGED
@@ -25,14 +25,20 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
Blacklight-Maps adds a map view capability for a results set that contains geospatial coordinates (latitude/longitude).
|
27
27
|
|
28
|
-
For now, Blacklight-Maps requires that your
|
28
|
+
For now, Blacklight-Maps requires that your Solr index include one of the following two types of fields:
|
29
29
|
|
30
|
-
A
|
30
|
+
1. A `location_rpt` field that contains a bounding box for the document. For more on `location_rpt` see [Solr help](https://cwiki.apache.org/confluence/display/solr/Spatial+Search). This field can be multivalued.
|
31
|
+
```
|
32
|
+
place_bbox: 44.0318907 25.0594286 63.3333366 39.7816755
|
33
|
+
# minX minY maxX maxY
|
34
|
+
```
|
35
|
+
|
36
|
+
2. A field containing placenames with latitude and longitude coordinates delimited by `-|-`. The delimiter can be configured in `app/controllers/catalog_controller.rb`. This field can be multivalued.
|
31
37
|
```
|
32
38
|
placename_coords:
|
33
|
-
- China
|
34
|
-
- Tibet
|
35
|
-
- India
|
39
|
+
- China-|-35.86166-|-104.195397
|
40
|
+
- Tibet-|-29.646923-|-91.117212
|
41
|
+
- India-|-20.593684-|-78.96288
|
36
42
|
```
|
37
43
|
|
38
44
|
Note: We are looking at implementing support for additional fields.
|
@@ -42,14 +48,15 @@ Note: We are looking at implementing support for additional fields.
|
|
42
48
|
#### Required
|
43
49
|
Blacklight-Maps expects you to provide:
|
44
50
|
|
45
|
-
-
|
51
|
+
- the type of location field you are using, `placename_coord` or `bbox` (`bbox` is default)
|
52
|
+
- a field to map the placename coordinates or bbox field
|
46
53
|
|
47
54
|
#### Optional
|
48
55
|
|
49
56
|
- the maxZoom [property of the map](http://leafletjs.com/reference.html#map-maxzoom)
|
50
57
|
- a [tileLayer url](http://leafletjs.com/reference.html#tilelayer-l.tilelayer) to change the basemap
|
51
58
|
- an [attribution string](http://leafletjs.com/reference.html#tilelayer-attribution) to describe the basemap layer
|
52
|
-
|
59
|
+
- a custom delimiter field (used to delimit placename_coord values)
|
53
60
|
|
54
61
|
All of these options can easily be configured in `CatalogController.rb` in the `config` block.
|
55
62
|
|
@@ -57,16 +64,19 @@ All of these options can easily be configured in `CatalogController.rb` in the `
|
|
57
64
|
...
|
58
65
|
configure_blacklight do |config|
|
59
66
|
## Default parameters to send to solr for all search-like requests. See also SolrHelper#solr_search_params
|
60
|
-
config.default_solr_params = {
|
67
|
+
config.default_solr_params = {
|
61
68
|
:qt => 'search',
|
62
69
|
:rows => 10,
|
63
70
|
:fl => '*'
|
64
71
|
}
|
65
72
|
|
66
73
|
## Default values
|
67
|
-
config.view.maps.
|
74
|
+
config.view.maps.type = "bbox" # also accepts 'placename_coord' to use the placename coordinate type
|
75
|
+
config.view.maps.bbox_field = "place_bbox"
|
76
|
+
config.view.maps.placename_coord_field = "placename_coords"
|
68
77
|
config.view.maps.tileurl = "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
69
78
|
config.view.maps.attribution = 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
|
79
|
+
config.view.maps.placename_coord_delimiter = '-|-'
|
70
80
|
...
|
71
81
|
|
72
82
|
```
|
@@ -1,141 +1,152 @@
|
|
1
|
-
|
1
|
+
;(function( $ ) {
|
2
|
+
|
3
|
+
$.fn.blacklight_leaflet_map = function(geojson_docs, arg_opts) {
|
4
|
+
var map, sidebar, markers, geoJsonLayer, currentLayer;
|
5
|
+
|
6
|
+
// Configure default options and those passed via the constructor options
|
7
|
+
var options = $.extend({
|
8
|
+
tileurl : 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
9
|
+
mapattribution : 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
|
10
|
+
sidebar: 'blacklight-map-sidebar'
|
11
|
+
}, arg_opts );
|
12
|
+
|
13
|
+
// Extend options from data-attributes
|
14
|
+
$.extend(options, this.data());
|
15
|
+
|
16
|
+
// Display the map
|
17
|
+
this.each(function() {
|
18
|
+
options.id = this.id;
|
19
|
+
|
20
|
+
// Setup Leaflet map
|
21
|
+
map = L.map(this.id).setView([0,0], 2);
|
22
|
+
L.tileLayer(options.tileurl, {
|
23
|
+
attribution: options.mapattribution,
|
24
|
+
maxZoom: options.maxzoom
|
25
|
+
}).addTo(map);
|
26
|
+
|
27
|
+
// Initialize sidebar
|
28
|
+
sidebar = L.control.sidebar(options.sidebar, {
|
29
|
+
position: 'right',
|
30
|
+
autoPan: false
|
31
|
+
});
|
2
32
|
|
3
|
-
|
33
|
+
// Adds leaflet-sidebar control to map
|
34
|
+
map.addControl(sidebar);
|
4
35
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
position: 'right',
|
22
|
-
autoPan: false
|
23
|
-
});
|
24
|
-
|
25
|
-
// Adds leaflet-sidebar control to map (object)
|
26
|
-
map.addControl(sidebar);
|
27
|
-
|
28
|
-
// Create a marker cluster object and set options
|
29
|
-
markers = new L.MarkerClusterGroup({
|
30
|
-
showCoverageOnHover: false,
|
31
|
-
spiderfyOnMaxZoom: false,
|
32
|
-
singleMarkerMode: true,
|
33
|
-
animateAddingMarkers: true
|
34
|
-
});
|
35
|
-
|
36
|
-
geoJsonLayer = L.geoJson(geojson_docs, {
|
37
|
-
onEachFeature: function(feature, layer){
|
38
|
-
layer.defaultOptions.title = feature.properties.placename;
|
39
|
-
layer.on('click', function(e){
|
40
|
-
if (sidebar.isVisible()){
|
41
|
-
sidebar.hide();
|
36
|
+
// Create a marker cluster object and set options
|
37
|
+
markers = new L.MarkerClusterGroup({
|
38
|
+
showCoverageOnHover: false,
|
39
|
+
spiderfyOnMaxZoom: false,
|
40
|
+
singleMarkerMode: true,
|
41
|
+
animateAddingMarkers: true
|
42
|
+
});
|
43
|
+
|
44
|
+
geoJsonLayer = L.geoJson(geojson_docs, {
|
45
|
+
onEachFeature: function(feature, layer){
|
46
|
+
layer.defaultOptions.title = getMapTitle(options.type, feature.properties.name);
|
47
|
+
layer.on('click', function(e){
|
48
|
+
var placenames = {};
|
49
|
+
placenames[layer.defaultOptions.title] = [feature.properties.html];
|
50
|
+
setupSidebarDisplay(e,placenames);
|
51
|
+
});
|
42
52
|
}
|
43
|
-
var placenames = {};
|
44
|
-
placenames[feature.properties.placename] = [feature.properties.html];
|
45
|
-
offsetMap(e);
|
46
|
-
$('#blacklight-map-sidebar').html(buildList(placenames));
|
47
|
-
sidebar.show();
|
48
53
|
});
|
49
|
-
}
|
50
|
-
});
|
51
54
|
|
52
|
-
|
53
|
-
|
55
|
+
// Add GeoJSON layer to marker cluster object
|
56
|
+
markers.addLayer(geoJsonLayer);
|
54
57
|
|
55
|
-
|
56
|
-
|
58
|
+
// Add marker cluster object to map
|
59
|
+
map.addLayer(markers);
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
//hide sidebar if it is visible
|
62
|
-
if (sidebar.isVisible()){
|
63
|
-
sidebar.hide();
|
64
|
-
}
|
61
|
+
// Listeners for marker cluster clicks
|
62
|
+
markers.on('clusterclick', function(e){
|
63
|
+
hideSidebar();
|
65
64
|
|
66
|
-
|
67
|
-
|
65
|
+
//if map is at the lowest zoom level
|
66
|
+
if (map.getZoom() === options.maxzoom){
|
68
67
|
|
69
|
-
|
70
|
-
|
68
|
+
var placenames = generatePlacenamesObject(e.layer.getAllChildMarkers());
|
69
|
+
setupSidebarDisplay(e,placenames);
|
70
|
+
}
|
71
|
+
});
|
72
|
+
|
73
|
+
//Add click listener to map
|
74
|
+
map.on('click drag', hideSidebar);
|
75
|
+
|
76
|
+
});
|
71
77
|
|
78
|
+
function setupSidebarDisplay(e, placenames){
|
79
|
+
hideSidebar();
|
72
80
|
offsetMap(e);
|
81
|
+
if (currentLayer !== e.layer || !("layer" in e)){
|
82
|
+
// Update sidebar div with new html
|
83
|
+
$('#' + options.sidebar).html(buildList(placenames));
|
73
84
|
|
74
|
-
|
75
|
-
|
85
|
+
// Scroll sidebar div to top
|
86
|
+
$('#' + options.sidebar).scrollTop(0);
|
87
|
+
currentLayer = e.layer;
|
88
|
+
}
|
76
89
|
|
77
|
-
//Show the sidebar
|
90
|
+
// Show the sidebar
|
78
91
|
sidebar.show();
|
92
|
+
|
79
93
|
}
|
80
|
-
});
|
81
94
|
|
82
|
-
|
83
|
-
|
95
|
+
// Hides sidebar if it is visible
|
96
|
+
function hideSidebar(){
|
97
|
+
if (sidebar.isVisible()){
|
98
|
+
sidebar.hide();
|
99
|
+
}
|
100
|
+
}
|
84
101
|
|
85
|
-
//
|
86
|
-
|
87
|
-
|
102
|
+
// Build the list
|
103
|
+
function buildList(placenames){
|
104
|
+
var html = "";
|
105
|
+
$.each(placenames, function(i,val){
|
106
|
+
html += "<h2>" + i + "</h2>";
|
107
|
+
html += "<ul class='sidebar-list'>";
|
108
|
+
$.each(val, function(j, val2){
|
109
|
+
html += val2;
|
110
|
+
});
|
111
|
+
html += "</ul>";
|
112
|
+
});
|
113
|
+
return html;
|
88
114
|
}
|
89
|
-
});
|
90
115
|
|
91
|
-
|
92
|
-
|
116
|
+
// Generates placenames object
|
117
|
+
function generatePlacenamesObject(markers){
|
118
|
+
var placenames = {};
|
119
|
+
$.each(markers, function(i,val){
|
120
|
+
if (!(val.defaultOptions.title in placenames)){
|
121
|
+
placenames[val.defaultOptions.title] = [];
|
122
|
+
}
|
123
|
+
placenames[val.defaultOptions.title].push(val.feature.properties.html);
|
124
|
+
});
|
125
|
+
return placenames;
|
126
|
+
}
|
93
127
|
|
94
|
-
//
|
95
|
-
|
96
|
-
|
128
|
+
// Move the map so that it centers the clicked cluster TODO account for various size screens
|
129
|
+
function offsetMap(e){
|
130
|
+
var mapWidth = $('#' + options.id).width();
|
131
|
+
var mapHeight = $('#' + options.id).height();
|
132
|
+
if (!e.latlng.equals(map.getCenter())){
|
133
|
+
map.panBy([(e.originalEvent.layerX - (mapWidth/4)), (e.originalEvent.layerY - (mapHeight/2))]);
|
134
|
+
}else{
|
135
|
+
map.panBy([(mapWidth/4), 0]);
|
136
|
+
}
|
97
137
|
}
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
$.each(placenames, function(i,val){
|
110
|
-
html += "<h2>" + i + "</h2>";
|
111
|
-
html += "<ul class='sidebar-list'>";
|
112
|
-
$.each(val, function(j, val2){
|
113
|
-
html += val2;
|
114
|
-
});
|
115
|
-
html += "</ul>";
|
116
|
-
});
|
117
|
-
return html;
|
118
|
-
}
|
119
|
-
|
120
|
-
// Generates placenames object
|
121
|
-
function generatePlacenamesObject(markers){
|
122
|
-
var placenames = {};
|
123
|
-
$.each(markers, function(i,val){
|
124
|
-
if (!(val.feature.properties.placename in placenames)){
|
125
|
-
placenames[val.feature.properties.placename] = [];
|
138
|
+
|
139
|
+
};
|
140
|
+
|
141
|
+
function getMapTitle(type, featureName){
|
142
|
+
switch(type){
|
143
|
+
case 'bbox':
|
144
|
+
return 'Results';
|
145
|
+
case 'placename_coord':
|
146
|
+
return featureName;
|
147
|
+
default:
|
148
|
+
return 'Results';
|
126
149
|
}
|
127
|
-
placenames[val.feature.properties.placename].push(val.feature.properties.html);
|
128
|
-
});
|
129
|
-
return placenames;
|
130
|
-
}
|
131
|
-
|
132
|
-
// Move the map so that it centers the clicked cluster TODO account for various size screens
|
133
|
-
function offsetMap(e){
|
134
|
-
mapWidth = $('#blacklight-map').width();
|
135
|
-
mapHeight = $('#blacklight-map').height();
|
136
|
-
if (!e.latlng.equals(map.getCenter())){
|
137
|
-
map.panBy([(e.originalEvent.layerX - (mapWidth/4)), (e.originalEvent.layerY - (mapHeight/2))]);
|
138
|
-
}else{
|
139
|
-
map.panBy([(mapWidth/4), 0]);
|
140
150
|
}
|
141
|
-
|
151
|
+
|
152
|
+
}( jQuery ));
|
@@ -1,36 +1,26 @@
|
|
1
|
+
# Helper methods used for Blacklight Maps
|
1
2
|
module BlacklightMapsHelper
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
# @param [String] id the html id
|
4
|
+
# @param [Hash] tag_options options to put on the tag
|
5
|
+
def blacklight_map_tag id, tag_options = {}, &block
|
6
|
+
default_data = {
|
7
|
+
maxzoom: blacklight_config.view.maps.maxzoom,
|
8
|
+
tileurl: blacklight_config.view.maps.tileurl,
|
9
|
+
type: blacklight_config.view.maps.type,
|
10
|
+
mapattribution: blacklight_config.view.maps.mapattribution
|
11
|
+
}
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
+
options = {id: id, data: default_data}.deep_merge(tag_options)
|
14
|
+
if block_given?
|
15
|
+
content_tag(:div, options, &block)
|
16
|
+
else
|
17
|
+
tag(:div, options)
|
13
18
|
end
|
19
|
+
end
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
feature = {type: "Feature", geometry: {type: "Point",
|
22
|
-
coordinates: [values[2].to_f, values[1].to_f]},
|
23
|
-
properties: {placename: values[0],
|
24
|
-
html: render_leaflet_sidebar_partial(doc)}}
|
25
|
-
geojson_docs[:features].push feature
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
return geojson_docs.to_json
|
30
|
-
end
|
31
|
-
|
32
|
-
def render_leaflet_sidebar_partial(doc)
|
33
|
-
render partial: 'catalog/index_maps', locals: {document: SolrDocument.new(doc)}
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
21
|
+
def serialize_geojson
|
22
|
+
export = BlacklightMaps::GeojsonExport.new(controller,
|
23
|
+
@response.docs)
|
24
|
+
export.to_geojson
|
25
|
+
end
|
26
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<% # container for all documents in map view -%>
|
2
2
|
<div id="documents" class="map">
|
3
|
-
<%=
|
3
|
+
<%= blacklight_map_tag('blacklight-map') %>
|
4
4
|
<div id="blacklight-map-sidebar"></div>
|
5
|
-
<%= javascript_tag "
|
6
|
-
</div>
|
5
|
+
<%= javascript_tag "$('#blacklight-map').blacklight_leaflet_map(#{serialize_geojson});" %>
|
6
|
+
</div>
|