-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial commits of blacklight-maps gem #1
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,5 @@ test/tmp | |
test/version_tmp | ||
tmp | ||
spec/internal | ||
jetty | ||
jetty | ||
.DS_Store |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,4 +40,4 @@ namespace :blacklight_maps do | |
cp("#{f}", 'jetty/solr/blacklight-core/conf/', :verbose => true) | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
// for Blacklight.onLoad: | ||
//= require blacklight/core | ||
|
||
//= require leaflet | ||
//= require leaflet.markercluster | ||
//= require L.Control.Sidebar | ||
//= require L.Control.Sidebar | ||
|
||
//= require_tree './blacklight-maps' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
var map, sidebar; | ||
|
||
Blacklight.onLoad(function() { | ||
|
||
// Stop doing stuff if the map div isn't there | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jkeck you mentioned using $("[data-blacklight-map]").blacklight_leaflet_map();
$.fn.blacklight_leaflet_map = function(arg_opts) {
this.each(function() {
L.map(this.id)...
}
} |
||
if ($("#blacklight-map").length === 0){ | ||
return; | ||
} | ||
|
||
// Get the configuration options from the data-attributes | ||
$.extend(Blacklight.mapOptions, $("#blacklight-map").data()); | ||
|
||
map = L.map('blacklight-map').setView([0,0], 2); | ||
L.tileLayer(Blacklight.mapOptions.tileurl, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make this more extensible, I think it might help to split each of these components out into its own method. |
||
attribution: Blacklight.mapOptions.mapattribution, | ||
maxZoom: Blacklight.mapOptions.maxzoom | ||
}).addTo(map); | ||
|
||
// Sets up leaflet-sidebar | ||
sidebar = L.control.sidebar('blacklight-map-sidebar', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're going to support reuse, we should derive the sidebar id from the container. |
||
position: 'right', | ||
autoPan: false | ||
}); | ||
|
||
// Adds leaflet-sidebar control to map (object) | ||
map.addControl(sidebar); | ||
|
||
// Create a marker cluster object and set options | ||
markers = new L.MarkerClusterGroup({ | ||
showCoverageOnHover: false, | ||
spiderfyOnMaxZoom: false, | ||
singleMarkerMode: true, | ||
animateAddingMarkers: true | ||
}); | ||
|
||
geoJsonLayer = L.geoJson(geojson_docs, { | ||
onEachFeature: function(feature, layer){ | ||
layer.defaultOptions.title = feature.properties.placename; | ||
layer.on('click', function(e){ | ||
if (sidebar.isVisible()){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extracting the nested click handler would be nice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and it seems like it's pretty similar to the clusterclick event handler. Maybe we can reuse the code there? |
||
sidebar.hide(); | ||
} | ||
var placenames = {}; | ||
placenames[feature.properties.placename] = [feature.properties.html]; | ||
offsetMap(e); | ||
$('#blacklight-map-sidebar').html(buildList(placenames)); | ||
sidebar.show(); | ||
}); | ||
} | ||
}); | ||
|
||
// Add GeoJSON layer to marker cluster object | ||
markers.addLayer(geoJsonLayer); | ||
|
||
// Add marker cluster object to map | ||
map.addLayer(markers); | ||
|
||
// Listeners for marker cluster clicks | ||
markers.on('clusterclick', function(e){ | ||
|
||
//hide sidebar if it is visible | ||
if (sidebar.isVisible()){ | ||
sidebar.hide(); | ||
} | ||
|
||
//if map is at the lowest zoom level | ||
if (map.getZoom() === Blacklight.mapOptions.maxzoom){ | ||
|
||
var placenames = generatePlacenamesObject(e.layer._markers); | ||
|
||
|
||
offsetMap(e); | ||
|
||
//Update sidebar div with new html | ||
$('#blacklight-map-sidebar').html(buildList(placenames)); | ||
|
||
//Show the sidebar! | ||
sidebar.show(); | ||
} | ||
}); | ||
|
||
//Add click listener to map | ||
map.on('click', function(e){ | ||
|
||
//hide the sidebar if it is visible | ||
if (sidebar.isVisible()){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should wrap this logic up in a function, so we could call, e.g.: map.on('click,drag', hideSidebar) (and I think that's the jquery syntax for matching multiple events. might be wrong..) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll look into this. Looks like this is supported in Leaflet too: http://stackoverflow.com/questions/14677974/single-event-handler-for-multiple-events-in-leaflet There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I didn't realize this wasn't the normal jquery |
||
sidebar.hide(); | ||
} | ||
}); | ||
|
||
//drag listener on map | ||
map.on('drag', function(e){ | ||
|
||
//hide the sidebar if it is visible | ||
if (sidebar.isVisible()){ | ||
sidebar.hide(); | ||
} | ||
}); | ||
|
||
}); | ||
|
||
Blacklight.mapOptions = { | ||
tileurl : 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', | ||
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>' | ||
}; | ||
|
||
function buildList(placenames){ | ||
var html = ""; | ||
$.each(placenames, function(i,val){ | ||
html += "<h2>" + i + "</h2>"; | ||
html += "<ul class='sidebar-list'>"; | ||
$.each(val, function(j, val2){ | ||
html += val2; | ||
}); | ||
html += "</ul>"; | ||
}); | ||
return html; | ||
} | ||
|
||
// Generates placenames object | ||
function generatePlacenamesObject(markers){ | ||
var placenames = {}; | ||
$.each(markers, function(i,val){ | ||
if (!(val.feature.properties.placename in placenames)){ | ||
placenames[val.feature.properties.placename] = []; | ||
} | ||
placenames[val.feature.properties.placename].push(val.feature.properties.html); | ||
}); | ||
return placenames; | ||
} | ||
|
||
// Move the map so that it centers the clicked cluster TODO account for various size screens | ||
function offsetMap(e){ | ||
mapWidth = $('#blacklight-map').width(); | ||
mapHeight = $('#blacklight-map').height(); | ||
map.panBy([(e.originalEvent.layerX - (mapWidth/4)), (e.originalEvent.layerY - (mapHeight/2))]); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,4 @@ | ||
/* Master manifest file for engine, so local app can require | ||
* this one file, but get all our files -- and local app | ||
* require does not need to change if we change file list. | ||
* | ||
*= require 'leaflet.markercluster' | ||
*= require 'leaflet.markercluster.default' | ||
*= require 'L.Control.Sidebar' | ||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,24 @@ | ||
#documents.map { | ||
/* | ||
*= require leaflet | ||
*= require leaflet.markercluster | ||
*= require leaflet.markercluster.default | ||
*= require L.Control.Sidebar.css | ||
*/ | ||
|
||
.view-icon-maps { | ||
&:before { content: "\e135"; } | ||
} | ||
|
||
.view-icon-maps { | ||
&:before { content: "\e062"; } | ||
#blacklight-map{ | ||
height: 550px; | ||
} | ||
|
||
.sidebar-thumb{ | ||
height: 64px; | ||
width: 64px; | ||
} | ||
|
||
.sidebar-list{ | ||
padding-left: 0; | ||
list-style: none; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
module BlacklightMapsHelper | ||
|
||
def show_map_div | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To give our consumers additional flexibility, I'd adjust this method signature to something like: def blacklight_map_tag id, tag_options = {}, &block
...
content_tag(:div, { id: id data: ...}.deep_merge(tag_options), &block)
end This (sorta) mirrors the regular |
||
data_attributes = { | ||
:maxzoom => blacklight_config.view.maps.maxzoom, | ||
:tileurl => blacklight_config.view.maps.tileurl | ||
|
||
} | ||
|
||
content_tag(:div, "", id: "blacklight-map", | ||
data: data_attributes | ||
) | ||
end | ||
|
||
def serialize_geojson | ||
geojson_docs = {type: "FeatureCollection", features: []} | ||
@response.docs.each_with_index do |doc, counter| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see where we're using the index counter. Maybe it's vestigial? We can also use {type: "FeatureCollection", features: @response.docs.map { |doc| ... }.compact }.to_json There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of rendering There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cbeer +1 on index counter. |
||
if doc[blacklight_config.view.maps.placename_coord_field] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could push some of this logic into the document, using the "Document Extensibility Framework" to add an |
||
doc[blacklight_config.view.maps.placename_coord_field].each do |loc| | ||
values = loc.split('|') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should wrap the doc.geofeatures.each &block |
||
feature = {type: "Feature", geometry: {type: "Point", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, but does leaflet support other geometries too? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cbeer yes it does, -pretty much everything that is out there. My thought was initially supporting this format "placename-coord" (kind of its own solr thing) and expanding to polygons (bounding boxes) and coordinates without the placenames. The Spotlight specific use case relies on these coordinates relating to placenames. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool. I can imagine for e.g. maps of africa, being able to show bounding boxes could be pretty interesting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, BPL wants this too. |
||
coordinates: [values[2].to_f, values[1].to_f]}, | ||
properties: {placename: values[0], | ||
html: render_leaflet_sidebar_partial(doc)}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think/hope the render_to_string(doc) # not sure if render_to_string supports this short-hand.. but it'd be awfully nice if it did. Obviously, if we delegate most of the geojson to the model, we'll have to have some way to pass or merge the html into the block.. |
||
geojson_docs[:features].push feature | ||
end | ||
end | ||
end | ||
return geojson_docs.to_json | ||
end | ||
|
||
def render_leaflet_sidebar_partial(doc) | ||
render partial: 'catalog/index_maps', locals: {document: SolrDocument.new(doc)} | ||
end | ||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
<% # container for all documents in index view -%> | ||
<% # container for all documents in map view -%> | ||
<div id="documents" class="map"> | ||
<!-- map goes here --> | ||
<%= show_map_div() %> | ||
<div id="blacklight-map-sidebar"></div> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't realize above that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cbeer are you thinking something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like: <div id="blacklight-maps" data-map-sidebar="#blacklight-map-sidebar" /> And then we can use that attribute in the javascript to figure out where the sidebar is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cbeer ok, just checked this with leaflet. It will not work with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I didn't assume leaflet would do that work. In our wrapping javascript, we'd have to parse it out and use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh got it. |
||
<%= javascript_tag "var geojson_docs = #{serialize_geojson}" %> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using global variables, can we turn this into a method call? Maybe something like: $("#blacklight-map").blacklight_leaflet_map(<%= serialize_geojson %>); |
||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<% # the way each document will be viewed in the sidebar list -%> | ||
<li class='media'> | ||
<%= render_thumbnail_tag document, {class: 'sidebar-thumb media-object'}, {class: 'pull-left'} %> | ||
<div class='media-body'> | ||
<h4 class='media-heading'> | ||
<%= link_to_document document, :label=>document_show_link_field(document) %> | ||
</h4> | ||
</div> | ||
</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this
gem engine_cart
can get dropped. The gemspec should be providing it, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbeer There was some reason I left it in there and now I can't remember. I'll look into it.