blacklight-maps 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,5 +3,7 @@ require "blacklight/maps/version"
3
3
  module Blacklight
4
4
  module Maps
5
5
  require 'blacklight/maps/engine'
6
+ require 'blacklight/maps/export'
7
+ require 'blacklight/maps/geometry'
6
8
  end
7
9
  end
@@ -3,16 +3,17 @@ require 'leaflet-rails'
3
3
  require 'leaflet-markercluster-rails'
4
4
  require 'leaflet-sidebar-rails'
5
5
 
6
-
7
6
  module Blacklight
8
7
  module Maps
9
8
  class Engine < Rails::Engine
10
-
11
9
  # Set some default configurations
12
- Blacklight::Configuration.default_values[:view].maps.placename_coord_field = "placename_coords"
10
+ Blacklight::Configuration.default_values[:view].maps.type = 'bbox'
11
+ Blacklight::Configuration.default_values[:view].maps.bbox_field = 'place_bbox'
12
+ Blacklight::Configuration.default_values[:view].maps.placename_coord_field = 'placename_coords'
13
13
  Blacklight::Configuration.default_values[:view].maps.tileurl = "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
14
14
  Blacklight::Configuration.default_values[:view].maps.mapattribution = 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
15
15
  Blacklight::Configuration.default_values[:view].maps.maxzoom = 8
16
+ Blacklight::Configuration.default_values[:view].maps.placename_coord_delimiter = '-|-'
16
17
 
17
18
  # Add our helpers
18
19
  initializer 'blacklight-maps.helpers' do |app|
@@ -29,4 +30,4 @@ module Blacklight
29
30
  end
30
31
  end
31
32
  end
32
- end
33
+ end
@@ -0,0 +1,111 @@
1
+ module BlacklightMaps
2
+
3
+ # This class provides the ability to export a response document to GeoJSON.
4
+ # The export is formated as a GeoJSON FeatureCollection, where the features
5
+ # consist of an array of Point features. For more on the GeoJSON
6
+ # specification see http://geojson.org/geojson-spec.html.
7
+ #
8
+ class GeojsonExport
9
+ include BlacklightMaps
10
+
11
+ # controller is a Blacklight CatalogController object passed by a helper
12
+ # response_docs is an array of documents passed by a helper
13
+ def initialize(controller, response_docs)
14
+ @controller = controller
15
+ @response_docs = response_docs
16
+ end
17
+
18
+ def to_geojson
19
+ geojson_docs = { type: 'FeatureCollection',
20
+ features: build_geojson_features }
21
+ geojson_docs.to_json
22
+ end
23
+
24
+ private
25
+
26
+ def blacklight_maps_config
27
+ @controller.blacklight_config.view.maps
28
+ end
29
+
30
+ def type
31
+ blacklight_maps_config.type
32
+ end
33
+
34
+ def placename_coord_field
35
+ blacklight_maps_config.placename_coord_field
36
+ end
37
+
38
+ def placename_coord_delimiter
39
+ blacklight_maps_config.placename_coord_delimiter
40
+ end
41
+
42
+ def bbox_field
43
+ blacklight_maps_config.bbox_field
44
+ end
45
+
46
+ def build_geojson_features
47
+ case type
48
+ when 'placename_coord'
49
+ build_placename_coord_features
50
+ when 'bbox'
51
+ build_bbox_features
52
+ else
53
+ Rails.logger.error("Your Solr field type was not configured with a recognized type, '#{type}' is not yet supported")
54
+ end
55
+ end
56
+
57
+ # Builds the features structure for placename_coord type documents
58
+ def build_placename_coord_features
59
+ features = []
60
+ @response_docs.each do |doc|
61
+ next if doc[placename_coord_field].nil?
62
+ doc[placename_coord_field].uniq.each do |loc|
63
+ values = loc.split(placename_coord_delimiter)
64
+ features.push(
65
+ build_point_feature(values[2], values[1],
66
+ name: values[0],
67
+ html: render_leaflet_sidebar_partial(doc)))
68
+ end
69
+ end
70
+ features
71
+ end
72
+
73
+ # Builds the features structure for bbox type documents
74
+ def build_bbox_features
75
+ features = []
76
+ @response_docs.each do |doc|
77
+ next if doc[bbox_field].nil?
78
+ doc[bbox_field].uniq.each do |loc|
79
+ lnglat = Geometry::BoundingBox.from_lon_lat_string(loc).find_center
80
+ features.push(
81
+ build_point_feature(lnglat[0], lnglat[1],
82
+ html: render_leaflet_sidebar_partial(doc)))
83
+ end
84
+ end
85
+ features
86
+ end
87
+
88
+ # Render to string the partial for each individual doc
89
+ def render_leaflet_sidebar_partial(doc)
90
+ @controller.render_to_string partial: 'catalog/index_maps',
91
+ locals: { document: SolrDocument.new(doc) }
92
+ end
93
+
94
+ # Build the individual feature which is added to the FeatureCollection.
95
+ # lng is the longitude of the feature
96
+ # lat is the latitude of the feature
97
+ # *args additional arguments can be passed to the feature, these arguments
98
+ # will be reflected in the 'properties' member.
99
+ # html: "html string to show up" must be passed for the sidebar to display
100
+ # list items
101
+ def build_point_feature(lng, lat, *args)
102
+ properties = args.extract_options!
103
+ feature = { type: 'Feature',
104
+ geometry: {
105
+ type: 'Point',
106
+ coordinates: [lng.to_f, lat.to_f] },
107
+ properties: properties }
108
+ feature
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,35 @@
1
+ module BlacklightMaps
2
+
3
+ # Parent class of geospatial objects used in BlacklightMaps
4
+ class Geometry
5
+
6
+ # This class contains Bounding Box objects and methods for interacting with
7
+ # them.
8
+ class BoundingBox
9
+
10
+ # points is an array containing longitude and latitude values which
11
+ # relate to the southwest and northeast points of a bounding box
12
+ # [west, south, east, north] ([swlng, swlat, nelng, nelat]).
13
+ def initialize(points)
14
+ @west = points[0].to_f
15
+ @south = points[1].to_f
16
+ @east = points[2].to_f
17
+ @north = points[3].to_f
18
+ end
19
+
20
+ # Returns an array [lng, lat] which is the centerpoint of a BoundingBox.
21
+ def find_center
22
+ center = []
23
+ center[0] = (@west + @east) / 2
24
+ center[1] = (@south + @north) / 2
25
+ center
26
+ end
27
+
28
+ # Creates a new bounding box from from a string of points
29
+ # "-100 -50 100 50" (south west north east)
30
+ def self.from_lon_lat_string(points)
31
+ new(points.split(' '))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  module Blacklight
2
2
  module Maps
3
- VERSION = "0.0.1"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -16,10 +16,10 @@
16
16
  limitations under the License.
17
17
  -->
18
18
 
19
- <!--
19
+ <!--
20
20
  This is the Solr schema file. This file should be named "schema.xml" and
21
21
  should be in the conf directory under the solr home
22
- (i.e. ./solr/conf/schema.xml by default)
22
+ (i.e. ./solr/conf/schema.xml by default)
23
23
  or located where the classloader for the Solr webapp can find it.
24
24
 
25
25
  This example schema is the recommended starting point for users.
@@ -51,7 +51,7 @@
51
51
  version="1.4" is Solr's version number for the schema syntax and semantics. It should
52
52
  not normally be changed by applications.
53
53
  1.0: multiValued attribute did not exist, all fields are multiValued by nature
54
- 1.1: multiValued attribute introduced, false by default
54
+ 1.1: multiValued attribute introduced, false by default
55
55
  1.2: omitTermFreqAndPositions attribute introduced, true by default except for text fields.
56
56
  1.3: removed optional field compress feature
57
57
  1.4: default auto-phrase (QueryParser feature) to off
@@ -88,7 +88,7 @@
88
88
  - If sortMissingLast="false" and sortMissingFirst="false" (the default),
89
89
  then default lucene sorting will be used which places docs without the
90
90
  field first in an ascending sort and last in a descending sort.
91
- -->
91
+ -->
92
92
 
93
93
  <!--
94
94
  Default numeric field types. For faster range queries, consider the tint/tfloat/tlong/tdouble types.
@@ -115,7 +115,7 @@
115
115
 
116
116
  <!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
117
117
  is a more restricted form of the canonical representation of dateTime
118
- http://www.w3.org/TR/xmlschema-2/#dateTime
118
+ http://www.w3.org/TR/xmlschema-2/#dateTime
119
119
  The trailing "Z" designates UTC time and is mandatory.
120
120
  Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
121
121
  All other components are mandatory.
@@ -130,7 +130,7 @@
130
130
  NOW/DAY+6MONTHS+3DAYS
131
131
  ... 6 months and 3 days in the future from the start of
132
132
  the current day
133
-
133
+
134
134
  Consult the DateField javadocs for more information.
135
135
 
136
136
  Note: For faster range queries, consider the tdate type
@@ -158,11 +158,11 @@
158
158
 
159
159
  <!-- The "RandomSortField" is not used to store or search any
160
160
  data. You can declare fields of this type it in your schema
161
- to generate pseudo-random orderings of your docs for sorting
162
- purposes. The ordering is generated based on the field name
161
+ to generate pseudo-random orderings of your docs for sorting
162
+ purposes. The ordering is generated based on the field name
163
163
  and the version of the index, As long as the index version
164
164
  remains unchanged, and the same field name is reused,
165
- the ordering of the docs will be consistent.
165
+ the ordering of the docs will be consistent.
166
166
  If you want different psuedo-random orderings of documents,
167
167
  for the same version of the index, use a dynamicField and
168
168
  change the name
@@ -391,13 +391,13 @@
391
391
  <filter class="solr.TrimFilterFactory" />
392
392
  <!-- The PatternReplaceFilter gives you the flexibility to use
393
393
  Java Regular expression to replace any sequence of characters
394
- matching a pattern with an arbitrary replacement string,
394
+ matching a pattern with an arbitrary replacement string,
395
395
  which may include back references to portions of the original
396
396
  string matched by the pattern.
397
-
397
+
398
398
  See the Java Regular Expression documentation for more
399
399
  information on pattern and replacement string syntax.
400
-
400
+
401
401
  http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
402
402
  -->
403
403
  <filter class="solr.PatternReplaceFilterFactory"
@@ -405,7 +405,7 @@
405
405
  />
406
406
  </analyzer>
407
407
  </fieldType>
408
-
408
+
409
409
  <fieldtype name="phonetic" stored="false" indexed="true" class="solr.TextField" >
410
410
  <analyzer>
411
411
  <tokenizer class="solr.StandardTokenizerFactory"/>
@@ -419,7 +419,7 @@
419
419
  <!--
420
420
  The DelimitedPayloadTokenFilter can put payloads on tokens... for example,
421
421
  a token of "foo|1.4" would be indexed as "foo" with a payload of 1.4f
422
- Attributes of the DelimitedPayloadTokenFilterFactory :
422
+ Attributes of the DelimitedPayloadTokenFilterFactory :
423
423
  "delimiter" - a one character delimiter. Default is | (pipe)
424
424
  "encoder" - how to encode the following value into a playload
425
425
  float -> org.apache.lucene.analysis.payloads.FloatEncoder,
@@ -446,12 +446,12 @@
446
446
  </fieldType>
447
447
 
448
448
  <!-- since fields of this type are by default not stored or indexed,
449
- any data added to them will be ignored outright. -->
449
+ any data added to them will be ignored outright. -->
450
450
  <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
451
451
 
452
452
  <!-- This point type indexes the coordinates as separate fields (subFields)
453
453
  If subFieldType is defined, it references a type, and a dynamic field
454
- definition is created matching *___<typename>. Alternately, if
454
+ definition is created matching *___<typename>. Alternately, if
455
455
  subFieldSuffix is defined, that is used to create the subFields.
456
456
  Example: if subFieldType="double", then the coordinates would be
457
457
  indexed in fields myloc_0___double,myloc_1___double.
@@ -470,13 +470,19 @@
470
470
  See http://wiki.apache.org/solr/SpatialSearch
471
471
  -->
472
472
  <fieldtype name="geohash" class="solr.GeoHashField"/>
473
+
474
+ <fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType"
475
+ distErrPct="0.025"
476
+ maxDistErr="0.000009"
477
+ units="degrees"
478
+ />
473
479
  </types>
474
480
 
475
481
 
476
482
  <fields>
477
483
  <!-- Valid attributes for fields:
478
484
  name: mandatory - the name for the field
479
- type: mandatory - the name of a previously defined type from the
485
+ type: mandatory - the name of a previously defined type from the
480
486
  <types> section
481
487
  indexed: true if this field should be indexed (searchable or sortable)
482
488
  stored: true if this field should be retrievable
@@ -489,9 +495,9 @@
489
495
  given field.
490
496
  When using MoreLikeThis, fields used for similarity should be
491
497
  stored for best performance.
492
- termPositions: Store position information with the term vector.
498
+ termPositions: Store position information with the term vector.
493
499
  This will increase storage costs.
494
- termOffsets: Store offset information with the term vector. This
500
+ termOffsets: Store offset information with the term vector. This
495
501
  will increase storage costs.
496
502
  default: a value that should be used if no value is specified
497
503
  when adding a document.
@@ -502,7 +508,7 @@
502
508
  <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false"/>
503
509
  <!-- default, catch all search field -->
504
510
  <field name="text" type="text" indexed="true" stored="false" multiValued="true"/>
505
-
511
+
506
512
  <!-- these display fields are NOT multi-valued -->
507
513
  <field name="marc_display" type="string" indexed="false" stored="true" multiValued="false"/>
508
514
  <field name="title_display" type="string" indexed="false" stored="true" multiValued="false"/>
@@ -511,7 +517,7 @@
511
517
  <field name="subtitle_vern_display" type="string" indexed="false" stored="true" multiValued="false"/>
512
518
  <field name="author_display" type="string" indexed="false" stored="true" multiValued="false"/>
513
519
  <field name="author_vern_display" type="string" indexed="false" stored="true" multiValued="false"/>
514
-
520
+
515
521
  <!-- these fields are also used for display, so they must be stored -->
516
522
  <field name="isbn_t" type="text" indexed="true" stored="true" multiValued="true"/>
517
523
  <field name="language_facet" type="string" indexed="true" stored="true" multiValued="true" />
@@ -523,7 +529,7 @@
523
529
  <!-- pub_date sort uses new trie-based int fields, which are recommended for any int and are displayable, sortable, and range-quer
524
530
  we use 'tint' for faster range-queries. -->
525
531
  <field name="pub_date_sort" type="tint" indexed="true" stored="true" multiValued="false"/>
526
-
532
+
527
533
  <!-- format is used for facet, display, and choosing which partial to use for the show view, so it must be stored and indexed -->
528
534
  <field name="format" type="string" indexed="true" stored="true"/>
529
535
 
@@ -558,7 +564,7 @@
558
564
  <dynamicField name="*_tf" type="tfloat" indexed="true" stored="true"/>
559
565
  <dynamicField name="*_td" type="tdouble" indexed="true" stored="true"/>
560
566
  <dynamicField name="*_tdt" type="tdate" indexed="true" stored="true"/>
561
-
567
+
562
568
  <dynamicField name="*_pi" type="pint" indexed="true" stored="true"/>
563
569
 
564
570
  <dynamicField name="ignored_*" type="ignored" multiValued="true"/>
@@ -572,15 +578,18 @@
572
578
  <dynamicField name="*_unstem_search" type="text_general" indexed="true" stored="false" multiValued="true" />
573
579
  <dynamicField name="*spell" type="textSpell" indexed="true" stored="false" multiValued="true" />
574
580
 
575
- <!-- uncomment the following to ignore any fields that don't already match an existing
576
- field name or dynamic field, rather than reporting them as an error.
577
- alternately, change the type="ignored" to some other type e.g. "text" if you want
578
- unknown fields indexed and/or stored by default -->
581
+ <dynamicField name="*_pt" type="location" stored="true" indexed="true"/>
582
+ <dynamicField name="*_bbox" type="location_rpt" stored="true" indexed="true" multiValued="true"/>
583
+
584
+ <!-- uncomment the following to ignore any fields that don't already match an existing
585
+ field name or dynamic field, rather than reporting them as an error.
586
+ alternately, change the type="ignored" to some other type e.g. "text" if you want
587
+ unknown fields indexed and/or stored by default -->
579
588
  <!--dynamicField name="*" type="ignored" multiValued="true" /-->
580
-
589
+
581
590
  </fields>
582
591
 
583
- <!-- Field to use to determine and enforce document uniqueness.
592
+ <!-- Field to use to determine and enforce document uniqueness.
584
593
  Unless this field is marked with required="false", it will be a required field
585
594
  -->
586
595
  <uniqueKey>id</uniqueKey>
@@ -595,7 +604,7 @@
595
604
  is added to the index. It's used either to index the same field differently,
596
605
  or to add multiple fields to the same field for easier/faster searching. -->
597
606
  <!-- Copy Fields -->
598
-
607
+
599
608
  <!-- unstemmed fields -->
600
609
  <copyField source="title_t" dest="title_unstem_search"/>
601
610
  <copyField source="subtitle_t" dest="subtitle_unstem_search"/>
@@ -607,11 +616,11 @@
607
616
  <copyField source="subject_t" dest="subject_unstem_search"/>
608
617
  <copyField source="subject_addl_t" dest="subject_addl_unstem_search"/>
609
618
  <copyField source="subject_topic_facet" dest="subject_topic_unstem_search"/>
610
-
619
+
611
620
  <!-- sort fields -->
612
621
  <copyField source="pub_date" dest="pub_date_sort"/>
613
-
614
-
622
+
623
+
615
624
  <!-- spellcheck fields -->
616
625
  <!-- default spell check; should match fields for default request handler -->
617
626
  <!-- it won't work with a copy of a copy field -->
@@ -629,8 +638,8 @@
629
638
  <!-- subject spell check; should match fields for subject request handler -->
630
639
  <copyField source="subject_topic_facet" dest="subject_spell"/>
631
640
  <copyField source="subject_t" dest="subject_spell"/>
632
- <copyField source="subject_addl_t" dest="subject_spell"/>
633
-
641
+ <copyField source="subject_addl_t" dest="subject_spell"/>
642
+
634
643
  <!-- OpenSearch query field should match request handler search fields -->
635
644
  <copyField source="title_t" dest="opensearch_display"/>
636
645
  <copyField source="subtitle_t" dest="opensearch_display"/>
@@ -641,19 +650,19 @@
641
650
  <copyField source="author_addl_t" dest="opensearch_display"/>
642
651
  <copyField source="subject_topic_facet" dest="opensearch_display"/>
643
652
  <copyField source="subject_t" dest="opensearch_display"/>
644
- <copyField source="subject_addl_t" dest="opensearch_display"/>
653
+ <copyField source="subject_addl_t" dest="opensearch_display"/>
645
654
 
646
655
 
647
- <!-- Above, multiple source fields are copied to the [text] field.
648
- Another way to map multiple source fields to the same
649
- destination field is to use the dynamic field syntax.
656
+ <!-- Above, multiple source fields are copied to the [text] field.
657
+ Another way to map multiple source fields to the same
658
+ destination field is to use the dynamic field syntax.
650
659
  copyField also supports a maxChars to copy setting. -->
651
-
660
+
652
661
  <!-- <copyField source="*_t" dest="text" maxChars="3000"/> -->
653
662
 
654
663
  <!-- copy name to alphaNameSort, a field designed for sorting by name -->
655
664
  <!-- <copyField source="name" dest="alphaNameSort"/> -->
656
-
665
+
657
666
 
658
667
  <!-- Similarity is the scoring routine for each document vs. a query.
659
668
  A custom similarity may be specified here, but the default is fine