mapkick-static 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1a8fd5cb74990e96209e87feab8beee6b31e1d69a42f34a10a92beb03cd5e064
4
+ data.tar.gz: d18aa7816d2e76a2cead6a487d443b88c3876d28f3885b578318e03d9c87332b
5
+ SHA512:
6
+ metadata.gz: 35dd8ef3064bc945f515119fee0fa29e0f58aad2e2042a5a23e407b6fa5cc10bdc6b5c109c61196c59fd7c71cb95f5e37aabc3bfc7e49101fccdb52bddfaac8e
7
+ data.tar.gz: e0e8577a9d1e7dd8cc6dc5b55e125f84aa03d5f0a61cda7068608cc807b12bfc7e2729d09d947e211558171d8c54a3567f0afeb1fac9fcd20441706c87064989
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 (2023-04-26)
2
+
3
+ - First commit
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Andrew Kane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Mapkick Static
2
+
3
+ Create beautiful static maps with one line of Ruby. No more fighting with mapping libraries!
4
+
5
+ [See it in action](https://chartkick.com/mapkick-static)
6
+
7
+ :fire: For JavaScript maps, check out [Mapkick](https://chartkick.com/mapkick)
8
+
9
+ [![Build Status](https://github.com/ankane/mapkick-static/workflows/build/badge.svg?branch=master)](https://github.com/ankane/mapkick-static/actions)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application’s Gemfile:
14
+
15
+ ```ruby
16
+ gem "mapkick-static"
17
+ ```
18
+
19
+ Mapkick Static uses the [Mapbox Static Images API](https://docs.mapbox.com/api/maps/static-images/). [Create a Mapbox account](https://account.mapbox.com/auth/signup/) to get an access token and set `ENV["MAPBOX_ACCESS_TOKEN"]` in your environment.
20
+
21
+ ## Maps
22
+
23
+ Point map
24
+
25
+ ```erb
26
+ <%= static_map [{latitude: 37.7829, longitude: -122.4190}] %>
27
+ ```
28
+
29
+ Area map (experimental)
30
+
31
+ ```erb
32
+ <%= static_area_map [{geometry: {type: "Polygon", coordinates: ...}}] %>
33
+ ```
34
+
35
+ ## Data
36
+
37
+ Data can be an array
38
+
39
+ ```erb
40
+ <%= static_map [{latitude: 37.7829, longitude: -122.4190}] %>
41
+ ```
42
+
43
+ ### Point Map
44
+
45
+ Use `latitude` or `lat` for latitude and `longitude`, `lon`, or `lng` for longitude
46
+
47
+ You can specify a color for each data point
48
+
49
+ ```ruby
50
+ {
51
+ latitude: ...,
52
+ longitude: ...,
53
+ color: "#f84d4d"
54
+ }
55
+ ```
56
+
57
+ ### Area Map
58
+
59
+ Use `geometry` with a GeoJSON `Polygon` or `MultiPolygon`
60
+
61
+ You can specify a color for each data point
62
+
63
+ ```ruby
64
+ {
65
+ geometry: {type: "Polygon", coordinates: ...},
66
+ color: "#0090ff"
67
+ }
68
+ ```
69
+
70
+ ## Options
71
+
72
+ Width and height
73
+
74
+ ```erb
75
+ <%= static_map data, width: 800, height: 500 %>
76
+ ```
77
+
78
+ Alt text
79
+
80
+ ```erb
81
+ <%= static_map data, alt: "Map of ..." %>
82
+ ```
83
+
84
+ Marker color
85
+
86
+ ```erb
87
+ <%= static_map data, markers: {color: "#f84d4d"} %>
88
+ ```
89
+
90
+ Map style
91
+
92
+ ```erb
93
+ <%= static_map data, style: "mapbox/outdoors-v12" %>
94
+ ```
95
+
96
+ ## History
97
+
98
+ View the [changelog](https://github.com/ankane/mapkick-static/blob/master/CHANGELOG.md)
99
+
100
+ ## Contributing
101
+
102
+ Everyone is encouraged to help improve this project. Here are a few ways you can help:
103
+
104
+ - [Report bugs](https://github.com/ankane/mapkick-static/issues)
105
+ - Fix bugs and [submit pull requests](https://github.com/ankane/mapkick-static/pulls)
106
+ - Write, clarify, or fix documentation
107
+ - Suggest or add new features
108
+
109
+ To get started with development:
110
+
111
+ ```sh
112
+ git clone https://github.com/ankane/mapkick-static.git
113
+ cd mapkick-static
114
+ bundle install
115
+ bundle exec rake test
116
+ ```
@@ -0,0 +1,27 @@
1
+ module Mapkick
2
+ module Static
3
+ class AreaMap < BaseMap
4
+ private
5
+
6
+ def generate_features(data, default_color)
7
+ default_color ||= "#0090ff"
8
+
9
+ data.map do |v|
10
+ color = v["color"] || default_color
11
+ {
12
+ type: "Feature",
13
+ # TODO round coordinates
14
+ geometry: v["geometry"],
15
+ properties: {
16
+ "fill" => color,
17
+ "fill-opacity" => 0.3,
18
+ "stroke" => color,
19
+ "stroke-width" => 1,
20
+ "stroke-opacity" => 0.7
21
+ }
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,99 @@
1
+ module Mapkick
2
+ module Static
3
+ class BaseMap
4
+ attr_reader :url, :url_2x
5
+
6
+ def initialize(data, width: 800, height: 500, markers: {}, style: "mapbox/streets-v12", alt: "Map", access_token: nil, view_context: nil)
7
+ @width = width.to_i
8
+ @height = height.to_i
9
+ @alt = alt
10
+ @view_context = view_context
11
+
12
+ prefix = "https://api.mapbox.com/styles/v1"
13
+ style = set_style(style)
14
+ geojson = create_geojson(data, markers)
15
+ overlay = "geojson(#{CGI.escape(JSON.generate(geojson))})"
16
+ viewport = set_viewport(geojson)
17
+ size = "%dx%d" % [@width.to_i, @height.to_i]
18
+ query = set_query(access_token, viewport)
19
+
20
+ url = "#{prefix}/#{style}/static/#{overlay}/#{viewport}/#{size}"
21
+ @url = "#{url}?#{query}"
22
+ @url_2x = "#{url}@2x?#{query}"
23
+
24
+ check_request_size
25
+ end
26
+
27
+ def to_s
28
+ @view_context.image_tag(url, alt: @alt, style: image_style, srcset: "#{url} 1x, #{url_2x} 2x")
29
+ end
30
+
31
+ private
32
+
33
+ def set_style(style)
34
+ style = style.delete_prefix("mapbox://styles/")
35
+ if style.count("/") != 1
36
+ raise ArgumentError, "Invalid style"
37
+ end
38
+ style.split("/", 2).map { |v| CGI.escape(v) }.join("/")
39
+ end
40
+
41
+ def create_geojson(data, markers)
42
+ data = data.map { |v| v.transform_keys(&:to_s) }
43
+ default_color = markers.transform_keys(&:to_s)["color"]
44
+ {
45
+ type: "FeatureCollection",
46
+ features: generate_features(data, default_color)
47
+ }
48
+ end
49
+
50
+ def set_viewport(geojson)
51
+ if geojson[:features].size == 1 && (geometry = geojson[:features][0][:geometry]) && geometry&.[](:type) == "MultiPoint" && geometry[:coordinates].size == 1
52
+ coordinates = geometry[:coordinates][0]
53
+ zoom = 15
54
+ "%f,%f,%d" % [round_coordinate(coordinates[0].to_f), round_coordinate(coordinates[1].to_f), zoom.to_i]
55
+ else
56
+ "auto"
57
+ end
58
+ end
59
+
60
+ def set_query(access_token, viewport)
61
+ params = {}
62
+ params[:access_token] = check_access_token(access_token || ENV["MAPBOX_ACCESS_TOKEN"])
63
+ if viewport == "auto"
64
+ params[:padding] = 40
65
+ end
66
+ URI.encode_www_form(params)
67
+ end
68
+
69
+ def check_access_token(access_token)
70
+ if !access_token
71
+ raise Error, "No access token"
72
+ elsif access_token.start_with?("sk.")
73
+ # can bypass with string keys
74
+ # but should help prevent common errors
75
+ raise Error, "Expected public access token"
76
+ elsif !access_token.start_with?("pk.")
77
+ raise Error, "Invalid access token"
78
+ end
79
+ access_token
80
+ end
81
+
82
+ # round to reduce URL size
83
+ def round_coordinate(point)
84
+ point.round(7)
85
+ end
86
+
87
+ # https://docs.mapbox.com/api/overview/#url-length-limits
88
+ def check_request_size
89
+ if @url_2x.bytesize > 8192
90
+ warn "[mapkick-static] URL exceeds 8192 byte limit of API (#{@url_2x.bytesize} bytes)"
91
+ end
92
+ end
93
+
94
+ def image_style
95
+ "width: %dpx; height: %dpx;" % [@width.to_i, @height.to_i]
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,13 @@
1
+ module Mapkick
2
+ module Static
3
+ module Helper
4
+ def static_map(data, **options)
5
+ Mapkick::Static::Map.new(data, **options, view_context: self)
6
+ end
7
+
8
+ def static_area_map(data, **options)
9
+ Mapkick::Static::AreaMap.new(data, **options, view_context: self)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ module Mapkick
2
+ module Static
3
+ class Map < BaseMap
4
+ private
5
+
6
+ def generate_features(data, default_color)
7
+ default_color ||= "#f84d4d"
8
+ default_icon = nil
9
+
10
+ data.group_by { |v| [v["color"] || default_color, v["x_icon"] || default_icon] }.map do |(color, icon), vs|
11
+ geometry = {
12
+ type: "MultiPoint",
13
+ coordinates: vs.map { |v| row_coordinates(v).map { |vi| round_coordinate(vi) } }
14
+ }
15
+
16
+ properties = {
17
+ "marker-color" => color
18
+ }
19
+ properties["marker-symbol"] = icon if icon
20
+
21
+ {
22
+ type: "Feature",
23
+ geometry: geometry,
24
+ properties: properties
25
+ }
26
+ end
27
+ end
28
+
29
+ def row_coordinates(row)
30
+ [row["longitude"] || row["lng"] || row["lon"], row["latitude"] || row["lat"]]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Mapkick
2
+ module Static
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ # stdlib
2
+ require "cgi"
3
+ require "json"
4
+ require "uri"
5
+
6
+ # maps
7
+ require_relative "static/base_map"
8
+ require_relative "static/area_map"
9
+ require_relative "static/map"
10
+
11
+ # modules
12
+ require_relative "static/helper"
13
+ require_relative "static/version"
14
+
15
+ if defined?(ActiveSupport.on_load)
16
+ ActiveSupport.on_load(:action_view) do
17
+ include Mapkick::Static::Helper
18
+ end
19
+
20
+ ActiveSupport.on_load(:action_mailer) do
21
+ include Mapkick::Static::Helper
22
+ end
23
+ end
24
+
25
+ module Mapkick
26
+ module Static
27
+ class Error < StandardError; end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mapkick-static
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Kane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: andrew@ankane.org
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - CHANGELOG.md
20
+ - LICENSE.txt
21
+ - README.md
22
+ - lib/mapkick/static.rb
23
+ - lib/mapkick/static/area_map.rb
24
+ - lib/mapkick/static/base_map.rb
25
+ - lib/mapkick/static/helper.rb
26
+ - lib/mapkick/static/map.rb
27
+ - lib/mapkick/static/version.rb
28
+ homepage: https://chartkick.com/mapkick-static
29
+ licenses:
30
+ - MIT
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubygems_version: 3.4.10
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Create beautiful static maps with one line of Ruby
51
+ test_files: []