blacklight_heatmaps 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +13 -0
  3. data/README.md +56 -0
  4. data/Rakefile +13 -0
  5. data/app/assets/javascripts/blacklight_heatmaps/blacklight_heatmaps.js +10 -0
  6. data/app/assets/javascripts/blacklight_heatmaps/default.js +6 -0
  7. data/app/assets/javascripts/blacklight_heatmaps/viewers/index.js +89 -0
  8. data/app/assets/javascripts/blacklight_heatmaps/viewers/show.js +34 -0
  9. data/app/assets/stylesheets/blacklight_heatmaps/default.scss +14 -0
  10. data/app/controllers/blacklight_heatmaps/application_controller.rb +5 -0
  11. data/app/helpers/blacklight/maps_helper.rb +59 -0
  12. data/app/models/concerns/blacklight_heatmaps/bounding_box.rb +50 -0
  13. data/app/models/concerns/blacklight_heatmaps/exceptions.rb +10 -0
  14. data/app/models/concerns/blacklight_heatmaps/geometry_parser.rb +21 -0
  15. data/app/models/concerns/blacklight_heatmaps/geometry_solr_document.rb +11 -0
  16. data/app/models/concerns/blacklight_heatmaps/point.rb +31 -0
  17. data/app/models/concerns/blacklight_heatmaps/solr_facet_heatmap_behavior.rb +53 -0
  18. data/app/views/catalog/_document_heatmaps.html.erb +5 -0
  19. data/app/views/catalog/_show_leaflet_map_default.html.erb +5 -0
  20. data/app/views/catalog/index.json.jbuilder +10 -0
  21. data/app/views/layouts/blacklight_heatmaps/application.html.erb +14 -0
  22. data/config/routes.rb +2 -0
  23. data/lib/blacklight_heatmaps.rb +7 -0
  24. data/lib/blacklight_heatmaps/engine.rb +8 -0
  25. data/lib/blacklight_heatmaps/version.rb +3 -0
  26. data/lib/generators/blacklight_heatmaps/install_generator.rb +41 -0
  27. data/lib/generators/blacklight_heatmaps/templates/blacklight_heatmaps.js +1 -0
  28. data/lib/generators/blacklight_heatmaps/templates/blacklight_heatmaps.scss +3 -0
  29. data/lib/tasks/blacklight_heatmaps_tasks.rake +25 -0
  30. data/spec/features/index_page_map_spec.rb +15 -0
  31. data/spec/features/show_page_map_spec.rb +15 -0
  32. data/spec/helpers/blacklight/maps_helper_spec.rb +38 -0
  33. data/spec/models/concerns/blacklight_heatmaps/bounding_box_spec.rb +31 -0
  34. data/spec/models/concerns/blacklight_heatmaps/geometry_parser_spec.rb +24 -0
  35. data/spec/models/concerns/blacklight_heatmaps/geometry_solr_document_spec.rb +34 -0
  36. data/spec/models/concerns/blacklight_heatmaps/point_spec.rb +18 -0
  37. data/spec/models/concerns/blacklight_heatmaps/solr_facet_heatmap_behavior_spec.rb +67 -0
  38. data/spec/spec_helper.rb +26 -0
  39. data/spec/test_app_templates/lib/generators/test_app_generator.rb +27 -0
  40. data/spec/views/catalog/_document_heatmaps.html.erb_spec.rb +19 -0
  41. data/spec/views/catalog/_show_leaflet_map_default.html.erb_spec.rb +32 -0
  42. metadata +242 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eb7c89ff10e6644af8ec6c7ac604b50f7b6e9322
4
+ data.tar.gz: a9b83bde4dcce36145deb45db7f3a523ae994010
5
+ SHA512:
6
+ metadata.gz: 63b31f0cb11727db4067c3e0cdb4047b54e0b6126a9f2b32a398f228bfffdc63b0aba90efdb4f4956b57c8df55dd8abd1db912b232b1fbb5119d5566eb63763e
7
+ data.tar.gz: 1b11f4e59a9166fe570f7e93d3f2b1b7c2199b5a95093be250a1d5214506474766e58226937a26b5a324013ea7ee436dee97a171aeb75325fccc724287243596
@@ -0,0 +1,13 @@
1
+ Copyright 2016 The Board of Trustees of the Leland Stanford Junior University
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,56 @@
1
+ # BlacklightHeatmaps
2
+ [![Build Status](https://travis-ci.org/sul-dlss/blacklight_heatmaps.svg?branch=master)](https://travis-ci.org/sul-dlss/blacklight_heatmaps) | [![Coverage Status](https://coveralls.io/repos/github/sul-dlss/blacklight_heatmaps/badge.svg?branch=master)](https://coveralls.io/github/sul-dlss/blacklight_heatmaps?branch=master)
3
+
4
+ ![blacklight_heatmap](https://cloud.githubusercontent.com/assets/1656824/16598401/d0538fce-42cb-11e6-86f8-81fd37ab2abe.gif)
5
+
6
+ ## Features
7
+ - Configurable heatmaps of result sets
8
+ - Works with center points and bounding box data
9
+ - Show page map view
10
+ - Changeout the basemap to any tile layer
11
+ - _Really_ fast with large data sets
12
+ - Utilizes Solr's [facet heatmap](https://issues.apache.org/jira/browse/SOLR-7005) feature to provide snappy results. Tested with an index > 10,000,000 records.
13
+ - Customizable result view template
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'blacklight_heatmaps'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```sh
26
+ bundle install
27
+ ```
28
+
29
+ Run the BlacklightMaps installer:
30
+
31
+ ```sh
32
+ rails generate blacklight_heatmaps:install
33
+ ```
34
+
35
+ ## Getting started
36
+
37
+ BlacklightHeatmaps expects your data to be indexed as a [Spatial Recursive Prefix Tree](https://cwiki.apache.org/confluence/display/solr/Spatial+Search#SpatialSearch-RPT) type. The plugin currently supports data indexed in formats:
38
+
39
+ - `x y` Syntax. example: "-121.631609 36.688128"
40
+ - CQL ENVELOPE Syntax (`minX, maxX, maxY, minY`). example: "ENVELOPE(122.934585571, 153.987060547, 45.522888184, 20.422889709)"
41
+
42
+ Additional formats could be added by extending `BlacklightHeatmaps::GeometryParser`
43
+
44
+ ## Development
45
+
46
+ Run Solr and Blacklight (with BlacklightMaps) for interactive development:
47
+
48
+ ```sh
49
+ bundle exec rake blacklight_heatmaps:server
50
+ ```
51
+
52
+ Run the test suite
53
+
54
+ ```sh
55
+ bundle exec rake ci
56
+ ```
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ load 'tasks/blacklight_heatmaps.rake'
10
+
11
+ require 'engine_cart/rake_task'
12
+
13
+ task default: :spec
@@ -0,0 +1,10 @@
1
+ !(function (global) {
2
+ 'use strict';
3
+
4
+ var BlacklightHeatmaps = L.Class.extend({
5
+ statics: {
6
+ __version__: '0.1.0',
7
+ },
8
+ });
9
+ global.BlacklightHeatmaps = BlacklightHeatmaps;
10
+ }(this));
@@ -0,0 +1,6 @@
1
+ //= require geostats
2
+ //= require leaflet
3
+ //= require L.Control.Sidebar
4
+ //= require leaflet_solr_heatmap
5
+ //= require blacklight_heatmaps/blacklight_heatmaps
6
+ //= require_tree ./viewers
@@ -0,0 +1,89 @@
1
+ Blacklight.onLoad(function () {
2
+ 'use strict';
3
+
4
+ $('[data-index-map]').each(function () {
5
+ BlacklightHeatmaps.indexView(this, {});
6
+ });
7
+ });
8
+
9
+ !(function (global) {
10
+ 'use strict';
11
+
12
+ var IndexView = L.Class.extend({
13
+ options: {},
14
+
15
+ initialize: function (el, options) {
16
+ var _this = this;
17
+ var $el = $(el);
18
+ var requestUrl = $el.data().searchUrl + '&format=json';
19
+ var geometryField = $el.data().geometryField;
20
+ var template = $el.data().sidebarTemplate;
21
+ var colorRamp = $el.data().colorRamp;
22
+
23
+ var map = L.map($el[0].id).setView([0, 0], 1);
24
+ var basemap = L.tileLayer($el.data().basemap, {
25
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
26
+ }).addTo(map);
27
+
28
+ var solrLayer = L.solrHeatmap(requestUrl, {
29
+ field: geometryField,
30
+ maxSampleSize: 50,
31
+ colors: colorRamp,
32
+ }).addTo(map);
33
+
34
+ var sidebar = L.control.sidebar('index-map-sidebar', {
35
+ position: 'right',
36
+ });
37
+
38
+ map.addControl(sidebar);
39
+
40
+ solrLayer.on('click', function (e) {
41
+ if (!sidebar.isVisible()) {
42
+ map.setView(e.latlng);
43
+ } else {
44
+ var point = map.project(e.latlng);
45
+ var offset = sidebar.getOffset();
46
+ var newPoint = L.point(point.x - (offset / 2), point.y);
47
+ map.setView(map.unproject(newPoint));
48
+ }
49
+
50
+ sidebar.show();
51
+ });
52
+
53
+ solrLayer.on('dataAdded', function (e) {
54
+ if (e.response && e.response.docs) {
55
+ var html = '';
56
+ $.each(e.response.docs, function (i, value) {
57
+ html += L.Util.template(template, value);
58
+ });
59
+
60
+ sidebar.setContent(html);
61
+
62
+ var docCount = e.response.pages.total_count;
63
+ $('#sortAndPerPage .page_links').html(
64
+ parseInt(docCount).toLocaleString() + ' ' +
65
+ _this.pluralize(docCount, 'item') + ' found'
66
+ );
67
+ }
68
+ });
69
+
70
+ $(document).on('turbolinks:click', function (e) {
71
+ e.preventDefault();
72
+ });
73
+ },
74
+
75
+ pluralize: function (count, word) {
76
+ switch (count) {
77
+ case 1:
78
+ return word;
79
+ default:
80
+ return word + 's';
81
+ }
82
+ },
83
+ });
84
+
85
+ global.BlacklightHeatmaps.IndexView = IndexView;
86
+ global.BlacklightHeatmaps.indexView = function (el, options) {
87
+ return new IndexView(el, options);
88
+ };
89
+ })(this);
@@ -0,0 +1,34 @@
1
+ Blacklight.onLoad(function () {
2
+ 'use strict';
3
+
4
+ $('[data-show-map]').each(function () {
5
+ BlacklightHeatmaps.showView(this);
6
+ });
7
+ });
8
+
9
+ !(function (global) {
10
+ 'use strict';
11
+
12
+ var ShowView = L.Class.extend({
13
+ options: {},
14
+
15
+ initialize: function (el, options) {
16
+ var $el = $(el);
17
+ var features = $el.data().features;
18
+
19
+ var map = L.map($el[0].id).setView([0, 0], 1);
20
+ var basemap = L.tileLayer($el.data().basemap, {
21
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
22
+ }).addTo(map);
23
+
24
+ var features = L.geoJson(features).addTo(map);
25
+
26
+ map.fitBounds(features.getBounds());
27
+ },
28
+ });
29
+
30
+ global.BlacklightHeatmaps.ShowView = ShowView;
31
+ global.BlacklightHeatmaps.showView = function (el, options) {
32
+ return new ShowView(el, options);
33
+ };
34
+ })(this);
@@ -0,0 +1,14 @@
1
+ @import 'leaflet';
2
+ @import 'L.Control.Sidebar';
3
+
4
+ .blacklight-heatmaps-show-map {
5
+ height: 500px;
6
+ }
7
+
8
+ .blacklight-heatmaps-index-map {
9
+ height: 500px;
10
+ }
11
+
12
+ .view-icon-heatmaps {
13
+ &:before { content: "\e135"; }
14
+ }
@@ -0,0 +1,5 @@
1
+ module BlacklightHeatmaps
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,59 @@
1
+ module Blacklight
2
+ module MapsHelper
3
+ ##
4
+ # Creates a div with needed attributes, used to display the index map
5
+ # @return String
6
+ def index_map_div
7
+ content_tag(
8
+ :div,
9
+ nil,
10
+ class: 'blacklight-heatmaps-index-map',
11
+ id: 'index-map',
12
+ data: index_map_data_attributes
13
+ )
14
+ end
15
+
16
+ ##
17
+ # The Leaflet template used for constructing the sidebar documents.
18
+ # Variables from returned docs should be keys within curly braces
19
+ # e.g. {title_display}
20
+ # @return String
21
+ def sidebar_template
22
+ <<-HTMLTEMPLATE
23
+ <div class='media'>
24
+ <div class='media-body'>
25
+ <h3 class='media-heading'>
26
+ <a href=\"#{document_path}\"}>
27
+ {#{blacklight_config.index.title_field}}
28
+ </a>
29
+ </h3>
30
+ </div>
31
+ </div>
32
+ HTMLTEMPLATE
33
+ end
34
+
35
+ private
36
+
37
+ ##
38
+ # Document path used for creating client side links to documents from a
39
+ # template
40
+ # @return String
41
+ def document_path
42
+ "#{search_catalog_path}/{#{blacklight_config.document_unique_id_param}}"
43
+ end
44
+
45
+ ##
46
+ # Data attributes used in displaying the index map
47
+ # @return Hash
48
+ def index_map_data_attributes
49
+ {
50
+ index_map: true,
51
+ basemap: blacklight_config.basemap,
52
+ search_url: request.url,
53
+ geometry_field: blacklight_config.geometry_field,
54
+ sidebar_template: sidebar_template,
55
+ color_ramp: blacklight_config.view.heatmaps.color_ramp
56
+ }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,50 @@
1
+ module BlacklightHeatmaps
2
+ ##
3
+ # Used for creating and parsing bounding boxes from CQL Envelope syntax
4
+ class BoundingBox
5
+ ##
6
+ # @param [String, Integer, Float] west
7
+ # @param [String, Integer, Float] south
8
+ # @param [String, Integer, Float] east
9
+ # @param [String, Integer, Float] north
10
+ def initialize(west, south, east, north)
11
+ @west = west.to_f
12
+ @south = south.to_f
13
+ @east = east.to_f
14
+ @north = north.to_f
15
+ end
16
+
17
+ ##
18
+ # Returns a bounding box in ENVELOPE syntax
19
+ # @return [String]
20
+ def to_envelope
21
+ "ENVELOPE(#{west}, #{east}, #{north}, #{south})"
22
+ end
23
+
24
+ ##
25
+ # @return String
26
+ def to_geojson
27
+ {
28
+ type: 'Polygon',
29
+ coordinates: [
30
+ [
31
+ [west, south],
32
+ [west, north],
33
+ [east, north],
34
+ [east, south],
35
+ [west, south]
36
+ ]
37
+ ]
38
+ }.to_json
39
+ end
40
+
41
+ def self.from_envelope(envelope)
42
+ envelope = envelope[/.*ENVELOPE\(([^\)]*)/,1].split(',')
43
+ new(envelope[0], envelope[3], envelope[1], envelope[2])
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :west, :south, :east, :north
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ module BlacklightHeatmaps
2
+ module Exceptions
3
+ class UnknownSpatialDataType < StandardError
4
+ def message
5
+ 'BlacklightHeatmaps does not know how to parse that type of spatial data. '\
6
+ 'Please try using the CQL ENVELOPE or X Y syntax.'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module BlacklightHeatmaps
2
+ ##
3
+ # Parser that tries to understand the type of geospatial data stored in a
4
+ # String and sends that to the correct parsing class.
5
+ class GeometryParser
6
+ ##
7
+ # Utility method for determing the type of spatial data stored in a string
8
+ # field
9
+ # @param String
10
+ def self.parse(geometry)
11
+ case geometry
12
+ when /^ENVELOPE\(.*\)$/
13
+ BlacklightHeatmaps::BoundingBox.from_envelope(geometry)
14
+ when /^(\-?\d+(\.\d+)?) \w*(\-?\d+(\.\d+)?)$/
15
+ BlacklightHeatmaps::Point.from_lng_lat(geometry)
16
+ else
17
+ raise BlacklightHeatmaps::Exceptions::UnknownSpatialDataType
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module BlacklightHeatmaps
2
+ ##
3
+ # Provides methods to convert Solr geometry strings
4
+ module GeometrySolrDocument
5
+ def to_geojson(blacklight_config = nil)
6
+ return unless blacklight_config.try(:geometry_field) && fetch(blacklight_config.geometry_field, nil)
7
+ BlacklightHeatmaps::GeometryParser
8
+ .parse(fetch(blacklight_config.geometry_field)).to_geojson
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module BlacklightHeatmaps
2
+ ##
3
+ # A geometry class to characterize points. Can be longitude and latitude.
4
+ class Point
5
+ def initialize(x, y)
6
+ @x = x.to_f
7
+ @y = y.to_f
8
+ end
9
+
10
+ ##
11
+ # @return String
12
+ def to_geojson
13
+ {
14
+ type: 'Point',
15
+ coordinates: [
16
+ x, y
17
+ ]
18
+ }.to_json
19
+ end
20
+
21
+ ##
22
+ # @param String
23
+ def self.from_lng_lat(lng_lat)
24
+ new(*lng_lat.split(' '))
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :x, :y
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ module BlacklightHeatmaps
2
+ ##
3
+ # Extends itself into a consuming application's SearchBuilder and adds
4
+ # relevancy, spatial search, and heatmap functionality.
5
+ module SolrFacetHeatmapBehavior
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ self.default_processor_chain += [:add_solr_facet_heatmap]
10
+ end
11
+
12
+ ##
13
+ # Add Solr spatial heatmap parameters to Solr request
14
+ def add_solr_facet_heatmap(solr_parameters = {})
15
+ if blacklight_params[:bbox]
16
+ solr_parameters['facet.heatmap'] = geometry_field
17
+ solr_parameters['facet.heatmap.geom'] = bbox_as_range
18
+ solr_parameters[:bq] ||= []
19
+ solr_parameters[:bq] << "#{geometry_field}:\"IsWithin(#{bbox_as_envelope})\""
20
+ solr_parameters[:fq] ||= []
21
+ solr_parameters[:fq] << "#{geometry_field}:\"Intersects(#{bbox_as_envelope})\""
22
+ end
23
+ solr_parameters
24
+ end
25
+
26
+ def geometry_field
27
+ blacklight_config.geometry_field
28
+ end
29
+
30
+ ##
31
+ # `bbox` parameter is in format "west,south,east,north"
32
+ # @return Array
33
+ def bbox
34
+ blacklight_params[:bbox].split(',')
35
+ end
36
+
37
+ ##
38
+ # Returned in CQL Envelope syntax
39
+ # https://cwiki.apache.org/confluence/display/solr/Spatial+Search
40
+ # @return String
41
+ def bbox_as_envelope
42
+ BlacklightHeatmaps::BoundingBox.new(*bbox).to_envelope
43
+ end
44
+
45
+ ##
46
+ # Returned in rectangle-range syntax ["-180 -90" TO "180 90"]
47
+ # https://cwiki.apache.org/confluence/display/solr/Spatial+Search
48
+ # @return String
49
+ def bbox_as_range
50
+ "[\"#{bbox[0]} #{bbox[1]}\" TO \"#{bbox[2]} #{bbox[3]}\"]"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ <div class='blacklight-heatmaps-index-map-container'>
2
+ <%= index_map_div %>
3
+ <%= content_tag(:div, id: 'index-map-sidebar') do %>
4
+ <% end %>
5
+ </div>
@@ -0,0 +1,5 @@
1
+ <% features = document.to_geojson(blacklight_config) %>
2
+
3
+ <% unless features.nil? %>
4
+ <%= content_tag(:div, nil, class: 'blacklight-heatmaps-show-map', id: "map-#{document.id}", data: {show_map: true, features: features, basemap: blacklight_config.basemap}) %>
5
+ <% end %>
@@ -0,0 +1,10 @@
1
+ ##
2
+ # Overrides default index.json.jbuilder from Blacklight by adding the
3
+ # `facet_heatmaps` key/value
4
+
5
+ json.response do
6
+ json.docs @presenter.documents
7
+ json.facets @presenter.search_facets_as_json
8
+ json.facet_heatmaps @response['facet_counts']['facet_heatmaps']
9
+ json.pages @presenter.pagination_info
10
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>BlacklightHeatmaps</title>
5
+ <%= stylesheet_link_tag "blacklight_heatmaps/application", media: "all" %>
6
+ <%= javascript_include_tag "blacklight_heatmaps/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,2 @@
1
+ BlacklightHeatmaps::Engine.routes.draw do
2
+ end
@@ -0,0 +1,7 @@
1
+ require 'leaflet-rails'
2
+ require 'leaflet-sidebar-rails'
3
+ require 'blacklight'
4
+
5
+ module BlacklightHeatmaps
6
+ require 'blacklight_heatmaps/engine'
7
+ end
@@ -0,0 +1,8 @@
1
+ module BlacklightHeatmaps
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace BlacklightHeatmaps
4
+ initializer 'blacklight-maps.helpers' do |app|
5
+ ActionView::Base.send :include, Blacklight::MapsHelper
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module BlacklightHeatmaps
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,41 @@
1
+ require 'rails/generators'
2
+
3
+ module BlacklightHeatmaps
4
+ class Install < Rails::Generators::Base
5
+ def add_gems
6
+ gem 'blacklight_heatmaps'
7
+ end
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def assets
12
+ copy_file 'blacklight_heatmaps.scss', 'app/assets/stylesheets/blacklight_heatmaps.scss'
13
+ copy_file 'blacklight_heatmaps.js', 'app/assets/javascripts/blacklight_heatmaps.js'
14
+ end
15
+
16
+ def configuration
17
+ inject_into_file 'app/controllers/catalog_controller.rb', after: 'configure_blacklight do |config|' do
18
+ "\n # BlacklightHeatmaps configuration values" \
19
+ "\n config.geometry_field = :geo_srpt" \
20
+ "\n config.basemap = 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'" \
21
+ "\n config.show.partials.insert(1, :show_leaflet_map)" \
22
+ "\n config.view.heatmaps.partials = []" \
23
+ "\n #Heatmap color ramp. For best results, use http://colorbrewer2.org or http://tristen.ca/hcl-picker/#/hlc/5/1" \
24
+ "\n config.view.heatmaps.color_ramp = ['#ffffcc', '#a1dab4', '#41b6c4', '#2c7fb8', '#253494']" \
25
+ "\n"
26
+ end
27
+ end
28
+
29
+ def add_model_mixin
30
+ inject_into_file 'app/models/solr_document.rb', after: 'include Blacklight::Solr::Document' do
31
+ "\n include BlacklightHeatmaps::GeometrySolrDocument\n"
32
+ end
33
+ end
34
+
35
+ def inject_search_builder
36
+ inject_into_file 'app/models/search_builder.rb', after: /include Blacklight::Solr::SearchBuilderBehavior.*$/ do
37
+ "\n include BlacklightHeatmaps::SolrFacetHeatmapBehavior\n"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1 @@
1
+ //= require blacklight_heatmaps/default
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require blacklight_heatmaps/default
3
+ */
@@ -0,0 +1,25 @@
1
+ # Tasks shared with the consuming application
2
+ namespace :blacklight_heatmaps do
3
+ namespace :index do
4
+ desc 'Put sample data into Solr'
5
+ task :seed, [:datafile] => [:environment] do |_t, args|
6
+ args.with_defaults(datafile: 'sample_solr_documents')
7
+ fn = File.join(BlacklightHeatmaps::Engine.root, 'solr', args[:datafile] + '.yml')
8
+ puts "Indexing sample data from #{fn}"
9
+ docs = YAML.load(File.open(fn))
10
+ conn = Blacklight.default_index.connection
11
+ conn.add docs
12
+ conn.commit
13
+ end
14
+
15
+ desc 'Fetch random data from WhosOnFirst gazetteer and index into Solr'
16
+ task :seed_random, [:n] => [:environment] do |_t, args|
17
+ args.with_defaults(n: 10)
18
+ puts "Indexing #{args[:n]} random data records"
19
+ docs = YAML.load(`bundle exec ruby #{File.join(BlacklightHeatmaps::Engine.root, 'scripts', 'sample_whosonfirst.rb')} #{args[:n]}`)
20
+ conn = Blacklight.default_index.connection
21
+ conn.add docs
22
+ conn.commit
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Index page map', js: true do
4
+ it 'renders a leaflet map' do
5
+ visit search_catalog_path(q: 'strong', view: 'heatmaps')
6
+ expect(page).to have_css '.leaflet-map-pane'
7
+ # Zoomed to world
8
+ expect(page).to have_css 'img[src="http://a.basemaps.cartocdn.com/light_all/1/0/0.png"]'
9
+ expect(page).to have_css '#index-map-sidebar', visible: false
10
+ page.find('svg g path').click
11
+ expect(page).to have_css '#index-map-sidebar', visible: true
12
+ expect(page)
13
+ .to have_css 'h3.media-heading a', text: '"Strong Medicine speaks"'
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Show map map', js: true do
4
+ it 'renders a leaflet map' do
5
+ visit solr_document_path '43037890'
6
+ expect(page).to have_css '.leaflet-map-pane'
7
+ # Zoomed to Kazakhstan
8
+ expect(page).to have_css 'img[src="http://b.basemaps.cartocdn.com/light_all/4/11/5.png"]'
9
+ expect(page).to have_css 'svg g path'
10
+ end
11
+ it 'renders a point type' do
12
+ visit solr_document_path '34860108'
13
+ expect(page).to have_css '.leaflet-marker-icon'
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blacklight::MapsHelper do
4
+ let(:blacklight_config) do
5
+ Blacklight::Configuration.new(
6
+ basemap: 'http://www.example.com/{z}/{x}/{y}.png',
7
+ geometry_field: 'geo_srpt',
8
+ index: Blacklight::OpenStructWithHashAccess.new(
9
+ title_field: 'title_display'
10
+ )
11
+ )
12
+ end
13
+ before do
14
+ allow(helper).to receive_messages(blacklight_config: blacklight_config)
15
+ end
16
+ describe '#index_map_div' do
17
+ it 'renders html with the required elements/attributes' do
18
+ expect(helper.index_map_div)
19
+ .to have_css '#index-map.blacklight-heatmaps-index-map'
20
+ expect(helper.index_map_div)
21
+ .to have_css '[data-index-map="true"]'
22
+ expect(helper.index_map_div)
23
+ .to have_css '[data-search-url="http://test.host"]'
24
+ expect(helper.index_map_div)
25
+ .to have_css '[data-geometry-field="geo_srpt"]'
26
+ expect(helper.index_map_div)
27
+ .to have_css '[data-basemap="http://www.example.com/{z}/{x}/{y}.png"]'
28
+ expect(helper.index_map_div).to have_css '[data-sidebar-template]'
29
+ expect(helper.index_map_div).to have_css '[data-color-ramp]'
30
+ end
31
+ end
32
+ describe '#sidebar_template' do
33
+ it 'renders html template used in sidebar' do
34
+ expect(helper.sidebar_template)
35
+ .to have_css '.media .media-body h3.media-heading a', text: '{title_display}'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlacklightHeatmaps::BoundingBox do
4
+ describe '#initialize' do
5
+ it 'handles multiple input types as arguments' do
6
+ expect(described_class.new('1', '1', '1', '1')).to be_an described_class
7
+ expect(described_class.new(1, 2, 3, 3)).to be_an described_class
8
+ expect(described_class.new(1.1, 2.1, 3.1, 3.1)).to be_an described_class
9
+ end
10
+ end
11
+ describe '#to_envelope' do
12
+ let(:example_box) { described_class.new(-160, -80, 120, 70) }
13
+ it 'creates an envelope syntax version of the bounding box' do
14
+ expect(example_box.to_envelope).to eq 'ENVELOPE(-160.0, 120.0, 70.0, -80.0)'
15
+ end
16
+ end
17
+ describe '#to_geojson' do
18
+ let(:example_box) { described_class.new(-160, -80, 120, 70) }
19
+ it 'creates a geoJSON string' do
20
+ expect(example_box.to_geojson).to eq '{"type":"Polygon","coordinates":[[[-160.0,-80.0],[-160.0,70.0],[120.0,70.0],[120.0,-80.0],[-160.0,-80.0]]]}'
21
+ end
22
+ end
23
+ describe '#from_rectangle' do
24
+ let(:envelope) { 'ENVELOPE(-160.0, 120.0, 70.0, -80.0)' }
25
+ let(:example_box) { described_class.from_envelope(envelope) }
26
+ it 'parses and creates a Geoblacklight::BoundingBox from a Solr lat-lon' do
27
+ expect(example_box).to be_an described_class
28
+ expect(example_box.to_envelope).to eq envelope
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlacklightHeatmaps::GeometryParser do
4
+ describe '.parse' do
5
+ context 'when parsing a CQL ENVELOPE' do
6
+ it 'returns a BlacklightHeatmaps::BoundingBox' do
7
+ expect(described_class.parse('ENVELOPE(1,2,4,3)'))
8
+ .to be_an BlacklightHeatmaps::BoundingBox
9
+ end
10
+ end
11
+ context 'when parsing an X Y coordinate' do
12
+ it 'returns a BlacklightHeatmaps::Point' do
13
+ expect(described_class.parse('-180 90')).to be_an BlacklightHeatmaps::Point
14
+ end
15
+ end
16
+ context 'when parsing an unknown type' do
17
+ it 'raises an exception' do
18
+ expect do
19
+ described_class.parse('123,23 32,123')
20
+ end.to raise_error(BlacklightHeatmaps::Exceptions::UnknownSpatialDataType)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlacklightHeatmaps::GeometrySolrDocument do
4
+ subject { SolrDocument.new(fields).to_geojson(blacklight_config) }
5
+
6
+ context 'when configured' do
7
+ let(:fields) { { some_field: 'ENVELOPE(1,2,4,3)' } }
8
+ let(:blacklight_config) { double('BlacklightConfig', geometry_field: :some_field) }
9
+ it 'returns the data from the document as geoJSON' do
10
+ expect(subject).to eq '{"type":"Polygon","coordinates":[[[1.0,3.0],[1.0,4.0],[2.0,4.0],[2.0,3.0],[1.0,3.0]]]}'
11
+ end
12
+ end
13
+ context 'when not configured' do
14
+ let(:fields) { { some_field: 'ENVELOPE(1,2,4,3)' } }
15
+ let(:blacklight_config) { double('BlacklightConfig') }
16
+ it 'returns nil' do
17
+ expect(subject).to be_nil
18
+ end
19
+ end
20
+ context 'when the document has the field' do
21
+ let(:fields) { { some_field: 'ENVELOPE(1,2,4,3)' } }
22
+ let(:blacklight_config) { double('BlacklightConfig', geometry_field: :some_field) }
23
+ it 'returns the data from the document as geoJSON' do
24
+ expect(subject).to eq '{"type":"Polygon","coordinates":[[[1.0,3.0],[1.0,4.0],[2.0,4.0],[2.0,3.0],[1.0,3.0]]]}'
25
+ end
26
+ end
27
+ context 'when the document does not have the field' do
28
+ let(:fields) { { some_other_field: 'ENVELOPE(1,2,4,3)' } }
29
+ let(:blacklight_config) { double('BlacklightConfig', geometry_field: :some_field) }
30
+ it 'returns nil' do
31
+ expect(subject).to be_nil
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlacklightHeatmaps::Point do
4
+ describe '#to_geojson' do
5
+ it 'creates geojson from the point' do
6
+ expect(described_class.new(-180, 90).to_geojson)
7
+ .to eq '{"type":"Point","coordinates":[-180.0,90.0]}'
8
+ end
9
+ end
10
+ describe '.from_lng_lat' do
11
+ subject { described_class.from_lng_lat('-180 90') }
12
+ it 'instantiates an instance from a lng lat string' do
13
+ expect(subject).to be_an described_class
14
+ expect(subject.to_geojson)
15
+ .to eq '{"type":"Point","coordinates":[-180.0,90.0]}'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlacklightHeatmaps::SolrFacetHeatmapBehavior do
4
+ let(:blacklight_config) { CatalogController.blacklight_config.deep_copy }
5
+ let(:context) { CatalogController.new }
6
+
7
+ before do
8
+ allow(context).to receive(:blacklight_config).and_return(blacklight_config)
9
+ end
10
+
11
+ let(:search_builder_class) do
12
+ Class.new(Blacklight::SearchBuilder) do
13
+ include Blacklight::Solr::SearchBuilderBehavior
14
+ include BlacklightHeatmaps::SolrFacetHeatmapBehavior
15
+ end
16
+ end
17
+
18
+ let(:search_builder) { search_builder_class.new(context) }
19
+
20
+ describe '#add_solr_facet_heatmap' do
21
+ let(:solr_params) { { fq: [filter: 'stuff'], bq: [boost: 'stuff'] } }
22
+ context 'when bbox is not present' do
23
+ subject { search_builder }
24
+ it 'does not modify the solr parameters' do
25
+ expect(subject.add_solr_facet_heatmap(solr_params)).to eq solr_params
26
+ end
27
+ end
28
+ context 'when a bbox is present' do
29
+ subject { search_builder.with(bbox: '1,2,4,3') }
30
+ it 'leaves solr params in place and adds new params' do
31
+ expect(subject.add_solr_facet_heatmap(solr_params)['facet.heatmap'])
32
+ .to eq :geo_srpt
33
+ expect(subject.add_solr_facet_heatmap(solr_params)['facet.heatmap.geom'])
34
+ .to eq '["1 2" TO "4 3"]'
35
+ expect(subject.add_solr_facet_heatmap(solr_params)[:bq])
36
+ .to include(boost: 'stuff')
37
+ expect(subject.add_solr_facet_heatmap(solr_params)[:bq])
38
+ .to include('geo_srpt:"IsWithin(ENVELOPE(1.0, 4.0, 3.0, 2.0))"')
39
+ expect(subject.add_solr_facet_heatmap(solr_params)[:fq])
40
+ .to include(filter: 'stuff')
41
+ expect(subject.add_solr_facet_heatmap(solr_params)[:fq])
42
+ .to include('geo_srpt:"Intersects(ENVELOPE(1.0, 4.0, 3.0, 2.0))"')
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#bbox' do
48
+ subject { search_builder.with(bbox: '1,2,4,3') }
49
+ it 'returns the bbox parameter split on ,' do
50
+ expect(subject.bbox).to match_array %w(1 2 4 3)
51
+ end
52
+ end
53
+
54
+ describe '#bbox_as_envelope' do
55
+ subject { search_builder.with(bbox: '1,2,4,3') }
56
+ it 'returns the bbox parameter in envelope syntax' do
57
+ expect(subject.bbox_as_envelope).to eq 'ENVELOPE(1.0, 4.0, 3.0, 2.0)'
58
+ end
59
+ end
60
+
61
+ describe '#bbox_as_range' do
62
+ subject { search_builder.with(bbox: '1,2,4,3') }
63
+ it 'returns the bbox parameter in rectangle-range syntax' do
64
+ expect(subject.bbox_as_range).to eq '["1 2" TO "4 3"]'
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+
3
+ if ENV['COVERAGE'] || ENV['CI']
4
+ require 'simplecov'
5
+ require 'coveralls'
6
+
7
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
8
+ SimpleCov.start do
9
+ add_filter '/spec/'
10
+ end
11
+ end
12
+
13
+ require 'engine_cart'
14
+ EngineCart.load_application!
15
+
16
+ require 'rspec/rails'
17
+
18
+ require 'capybara/poltergeist'
19
+ Capybara.javascript_driver = :poltergeist
20
+
21
+ require 'blacklight'
22
+ require 'blacklight_heatmaps'
23
+
24
+ RSpec.configure do |c|
25
+ c.infer_spec_type_from_file_location!
26
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails/generators'
2
+
3
+ class TestAppGenerator < Rails::Generators::Base
4
+ source_root './spec/test_app_templates'
5
+
6
+ def add_gems
7
+ gem 'blacklight', '~> 6.0'
8
+
9
+ Bundler.with_clean_env do
10
+ run 'bundle install'
11
+ end
12
+ end
13
+
14
+ def run_blacklight_generator
15
+ say_status('warning', 'GENERATING BL', :yellow)
16
+
17
+ generate 'blacklight:install', '--devise'
18
+ end
19
+
20
+ # if you need to generate any additional configuration
21
+ # into the test app, this generator will be run immediately
22
+ # after setting up the application
23
+
24
+ def install_engine
25
+ generate 'blacklight_heatmaps:install'
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'catalog/_document_heatmaps.html.erb' do
4
+ let(:blacklight_config) { Blacklight::Configuration.new }
5
+ before do
6
+ allow(view).to receive(:index_map_div)
7
+ .and_return '<div id="index-map"></div>'.html_safe
8
+ end
9
+ it 'renders a div with needed elements' do
10
+ blacklight_config.configure do |config|
11
+ config.geometry_field = :geo_srpt
12
+ end
13
+ render partial: 'catalog/document_heatmaps',
14
+ locals: { blacklight_config: blacklight_config }
15
+ expect(rendered).to have_css '.blacklight-heatmaps-index-map-container'
16
+ expect(rendered).to have_css '#index-map'
17
+ expect(rendered).to have_css '#index-map-sidebar'
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'catalog/_show_leaflet_map_default.html.erb' do
4
+ let(:document) do
5
+ SolrDocument.new(geo_srpt: 'ENVELOPE(1,2,4,3)', id: 'abc123')
6
+ end
7
+ let(:blacklight_config) { Blacklight::Configuration.new }
8
+ before do
9
+ allow(view).to receive(:document).and_return(document)
10
+ end
11
+ context 'when geojson is available' do
12
+ it 'should render the leaflet container' do
13
+ blacklight_config.configure do |config|
14
+ config.geometry_field = :geo_srpt
15
+ end
16
+ render partial: 'catalog/show_leaflet_map_default',
17
+ locals: { document: document, blacklight_config: blacklight_config }
18
+ expect(rendered).to have_css '.blacklight-heatmaps-show-map'
19
+ expect(rendered).to have_css '#map-abc123'
20
+ expect(rendered).to have_css '[data-basemap]'
21
+ expect(rendered).to have_css '[data-show-map]'
22
+ expect(rendered).to have_css '[data-features]'
23
+ end
24
+ end
25
+ context 'when geojson is not available' do
26
+ it 'renders nothing' do
27
+ render partial: 'catalog/show_leaflet_map_default',
28
+ locals: { document: document, blacklight_config: blacklight_config }
29
+ expect(rendered).to eq "\n"
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,242 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blacklight_heatmaps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jack Reed
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: blacklight
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: leaflet-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: leaflet-sidebar-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.9
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.9
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '4'
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '3.4'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '4'
103
+ - !ruby/object:Gem::Dependency
104
+ name: engine_cart
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.8'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.8'
117
+ - !ruby/object:Gem::Dependency
118
+ name: solr_wrapper
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: capybara
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: poltergeist
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ description: Search and view Blacklight resources on a map.
160
+ email:
161
+ - phillipjreed@gmail.com
162
+ executables: []
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - LICENSE.txt
167
+ - README.md
168
+ - Rakefile
169
+ - app/assets/javascripts/blacklight_heatmaps/blacklight_heatmaps.js
170
+ - app/assets/javascripts/blacklight_heatmaps/default.js
171
+ - app/assets/javascripts/blacklight_heatmaps/viewers/index.js
172
+ - app/assets/javascripts/blacklight_heatmaps/viewers/show.js
173
+ - app/assets/stylesheets/blacklight_heatmaps/default.scss
174
+ - app/controllers/blacklight_heatmaps/application_controller.rb
175
+ - app/helpers/blacklight/maps_helper.rb
176
+ - app/models/concerns/blacklight_heatmaps/bounding_box.rb
177
+ - app/models/concerns/blacklight_heatmaps/exceptions.rb
178
+ - app/models/concerns/blacklight_heatmaps/geometry_parser.rb
179
+ - app/models/concerns/blacklight_heatmaps/geometry_solr_document.rb
180
+ - app/models/concerns/blacklight_heatmaps/point.rb
181
+ - app/models/concerns/blacklight_heatmaps/solr_facet_heatmap_behavior.rb
182
+ - app/views/catalog/_document_heatmaps.html.erb
183
+ - app/views/catalog/_show_leaflet_map_default.html.erb
184
+ - app/views/catalog/index.json.jbuilder
185
+ - app/views/layouts/blacklight_heatmaps/application.html.erb
186
+ - config/routes.rb
187
+ - lib/blacklight_heatmaps.rb
188
+ - lib/blacklight_heatmaps/engine.rb
189
+ - lib/blacklight_heatmaps/version.rb
190
+ - lib/generators/blacklight_heatmaps/install_generator.rb
191
+ - lib/generators/blacklight_heatmaps/templates/blacklight_heatmaps.js
192
+ - lib/generators/blacklight_heatmaps/templates/blacklight_heatmaps.scss
193
+ - lib/tasks/blacklight_heatmaps_tasks.rake
194
+ - spec/features/index_page_map_spec.rb
195
+ - spec/features/show_page_map_spec.rb
196
+ - spec/helpers/blacklight/maps_helper_spec.rb
197
+ - spec/models/concerns/blacklight_heatmaps/bounding_box_spec.rb
198
+ - spec/models/concerns/blacklight_heatmaps/geometry_parser_spec.rb
199
+ - spec/models/concerns/blacklight_heatmaps/geometry_solr_document_spec.rb
200
+ - spec/models/concerns/blacklight_heatmaps/point_spec.rb
201
+ - spec/models/concerns/blacklight_heatmaps/solr_facet_heatmap_behavior_spec.rb
202
+ - spec/spec_helper.rb
203
+ - spec/test_app_templates/lib/generators/test_app_generator.rb
204
+ - spec/views/catalog/_document_heatmaps.html.erb_spec.rb
205
+ - spec/views/catalog/_show_leaflet_map_default.html.erb_spec.rb
206
+ homepage: https://github.com/sul-dlss/blacklight_heatmaps
207
+ licenses:
208
+ - Apache
209
+ metadata: {}
210
+ post_install_message:
211
+ rdoc_options: []
212
+ require_paths:
213
+ - lib
214
+ required_ruby_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ required_rubygems_version: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: '0'
224
+ requirements: []
225
+ rubyforge_project:
226
+ rubygems_version: 2.4.5.1
227
+ signing_key:
228
+ specification_version: 4
229
+ summary: Search and view Blacklight resources on a map.
230
+ test_files:
231
+ - spec/features/index_page_map_spec.rb
232
+ - spec/features/show_page_map_spec.rb
233
+ - spec/helpers/blacklight/maps_helper_spec.rb
234
+ - spec/models/concerns/blacklight_heatmaps/bounding_box_spec.rb
235
+ - spec/models/concerns/blacklight_heatmaps/geometry_parser_spec.rb
236
+ - spec/models/concerns/blacklight_heatmaps/geometry_solr_document_spec.rb
237
+ - spec/models/concerns/blacklight_heatmaps/point_spec.rb
238
+ - spec/models/concerns/blacklight_heatmaps/solr_facet_heatmap_behavior_spec.rb
239
+ - spec/spec_helper.rb
240
+ - spec/test_app_templates/lib/generators/test_app_generator.rb
241
+ - spec/views/catalog/_document_heatmaps.html.erb_spec.rb
242
+ - spec/views/catalog/_show_leaflet_map_default.html.erb_spec.rb