mapkick-static 0.1.0
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 +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +116 -0
- data/lib/mapkick/static/area_map.rb +27 -0
- data/lib/mapkick/static/base_map.rb +99 -0
- data/lib/mapkick/static/helper.rb +13 -0
- data/lib/mapkick/static/map.rb +34 -0
- data/lib/mapkick/static/version.rb +5 -0
- data/lib/mapkick/static.rb +29 -0
- metadata +51 -0
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
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
|
+
[](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,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: []
|