mapkick-rb 0.1.0 → 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: 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