mapkick-rb 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7678cfa0645e5386090c2ce73f06ffca32e0cbe3d3643b23d241ac80036d8c9d
4
- data.tar.gz: 348c96a8b02cc983dda7fcc4a655bd2bb276d20b210290d6cce424b669449eac
3
+ metadata.gz: 6040b675cb3aebd2b20f20fb679d6dbd8da27282bfca83a2a3063ed8ebf9a593
4
+ data.tar.gz: 0b1de6cbd644d9d9f5eb52ec854403c47218fc2198e00a23d5d435420b2b68c4
5
5
  SHA512:
6
- metadata.gz: 466776e05996b2a6cd0144c7f68e627696b6bbf85742f700ceab060a347a444a8152aa2b8fe6f30d4f0ca677985e5cbc93b7ea80c9c736500ab6e609ae620f04
7
- data.tar.gz: 68f2cf1f40bdd1242a2b91ade1bfce042bf2a234c23a64ec87b9989bf6708adcbe822d11e2c477714f989ec151b15358ac082773593f0ad400730c169d12b7a2
6
+ metadata.gz: f312d4524c8705ed35a2f4d6d51d3cc1651156e93ac335c22d74ae5a143687303a3062b8877c92008836c3db50fdfa62c59238cddf2448a82448f5565e73399f
7
+ data.tar.gz: da747aaa4b2bb078f981d5de52ba4c61fa9070c46e8ce6ff2364be7ec2c188077d717b31d1e69063ecbdafc2b26d4dbfb4094165a3ceba91739e8b3bdaecb588
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.1.2 (2023-02-08)
2
+
3
+ - Updated Mapkick.js to 0.2.3
4
+ - Added experimental support for area maps
5
+
6
+ ## 0.1.1 (2023-01-24)
7
+
8
+ - Updated Mapkick.js to 0.2.2
9
+
1
10
  ## 0.1.0 (2023-01-19)
2
11
 
3
12
  - First release
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,10 +79,16 @@ 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
- <%= js_map [{latitude: 1.23, longitude: 4.56}] %>
85
+ <%= js_map [{latitude: 37.7829, longitude: -122.4190}] %>
86
+ ```
87
+
88
+ Area map (experimental)
89
+
90
+ ```erb
91
+ <%= area_map [{geometry: {type: "Polygon", coordinates: ...}}] %>
86
92
  ```
87
93
 
88
94
  ## Data
@@ -90,7 +96,7 @@ Create a map
90
96
  Data can be an array
91
97
 
92
98
  ```erb
93
- <%= js_map [{latitude: 1.23, longitude: 4.56}] %>
99
+ <%= js_map [{latitude: 37.7829, longitude: -122.4190}] %>
94
100
  ```
95
101
 
96
102
  Or a URL that returns JSON (same format as above)
@@ -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
@@ -160,7 +188,7 @@ Mapkick.options[:height] = "400px"
160
188
 
161
189
  ## Sinatra and Padrino
162
190
 
163
- Download [mapkick.bundle.js](https://raw.githubusercontent.com/ankane/mapkick/master/vendor/assets/javascripts/mapkick.js) and include it manually.
191
+ Download [mapkick.bundle.js](https://raw.githubusercontent.com/ankane/mapkick/master/vendor/assets/javascripts/mapkick.bundle.js) and include it manually.
164
192
 
165
193
  ```html
166
194
  <script src="mapkick.bundle.js"></script>
@@ -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.0"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  ================================================================================
2
- Mapkick.js 0.2.1
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.1
4
+ * Mapkick.js v0.2.3
5
5
  * https://github.com/ankane/mapkick.js
6
6
  * MIT License
7
7
  *
@@ -262,7 +262,7 @@
262
262
 
263
263
  function createMarkerImage(library, color) {
264
264
  // set height to center vertically
265
- var height = 71;
265
+ var height = 41;
266
266
  var width = 27;
267
267
  var scale = 2;
268
268
 
@@ -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,19 +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
485
 
454
486
  geojson.features.push({
455
487
  type: "Feature",
456
488
  id: i,
457
- geometry: {
458
- type: "Point",
459
- coordinates: rowCoordinates(row),
460
- },
489
+ geometry: geometry,
461
490
  properties: properties
462
491
  });
463
492
  }
@@ -507,29 +536,148 @@
507
536
  return geojson
508
537
  }
509
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
+
510
588
  function addLayer(name, geojson) {
589
+ var centersById = {};
590
+
511
591
  map.addSource(name, {
512
592
  type: "geojson",
513
593
  data: geojson
514
594
  });
515
595
 
516
- // use a symbol layer for markers for performance
517
- // https://docs.mapbox.com/help/getting-started/add-markers/#approach-1-adding-markers-inside-a-map
518
- map.addLayer({
519
- id: name,
520
- source: name,
521
- type: "symbol",
522
- layout: {
523
- "icon-image": "{icon}-15",
524
- "icon-allow-overlap": true,
525
- "icon-size": {type: "identity", property: "mapkickIconSize"},
526
- "text-field": "{label}",
527
- "text-size": 11,
528
- "text-anchor": "top",
529
- "text-offset": [0, 1],
530
- "text-allow-overlap": true
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;
531
660
  }
532
- });
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
+ }
533
681
 
534
682
  var hover = !("hover" in tooltipOptions) || tooltipOptions.hover;
535
683
 
@@ -582,8 +730,15 @@
582
730
  popup.options.offset = 14;
583
731
  }
584
732
 
733
+ var coordinates;
734
+ if (mapType === "point") {
735
+ coordinates = feature.geometry.coordinates;
736
+ } else {
737
+ coordinates = centersById[feature.id];
738
+ }
739
+
585
740
  // add the tooltip
586
- popup.setLngLat(feature.geometry.coordinates);
741
+ popup.setLngLat(coordinates);
587
742
  if (tooltipOptions.html) {
588
743
  popup.setHTML(tooltip);
589
744
  } else {
@@ -597,7 +752,9 @@
597
752
  popup._container.style.width = popup._container.offsetWidth + 1 + "px";
598
753
  }
599
754
 
600
- panMap(map, popup);
755
+ if (mapType !== "area") {
756
+ panMap(map, popup);
757
+ }
601
758
  };
602
759
 
603
760
  var getLatitude = function (feature) {
@@ -658,12 +815,31 @@
658
815
  });
659
816
  }
660
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
+
661
837
  var generateMap = function (element, data, options) {
662
838
  var geojson = generateGeoJSON(data, options);
663
839
  options = options || {};
664
840
 
665
841
  for (var i = 0; i < geojson.features.length; i++) {
666
- bounds.extend(geojson.features[i].geometry.coordinates);
842
+ extendBounds(bounds, geojson.features[i].geometry);
667
843
  }
668
844
 
669
845
  // remove any child elements
@@ -780,11 +956,11 @@
780
956
  }
781
957
  };
782
958
 
783
- Map.prototype.getMapObject = function getMapObject () {
959
+ BaseMap.prototype.getMapObject = function getMapObject () {
784
960
  return this.map
785
961
  };
786
962
 
787
- Map.prototype.destroy = function destroy () {
963
+ BaseMap.prototype.destroy = function destroy () {
788
964
  this.stopRefresh();
789
965
 
790
966
  if (this.map) {
@@ -793,15 +969,40 @@
793
969
  }
794
970
  };
795
971
 
796
- Map.prototype.stopRefresh = function stopRefresh () {
972
+ BaseMap.prototype.stopRefresh = function stopRefresh () {
797
973
  if (this.intervalId) {
798
974
  clearInterval(this.intervalId);
799
975
  this.intervalId = null;
800
976
  }
801
977
  };
802
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
+
803
1003
  var Mapkick = {
804
1004
  Map: Map,
1005
+ AreaMap: AreaMap,
805
1006
  maps: maps,
806
1007
  options: {},
807
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.0
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-20 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