mapkick-rb 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 142c0d5f613b1bf31098593e360bcef22fc2f67f8bec3df00eae695ffc38374a
4
- data.tar.gz: 5f9411788846f23203395f615448736c12712f4d71b0069306c0b053731e4ba7
3
+ metadata.gz: 6040b675cb3aebd2b20f20fb679d6dbd8da27282bfca83a2a3063ed8ebf9a593
4
+ data.tar.gz: 0b1de6cbd644d9d9f5eb52ec854403c47218fc2198e00a23d5d435420b2b68c4
5
5
  SHA512:
6
- metadata.gz: 3b40922653a27f565fa349d4d1687d04b2add2aacf26bcab042f6ef9a87168940e3dafe7cf39e058f93b430391fbdb484779b3fce8eec3ae4d43678d97b5fa30
7
- data.tar.gz: 17a189a1a596dfa60889ba6c8c2595faeebdeeecc4235d74adc3ebba3c8a774fcbd1347b7ce0d5c579ebb7dc363a8186a0d3763eaf2f83be3fd0ce2dd7ff9a2b
6
+ metadata.gz: f312d4524c8705ed35a2f4d6d51d3cc1651156e93ac335c22d74ae5a143687303a3062b8877c92008836c3db50fdfa62c59238cddf2448a82448f5565e73399f
7
+ data.tar.gz: da747aaa4b2bb078f981d5de52ba4c61fa9070c46e8ce6ff2364be7ec2c188077d717b31d1e69063ecbdafc2b26d4dbfb4094165a3ceba91739e8b3bdaecb588
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.1.2 (2023-02-08)
2
+
3
+ - Updated Mapkick.js to 0.2.3
4
+ - Added experimental support for area maps
5
+
1
6
  ## 0.1.1 (2023-01-24)
2
7
 
3
8
  - Updated Mapkick.js to 0.2.2
data/README.md CHANGED
@@ -16,7 +16,7 @@ Add this line to your application’s Gemfile:
16
16
  gem "mapkick-rb"
17
17
  ```
18
18
 
19
- Mapkick uses [Mapbox GL JS v1](https://github.com/mapbox/mapbox-gl-js/tree/v1.13.3). To use tiles from Mapbox, [create a Mapbox account](https://account.mapbox.com/auth/signup/) to get an access token and set `ENV["MAPBOX_ACCESS_TOKEN"]` in your environment.
19
+ Mapkick uses [Mapbox GL JS v1](https://github.com/mapbox/mapbox-gl-js/tree/v1.13.3). To use tiles from Mapbox, [create a Mapbox account](https://account.mapbox.com/auth/signup/) to get an access token and set `ENV["MAPBOX_ACCESS_TOKEN"]` in your environment (or set `Mapkick.options[:access_token]` in an initializer).
20
20
 
21
21
  Then follow the instructions for your JavaScript setup:
22
22
 
@@ -79,12 +79,18 @@ In `app/assets/javascripts/application.js`, add:
79
79
 
80
80
  ## Maps
81
81
 
82
- Create a map
82
+ Point map
83
83
 
84
84
  ```erb
85
85
  <%= js_map [{latitude: 37.7829, longitude: -122.4190}] %>
86
86
  ```
87
87
 
88
+ Area map (experimental)
89
+
90
+ ```erb
91
+ <%= area_map [{geometry: {type: "Polygon", coordinates: ...}}] %>
92
+ ```
93
+
88
94
  ## Data
89
95
 
90
96
  Data can be an array
@@ -99,11 +105,13 @@ Or a URL that returns JSON (same format as above)
99
105
  <%= js_map cities_path %>
100
106
  ```
101
107
 
102
- You can use `latitude`, `lat`, `longitude`, `lon`, and `lng`
108
+ ### Point Map
109
+
110
+ Use `latitude` or `lat` for latitude and `longitude`, `lon`, or `lng` for longitude
103
111
 
104
112
  You can specify a label and tooltip for each data point
105
113
 
106
- ```javascript
114
+ ```ruby
107
115
  {
108
116
  latitude: ...,
109
117
  longitude: ...,
@@ -112,6 +120,20 @@ You can specify a label and tooltip for each data point
112
120
  }
113
121
  ```
114
122
 
123
+ ### Area Map
124
+
125
+ Use `geometry` with a GeoJSON `Polygon` or `MultiPolygon`
126
+
127
+ You can specify a label and tooltip for each data point
128
+
129
+ ```ruby
130
+ {
131
+ geometry: {type: "Polygon", coordinates: ...},
132
+ label: "Hot Chicken Takeover",
133
+ tooltip: "5 stars"
134
+ }
135
+ ```
136
+
115
137
  ## Options
116
138
 
117
139
  Id, width, and height
@@ -120,16 +142,22 @@ Id, width, and height
120
142
  <%= js_map data, id: "cities-map", width: "800px", height: "500px" %>
121
143
  ```
122
144
 
123
- Markers
145
+ Marker color
124
146
 
125
147
  ```erb
126
148
  <%= js_map data, markers: {color: "#f84d4d"} %>
127
149
  ```
128
150
 
129
- Tooltips
151
+ Show tooltips on click instead of hover
152
+
153
+ ```erb
154
+ <%= js_map data, tooltips: {hover: false} %>
155
+ ```
156
+
157
+ Allow HTML in tooltips (must sanitize manually)
130
158
 
131
159
  ```erb
132
- <%= js_map data, tooltips: {hover: false, html: true} %>
160
+ <%= js_map data, tooltips: {html: true} %>
133
161
  ```
134
162
 
135
163
  Map style
@@ -1,7 +1,17 @@
1
1
  module Mapkick
2
2
  module Helper
3
- # don't break out options since need to merge with default options
4
3
  def js_map(data_source, **options)
4
+ mapkick_map "Map", data_source, **options
5
+ end
6
+
7
+ def area_map(data_source, **options)
8
+ mapkick_map "AreaMap", data_source, **options
9
+ end
10
+
11
+ private
12
+
13
+ # don't break out options since need to merge with default options
14
+ def mapkick_map(type, data_source, **options)
5
15
  options = Mapkick::Utils.deep_merge(Mapkick.options, options)
6
16
 
7
17
  @mapkick_map_id ||= 0
@@ -67,6 +77,7 @@ module Mapkick
67
77
 
68
78
  # js vars
69
79
  js_vars = {
80
+ type: type,
70
81
  id: element_id,
71
82
  data: data_source,
72
83
  options: options
@@ -74,7 +85,7 @@ module Mapkick
74
85
  js_vars.each_key do |k|
75
86
  js_vars[k] = Mapkick::Utils.json_escape(js_vars[k].to_json)
76
87
  end
77
- createjs = "new Mapkick.Map(%{id}, %{data}, %{options});" % js_vars
88
+ createjs = "new Mapkick[%{type}](%{id}, %{data}, %{options});" % js_vars
78
89
 
79
90
  # don't rerun JS on preview
80
91
  js = <<~JS
@@ -1,3 +1,3 @@
1
1
  module Mapkick
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  ================================================================================
2
- Mapkick.js 0.2.2
2
+ Mapkick.js 0.2.3
3
3
  ================================================================================
4
4
 
5
5
  Copyright (c) 2017-2023 Andrew Kane
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * This bundle includes:
3
3
  *
4
- * Mapkick.js v0.2.2
4
+ * Mapkick.js v0.2.3
5
5
  * https://github.com/ankane/mapkick.js
6
6
  * MIT License
7
7
  *
@@ -304,7 +304,7 @@
304
304
 
305
305
  var maps = {};
306
306
 
307
- var Map = function Map(element, data, options) {
307
+ var BaseMap = function BaseMap(element, data, options, mapType) {
308
308
  var this$1$1 = this;
309
309
 
310
310
  if (!Mapkick.library && typeof window !== "undefined") {
@@ -405,24 +405,28 @@
405
405
  element.textContent = message;
406
406
  }
407
407
 
408
+ function errorCatcher(element, data, options, callback) {
409
+ try {
410
+ callback(element, data, options);
411
+ } catch (err) {
412
+ showError(element, err.message);
413
+ throw err
414
+ }
415
+ }
416
+
408
417
  function fetchData(element, data, options, callback) {
409
418
  if (typeof data === "string") {
410
419
  getJSON(element, data, function (newData) {
411
- callback(element, newData, options);
420
+ errorCatcher(element, newData, options, callback);
412
421
  });
413
422
  } else if (typeof data === "function") {
414
- try {
415
- data(function (newData) {
416
- callback(element, newData, options);
417
- }, function (message) {
418
- showError(element, message);
419
- });
420
- } catch (err) {
421
- showError(element, "Error");
422
- throw err
423
- }
423
+ data(function (newData) {
424
+ errorCatcher(element, newData, options, callback);
425
+ }, function (message) {
426
+ showError(element, message);
427
+ });
424
428
  } else {
425
- callback(element, data, options);
429
+ errorCatcher(element, data, options, callback);
426
430
  }
427
431
  }
428
432
 
@@ -445,21 +449,44 @@
445
449
  for (var i = 0; i < data.length; i++) {
446
450
  var row = data[i];
447
451
  var properties = Object.assign({}, row);
452
+ var geometry = (void 0);
453
+
454
+ if (mapType === "point") {
455
+ if (!properties.icon) {
456
+ properties.icon = options.defaultIcon || "mapkick";
457
+ }
458
+ properties.mapkickIconSize = properties.icon === "mapkick" ? 0.5 : 1;
459
+ properties.mapkickIconAnchor = properties.icon === "mapkick" ? "bottom" : "center";
460
+ properties.mapkickIconOffset = properties.icon === "mapkick" ? [0, 10] : [0, 0];
461
+
462
+ var coordinates = rowCoordinates(row);
463
+
464
+ if (!coordinates[1]) {
465
+ throw new Error(("missing latitude (index: " + i + ")"))
466
+ }
467
+
468
+ if (!coordinates[0]) {
469
+ throw new Error(("missing longitude (index: " + i + ")"))
470
+ }
471
+
472
+ geometry = {
473
+ type: "Point",
474
+ coordinates: coordinates
475
+ };
476
+ } else {
477
+ geometry = row.geometry;
478
+
479
+ if (!geometry) {
480
+ throw new Error(("missing geometry (index: " + i + ")"))
481
+ }
448
482
 
449
- if (!properties.icon) {
450
- properties.icon = options.defaultIcon || "mapkick";
483
+ delete properties.geometry;
451
484
  }
452
- properties.mapkickIconSize = properties.icon === "mapkick" ? 0.5 : 1;
453
- properties.mapkickIconAnchor = properties.icon === "mapkick" ? "bottom" : "center";
454
- properties.mapkickIconOffset = properties.icon === "mapkick" ? [0, 10] : [0, 0];
455
485
 
456
486
  geojson.features.push({
457
487
  type: "Feature",
458
488
  id: i,
459
- geometry: {
460
- type: "Point",
461
- coordinates: rowCoordinates(row),
462
- },
489
+ geometry: geometry,
463
490
  properties: properties
464
491
  });
465
492
  }
@@ -509,42 +536,148 @@
509
536
  return geojson
510
537
  }
511
538
 
539
+ function generateLabelGeoJSON(data) {
540
+ var geojson = {
541
+ type: "FeatureCollection",
542
+ features: []
543
+ };
544
+
545
+ for (var i = 0; i < data.features.length; i++) {
546
+ var feature = data.features[i];
547
+ var coordinates = (void 0);
548
+
549
+ // use center for now
550
+ var bounds = new library.LngLatBounds();
551
+ extendBounds(bounds, feature.geometry);
552
+ if (!bounds.isEmpty()) {
553
+ var center = bounds.getCenter();
554
+ coordinates = [center.lng, center.lat];
555
+ }
556
+
557
+ if (coordinates) {
558
+ geojson.features.push({
559
+ type: "Feature",
560
+ id: i,
561
+ geometry: {
562
+ type: "Point",
563
+ coordinates: coordinates
564
+ },
565
+ properties: feature.properties
566
+ });
567
+ }
568
+ }
569
+
570
+ return geojson
571
+ }
572
+
573
+ function layerBeforeFill(map) {
574
+ // place below labels
575
+ var layers = map.getStyle().layers;
576
+ var beforeId;
577
+ for (var i = layers.length - 1; i >= 0; i--) {
578
+ var layer = layers[i];
579
+ // TODO improve
580
+ if (!(layer.metadata && layer.metadata["mapbox:featureComponent"] === "place-labels")) {
581
+ break
582
+ }
583
+ beforeId = layer.id;
584
+ }
585
+ return beforeId
586
+ }
587
+
512
588
  function addLayer(name, geojson) {
589
+ var centersById = {};
590
+
513
591
  map.addSource(name, {
514
592
  type: "geojson",
515
593
  data: geojson
516
594
  });
517
595
 
518
- // use a symbol layer for markers for performance
519
- // https://docs.mapbox.com/help/getting-started/add-markers/#approach-1-adding-markers-inside-a-map
520
- // use separate layers to prevent labels from overlapping markers
521
- map.addLayer({
522
- id: (name + "-text"),
523
- source: name,
524
- type: "symbol",
525
- layout: {
526
- "text-field": "{label}",
527
- "text-size": 11,
528
- "text-anchor": "top",
529
- "text-offset": [0, 1]
530
- },
531
- paint: {
532
- "text-halo-color": "rgba(255, 255, 255, 1)",
533
- "text-halo-width": 1
534
- }
535
- });
536
- map.addLayer({
537
- id: name,
538
- source: name,
539
- type: "symbol",
540
- layout: {
541
- "icon-image": "{icon}-15",
542
- "icon-allow-overlap": true,
543
- "icon-size": {type: "identity", property: "mapkickIconSize"},
544
- "icon-anchor": {type: "identity", property: "mapkickIconAnchor"},
545
- "icon-offset": {type: "identity", property: "mapkickIconOffset"}
596
+ if (mapType === "point") {
597
+ // use a symbol layer for markers for performance
598
+ // https://docs.mapbox.com/help/getting-started/add-markers/#approach-1-adding-markers-inside-a-map
599
+ // use separate layers to prevent labels from overlapping markers
600
+ map.addLayer({
601
+ id: (name + "-text"),
602
+ source: name,
603
+ type: "symbol",
604
+ layout: {
605
+ "text-field": "{label}",
606
+ "text-size": 11,
607
+ "text-anchor": "top",
608
+ "text-offset": [0, 1]
609
+ },
610
+ paint: {
611
+ "text-halo-color": "rgba(255, 255, 255, 1)",
612
+ "text-halo-width": 1
613
+ }
614
+ });
615
+ map.addLayer({
616
+ id: name,
617
+ source: name,
618
+ type: "symbol",
619
+ layout: {
620
+ "icon-image": "{icon}-15",
621
+ "icon-allow-overlap": true,
622
+ "icon-size": {type: "identity", property: "mapkickIconSize"},
623
+ "icon-anchor": {type: "identity", property: "mapkickIconAnchor"},
624
+ "icon-offset": {type: "identity", property: "mapkickIconOffset"}
625
+ }
626
+ });
627
+ } else {
628
+ var fillColor = markerOptions.color || "#0090ff";
629
+
630
+ var beforeId = layerBeforeFill(map);
631
+
632
+ var outlineId = name + "-outline";
633
+ map.addLayer({
634
+ id: outlineId,
635
+ source: name,
636
+ type: "line",
637
+ paint: {
638
+ "line-color": fillColor,
639
+ "line-opacity": 0.7,
640
+ "line-width": 1
641
+ }
642
+ }, beforeId);
643
+
644
+ map.addLayer({
645
+ id: name,
646
+ source: name,
647
+ type: "fill",
648
+ paint: {
649
+ "fill-color": fillColor,
650
+ "fill-opacity": 0.3
651
+ }
652
+ }, outlineId);
653
+
654
+ var labelName = name + "-text";
655
+ var labelData = generateLabelGeoJSON(geojson);
656
+
657
+ for (var i = 0; i < labelData.features.length; i++) {
658
+ var feature = labelData.features[i];
659
+ centersById[feature.id] = feature.geometry.coordinates;
546
660
  }
547
- });
661
+
662
+ map.addSource(labelName, {
663
+ type: "geojson",
664
+ data: labelData
665
+ });
666
+
667
+ map.addLayer({
668
+ id: (name + "-text"),
669
+ source: labelName,
670
+ type: "symbol",
671
+ layout: {
672
+ "text-field": "{label}",
673
+ "text-size": 11
674
+ },
675
+ paint: {
676
+ "text-halo-color": "rgba(255, 255, 255, 1)",
677
+ "text-halo-width": 1
678
+ }
679
+ });
680
+ }
548
681
 
549
682
  var hover = !("hover" in tooltipOptions) || tooltipOptions.hover;
550
683
 
@@ -597,8 +730,15 @@
597
730
  popup.options.offset = 14;
598
731
  }
599
732
 
733
+ var coordinates;
734
+ if (mapType === "point") {
735
+ coordinates = feature.geometry.coordinates;
736
+ } else {
737
+ coordinates = centersById[feature.id];
738
+ }
739
+
600
740
  // add the tooltip
601
- popup.setLngLat(feature.geometry.coordinates);
741
+ popup.setLngLat(coordinates);
602
742
  if (tooltipOptions.html) {
603
743
  popup.setHTML(tooltip);
604
744
  } else {
@@ -612,7 +752,9 @@
612
752
  popup._container.style.width = popup._container.offsetWidth + 1 + "px";
613
753
  }
614
754
 
615
- panMap(map, popup);
755
+ if (mapType !== "area") {
756
+ panMap(map, popup);
757
+ }
616
758
  };
617
759
 
618
760
  var getLatitude = function (feature) {
@@ -673,12 +815,31 @@
673
815
  });
674
816
  }
675
817
 
818
+ function extendBounds(bounds, geometry) {
819
+ if (geometry.type === "Point") {
820
+ bounds.extend(geometry.coordinates);
821
+ } else if (geometry.type === "Polygon") {
822
+ var coordinates = geometry.coordinates[0];
823
+ for (var j = 0; j < coordinates.length; j++) {
824
+ bounds.extend(coordinates[j]);
825
+ }
826
+ } else if (geometry.type === "MultiPolygon") {
827
+ var coordinates$1 = geometry.coordinates;
828
+ for (var j$1 = 0; j$1 < coordinates$1.length; j$1++) {
829
+ var polygon = coordinates$1[j$1][0];
830
+ for (var k = 0; k < polygon.length; k++) {
831
+ bounds.extend(polygon[k]);
832
+ }
833
+ }
834
+ }
835
+ }
836
+
676
837
  var generateMap = function (element, data, options) {
677
838
  var geojson = generateGeoJSON(data, options);
678
839
  options = options || {};
679
840
 
680
841
  for (var i = 0; i < geojson.features.length; i++) {
681
- bounds.extend(geojson.features[i].geometry.coordinates);
842
+ extendBounds(bounds, geojson.features[i].geometry);
682
843
  }
683
844
 
684
845
  // remove any child elements
@@ -795,11 +956,11 @@
795
956
  }
796
957
  };
797
958
 
798
- Map.prototype.getMapObject = function getMapObject () {
959
+ BaseMap.prototype.getMapObject = function getMapObject () {
799
960
  return this.map
800
961
  };
801
962
 
802
- Map.prototype.destroy = function destroy () {
963
+ BaseMap.prototype.destroy = function destroy () {
803
964
  this.stopRefresh();
804
965
 
805
966
  if (this.map) {
@@ -808,15 +969,40 @@
808
969
  }
809
970
  };
810
971
 
811
- Map.prototype.stopRefresh = function stopRefresh () {
972
+ BaseMap.prototype.stopRefresh = function stopRefresh () {
812
973
  if (this.intervalId) {
813
974
  clearInterval(this.intervalId);
814
975
  this.intervalId = null;
815
976
  }
816
977
  };
817
978
 
979
+ var Map = /*@__PURE__*/(function (BaseMap) {
980
+ function Map(element, data, options) {
981
+ BaseMap.call(this, element, data, options, "point");
982
+ }
983
+
984
+ if ( BaseMap ) Map.__proto__ = BaseMap;
985
+ Map.prototype = Object.create( BaseMap && BaseMap.prototype );
986
+ Map.prototype.constructor = Map;
987
+
988
+ return Map;
989
+ }(BaseMap));
990
+
991
+ var AreaMap = /*@__PURE__*/(function (BaseMap) {
992
+ function AreaMap(element, data, options) {
993
+ BaseMap.call(this, element, data, options, "area");
994
+ }
995
+
996
+ if ( BaseMap ) AreaMap.__proto__ = BaseMap;
997
+ AreaMap.prototype = Object.create( BaseMap && BaseMap.prototype );
998
+ AreaMap.prototype.constructor = AreaMap;
999
+
1000
+ return AreaMap;
1001
+ }(BaseMap));
1002
+
818
1003
  var Mapkick = {
819
1004
  Map: Map,
1005
+ AreaMap: AreaMap,
820
1006
  maps: maps,
821
1007
  options: {},
822
1008
  library: null
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mapkick-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-25 00:00:00.000000000 Z
11
+ date: 2023-02-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: andrew@ankane.org
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  requirements: []
50
- rubygems_version: 3.4.1
50
+ rubygems_version: 3.4.6
51
51
  signing_key:
52
52
  specification_version: 4
53
53
  summary: Create beautiful JavaScript maps with one line of Ruby