map_print 0.3.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +23 -31
- data/README.md +80 -9
- data/lib/map_print/core.rb +15 -12
- data/lib/map_print/exceptions.rb +20 -0
- data/lib/map_print/geo_json_handler.rb +26 -2
- data/lib/map_print/lat_lng.rb +26 -1
- data/lib/map_print/layer_handler.rb +5 -5
- data/lib/map_print/legend_handler.rb +100 -1
- data/lib/map_print/logger.rb +52 -0
- data/lib/map_print/pdf_handler.rb +15 -3
- data/lib/map_print/pdf_handlers/images.rb +1 -1
- data/lib/map_print/pdf_handlers/texts.rb +1 -1
- data/lib/map_print/png_handler.rb +22 -7
- data/lib/map_print/png_handlers/images.rb +1 -1
- data/lib/map_print/png_handlers/texts.rb +1 -1
- data/lib/map_print/providers/base.rb +1 -6
- data/lib/map_print/scalebar_handler.rb +103 -1
- data/lib/map_print/tiles/osm_tile.rb +0 -4
- data/lib/map_print/tiles/tile.rb +3 -8
- data/lib/map_print/tiles/tile_factory.rb +10 -13
- data/lib/map_print/version.rb +1 -1
- data/lib/map_print.rb +1 -1
- data/map_print.gemspec +1 -1
- data/minimum_map.pdf +0 -0
- metadata +20 -19
- data/lib/map_print/osm/tile.rb +0 -125
- data/lib/map_print/osm/tile_factory.rb +0 -75
- data/marker.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 071baa0acdcea17783abc5c2a0cd40b5a7bead82
|
4
|
+
data.tar.gz: 397e7e367a66af93f2abc590aaf462fb4afa94d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e77dcb704714848b51dc7a2bde3f0e0c42557a0735284f005cccdf3608395e33b93f7ac2bfaefa27e508d26a947c5e71d971bff3f3032060829a4d2d0836fccc
|
7
|
+
data.tar.gz: 4afb485eb8225eec40a8d50d8853f4f8d456df08e1b87a610b9acf6fc89f833873a6d81fc80e71f8afc52a182a01bd1e50d81c6bfed58fba10e0f9f92899d63b
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
map_print (0.
|
5
|
-
geo-distance (~> 0.1)
|
4
|
+
map_print (0.9.0)
|
6
5
|
mini_magick (~> 4.3)
|
7
6
|
parallel (~> 1.6)
|
8
7
|
prawn (~> 2.0)
|
@@ -12,30 +11,16 @@ PATH
|
|
12
11
|
GEM
|
13
12
|
remote: https://rubygems.org/
|
14
13
|
specs:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
geo_point (~> 0.2.5)
|
23
|
-
geo_units (~> 0.2.4.1)
|
24
|
-
geo_calc (0.7.6)
|
25
|
-
activesupport (>= 3.0.1)
|
26
|
-
geo_units (~> 0.2.1)
|
27
|
-
i18n
|
28
|
-
require_all (~> 1.2.0)
|
29
|
-
sugar-high (~> 0.4.6.3)
|
30
|
-
geo_point (0.2.5)
|
31
|
-
geo_calc (~> 0.7.5)
|
32
|
-
geo_units (~> 0.2.1)
|
33
|
-
geo_units (0.2.4.1)
|
34
|
-
sugar-high (~> 0.4.6.2)
|
35
|
-
i18n (0.7.0)
|
14
|
+
addressable (2.4.0)
|
15
|
+
codeclimate-test-reporter (0.4.8)
|
16
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
17
|
+
crack (0.4.3)
|
18
|
+
safe_yaml (~> 1.0.0)
|
19
|
+
docile (1.1.5)
|
20
|
+
hashdiff (0.2.3)
|
36
21
|
json (1.8.3)
|
37
|
-
mini_magick (4.
|
38
|
-
minitest (5.8.
|
22
|
+
mini_magick (4.4.0)
|
23
|
+
minitest (5.8.4)
|
39
24
|
parallel (1.6.1)
|
40
25
|
pdf-core (0.6.0)
|
41
26
|
prawn (2.0.2)
|
@@ -44,24 +29,31 @@ GEM
|
|
44
29
|
prawn-fast-png (0.2.3)
|
45
30
|
prawn
|
46
31
|
rmagick
|
47
|
-
rake (10.
|
48
|
-
require_all (1.2.1)
|
32
|
+
rake (10.5.0)
|
49
33
|
rmagick (2.15.4)
|
50
|
-
|
34
|
+
safe_yaml (1.0.4)
|
35
|
+
simplecov (0.11.2)
|
36
|
+
docile (~> 1.1.0)
|
37
|
+
json (~> 1.8)
|
38
|
+
simplecov-html (~> 0.10.0)
|
39
|
+
simplecov-html (0.10.0)
|
51
40
|
thor (0.19.1)
|
52
|
-
thread_safe (0.3.5)
|
53
41
|
ttfunk (1.4.0)
|
54
|
-
|
55
|
-
|
42
|
+
webmock (1.22.6)
|
43
|
+
addressable (>= 2.3.6)
|
44
|
+
crack (>= 0.3.2)
|
45
|
+
hashdiff
|
56
46
|
|
57
47
|
PLATFORMS
|
58
48
|
ruby
|
59
49
|
|
60
50
|
DEPENDENCIES
|
61
51
|
bundler (~> 1.10)
|
52
|
+
codeclimate-test-reporter
|
62
53
|
map_print!
|
63
54
|
minitest (~> 5.8)
|
64
55
|
rake (~> 10.0)
|
56
|
+
webmock (= 1.22.6)
|
65
57
|
|
66
58
|
BUNDLED WITH
|
67
59
|
1.10.6
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# MapPrint
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/map_print.svg)](https://badge.fury.io/rb/map_print)
|
4
|
+
[![Build Status](https://travis-ci.org/afast/map_print.svg)](https://travis-ci.org/afast/map_print)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/afast/map_print/badges/gpa.svg)](https://codeclimate.com/github/afast/map_print)
|
6
|
+
[![Test Coverage](https://codeclimate.com/github/afast/map_print/badges/coverage.svg)](https://codeclimate.com/github/afast/map_print/coverage)
|
4
7
|
|
5
8
|
MapPrint allows for exporting many map sources and GeoJSON to a pdf file. Allowing to specify map element, text/image elements as well as printing a legend with the reference and a scale bar.
|
6
9
|
|
@@ -34,15 +37,62 @@ This is intended to be more of a testing method as it doesn't support all the op
|
|
34
37
|
|
35
38
|
### In ruby code
|
36
39
|
|
37
|
-
The
|
40
|
+
The most common usage will be creating a new instance with the map configuration and then call `print` with the output path. MapPrint will take the map configuration, generate the map and write the output to the output path. In the example below `./map.png`.
|
38
41
|
|
39
42
|
```ruby
|
40
|
-
MapPrint::Core.new(map_configuration).print('./map.png')
|
43
|
+
MapPrint::Core.new(map_configuration).print('./map.png')
|
44
|
+
```
|
45
|
+
|
46
|
+
`map_configuration` is a hash which contains all the necessary fields to print a map.
|
47
|
+
For detailed information about what `map_print` expects in a hash please look at the [wiki](https://github.com/afast/map_print/wiki).
|
48
|
+
|
49
|
+
The minimum hash to generate a PNG:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
map_configuration = {
|
53
|
+
png_options: {
|
54
|
+
width: 800,
|
55
|
+
height: 1000
|
56
|
+
},
|
57
|
+
map: {
|
58
|
+
sw: {
|
59
|
+
lat: -35.026862,
|
60
|
+
lng: -58.425003
|
61
|
+
},
|
62
|
+
ne: {
|
63
|
+
lat: -29.980172,
|
64
|
+
lng: -52.959305
|
65
|
+
},
|
66
|
+
zoom: 9,
|
67
|
+
layers: [{type: 'osm'}]
|
68
|
+
}
|
69
|
+
}
|
70
|
+
```
|
71
|
+
|
72
|
+
The minimum hash to generate a PDF:
|
41
73
|
|
42
|
-
|
74
|
+
```ruby
|
75
|
+
map_configuration = {
|
76
|
+
format: 'pdf',
|
77
|
+
map: {
|
78
|
+
sw: {
|
79
|
+
lat: -35.026862,
|
80
|
+
lng: -58.425003
|
81
|
+
},
|
82
|
+
ne: {
|
83
|
+
lat: -29.980172,
|
84
|
+
lng: -52.959305
|
85
|
+
},
|
86
|
+
zoom: 9,
|
87
|
+
layers: [{type: 'osm'}]
|
88
|
+
}
|
89
|
+
}
|
90
|
+
```
|
91
|
+
|
92
|
+
A full example showing all the available options:
|
43
93
|
|
44
94
|
```ruby
|
45
|
-
|
95
|
+
map_configuration = {
|
46
96
|
format: 'pdf', # pdf or png
|
47
97
|
pdf_options: {
|
48
98
|
page_size: 'A4', # A0-10, B0-10, C0-10
|
@@ -123,12 +173,29 @@ BASIC_MAP = {
|
|
123
173
|
text: "some text",
|
124
174
|
position: {x: 50, y: 50 },
|
125
175
|
box_size: {width: 50, height: 50},
|
126
|
-
options: {
|
176
|
+
options: {
|
177
|
+
fill_color: '#ffffff',
|
178
|
+
color: '#000000',
|
179
|
+
font: 'Arial',
|
180
|
+
pointsize: '16',
|
181
|
+
gravity: 'NorthWest',
|
182
|
+
}
|
127
183
|
}
|
128
184
|
],
|
129
|
-
legend: {
|
185
|
+
legend: {
|
130
186
|
position: {x: 50, y: 50},
|
131
187
|
size: {width: 50, height: 50},
|
188
|
+
image_size: {width: 16, height: 16},
|
189
|
+
textbox_size: {width: 40, height: 16},
|
190
|
+
textbox_style: {
|
191
|
+
fill_color: '#ffffff',
|
192
|
+
color: '#000000',
|
193
|
+
font: 'Arial',
|
194
|
+
pointsize: '16',
|
195
|
+
gravity: 'NorthWest',
|
196
|
+
},
|
197
|
+
orientation: 'horizontal', # horizontal, vertical
|
198
|
+
overflow: 'hidden', # expand, hidden, compact
|
132
199
|
columns: 5,
|
133
200
|
rows: 5,
|
134
201
|
elements: [{
|
@@ -136,10 +203,14 @@ BASIC_MAP = {
|
|
136
203
|
text: 'text'
|
137
204
|
}]
|
138
205
|
},
|
139
|
-
scalebar: {
|
206
|
+
scalebar: {
|
140
207
|
unit: 'meters', # meters, km, miles, feet
|
141
|
-
position: {x:
|
142
|
-
size: {width:
|
208
|
+
position: {x: 500, y: 550},
|
209
|
+
size: {width: 200, height: 40},
|
210
|
+
padding: {top: 5, right: 5, bottom: 5, left: 5},
|
211
|
+
bar_height: 10,
|
212
|
+
background_color: 'black',
|
213
|
+
background_opacity: 0.4
|
143
214
|
}
|
144
215
|
}
|
145
216
|
```
|
data/lib/map_print/core.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative 'logger'
|
1
2
|
require_relative 'lat_lng'
|
2
3
|
require_relative 'tiles/tile'
|
3
4
|
require_relative 'tiles/tile_factory'
|
@@ -29,6 +30,10 @@ module MapPrint
|
|
29
30
|
@texts = args[:texts]
|
30
31
|
@legend = args[:legend]
|
31
32
|
@scalebar = args[:scalebar]
|
33
|
+
raise ParameterError.new("Please indicate the southwest point for the map ({map: {sw: {lat: -35.026862, lng: -58.425003}}})") unless @map && @map[:sw] && @map[:sw][:lat] && @map[:sw][:lng]
|
34
|
+
raise ParameterError.new("Please indicate the northeast point for the map ({map: {ne: {lat: -29.980172, lng: -52.959305}}})") unless @map[:ne] && @map[:ne][:lat] && @map[:ne][:lng]
|
35
|
+
raise ParameterError.new("Please indicate the zoom level for the map ({map: {zoom: 9})") unless @map[:zoom]
|
36
|
+
raise ParameterError.new("Please indicate layers to be printed for the map ({map: {layers: [{type: 'osm'}]})") unless @map[:layers].is_a?(Array)
|
32
37
|
end
|
33
38
|
|
34
39
|
def print(output_path)
|
@@ -36,10 +41,9 @@ module MapPrint
|
|
36
41
|
|
37
42
|
if @format == 'pdf'
|
38
43
|
handler = PdfHandler.new(self)
|
39
|
-
elsif @format == 'png'
|
40
|
-
handler = PngHandler.new(self)
|
41
44
|
else
|
42
|
-
|
45
|
+
Logger.warn 'Did not specify format, defaulting to png'
|
46
|
+
handler = PngHandler.new(self)
|
43
47
|
end
|
44
48
|
|
45
49
|
handler.print
|
@@ -47,11 +51,7 @@ module MapPrint
|
|
47
51
|
end
|
48
52
|
|
49
53
|
def print_layers
|
50
|
-
|
51
|
-
|
52
|
-
FileUtils.cp file.path, 'layers.png' if defined?(DEBUG)
|
53
|
-
|
54
|
-
file
|
54
|
+
LayerHandler.new(@map[:layers], @map[:sw], @map[:ne], @map[:zoom]).process
|
55
55
|
end
|
56
56
|
|
57
57
|
def print_geojson(map_image)
|
@@ -67,12 +67,15 @@ module MapPrint
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def print_scalebar
|
70
|
+
if @scalebar
|
71
|
+
ScalebarHandler.new(@scalebar, @map[:zoom]).process
|
72
|
+
end
|
70
73
|
end
|
71
74
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
75
|
+
def print_legend
|
76
|
+
if @legend
|
77
|
+
LegendHandler.new(@legend).process
|
78
|
+
end
|
76
79
|
end
|
77
80
|
end
|
78
81
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MapPrint
|
2
|
+
class GeoJSONHandlerError < StandardError; end
|
3
|
+
class InvalidGeoJSON < GeoJSONHandlerError; end
|
4
|
+
class NoPointImage < GeoJSONHandlerError; end
|
5
|
+
class NoGeometryPresent < GeoJSONHandlerError; end
|
6
|
+
class FeatureNotImplemented < GeoJSONHandlerError; end
|
7
|
+
|
8
|
+
class LegendHandlerError < StandardError; end
|
9
|
+
class NoLegendData < LegendHandlerError; end
|
10
|
+
class InvalidSize < LegendHandlerError; end
|
11
|
+
class MissingLayoutInformation < LegendHandlerError; end
|
12
|
+
|
13
|
+
class ScalebarHandlerError < StandardError; end
|
14
|
+
class NoScalebarData < ScalebarHandlerError; end
|
15
|
+
class InvalidScalebarSize < ScalebarHandlerError; end
|
16
|
+
class InvalidScalebarZoom < ScalebarHandlerError; end
|
17
|
+
|
18
|
+
class CoreError < StandardError; end
|
19
|
+
class ParameterError < CoreError; end
|
20
|
+
end
|
@@ -10,6 +10,8 @@ module MapPrint
|
|
10
10
|
@height = height
|
11
11
|
@width = width
|
12
12
|
@geojson = JSON[geojson]
|
13
|
+
rescue JSON::GeneratorError, JSON::ParserError
|
14
|
+
raise InvalidGeoJSON.new("Invalid GeoJSON: #{geojson.inspect}")
|
13
15
|
end
|
14
16
|
|
15
17
|
def process
|
@@ -25,23 +27,26 @@ module MapPrint
|
|
25
27
|
tempfile.close
|
26
28
|
end
|
27
29
|
|
30
|
+
private
|
28
31
|
def draw_geojson
|
29
32
|
if @geojson['type'] == 'Feature'
|
30
33
|
feature(@geojson['geometry'], @geojson['properties'])
|
31
34
|
elsif @geojson['type'] == 'FeatureCollection'
|
32
35
|
feature_collection(@geojson['features'])
|
33
36
|
else
|
34
|
-
|
37
|
+
Logger.warn "Warning, expected type Feature with #{@geojson['type'].inspect} inside geometry and drawing properties, like: {'type': 'Feature', 'geometry':#{@geojson.to_json}, 'properties':{'image': 'path/or/url/to/image'}}"
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
41
|
def feature(geometry, properties={})
|
42
|
+
raise NoGeometryPresent.new("No geometry present for this feature") if geometry.nil?
|
39
43
|
case geometry['type']
|
40
44
|
when 'Feature'
|
41
45
|
feature(geometry['geometry'], geometry['properties'])
|
42
46
|
when 'FeatureCollection'
|
43
47
|
feature_collection(geometry['features'])
|
44
48
|
when 'Point'
|
49
|
+
raise NoPointImage.new("Missing image in point geometry") unless properties && properties['image']
|
45
50
|
point(geometry, properties['image'])
|
46
51
|
when 'LineString'
|
47
52
|
line_string(geometry, properties)
|
@@ -55,7 +60,11 @@ module MapPrint
|
|
55
60
|
multi_polygon(geometry, properties)
|
56
61
|
when 'GeometryCollection'
|
57
62
|
geometry_collection(geometry, properties)
|
63
|
+
else
|
64
|
+
Logger.warn "Feature type '#{geometry['type']}' not implemented!"
|
58
65
|
end
|
66
|
+
rescue GeoJSONHandlerError => ex
|
67
|
+
Logger.warn ex
|
59
68
|
end
|
60
69
|
|
61
70
|
def feature_collection(features)
|
@@ -103,7 +112,22 @@ module MapPrint
|
|
103
112
|
end
|
104
113
|
end
|
105
114
|
|
106
|
-
|
115
|
+
def multi_point(geometry, properties)
|
116
|
+
raise FeatureNotImplemented.new("Please consider contributing!")
|
117
|
+
end
|
118
|
+
|
119
|
+
def multi_line_string(geometry, properties)
|
120
|
+
raise FeatureNotImplemented.new("Please consider contributing!")
|
121
|
+
end
|
122
|
+
|
123
|
+
def multi_polygon(geometry, properties)
|
124
|
+
raise FeatureNotImplemented.new("Please consider contributing!")
|
125
|
+
end
|
126
|
+
|
127
|
+
def geometry_collection(geometry, properties)
|
128
|
+
raise FeatureNotImplemented.new("Please consider contributing!")
|
129
|
+
end
|
130
|
+
|
107
131
|
def draw_options(properties, line=true)
|
108
132
|
options = ''
|
109
133
|
if properties['stroke'] || properties['stroke'].nil?
|
data/lib/map_print/lat_lng.rb
CHANGED
@@ -1,7 +1,30 @@
|
|
1
1
|
module MapPrint
|
2
2
|
class LatLng
|
3
|
+
EARTH_RADIUS_IN_METERS = 6_376_772.71
|
4
|
+
|
3
5
|
attr_accessor :lat, :lng
|
4
6
|
|
7
|
+
class << self
|
8
|
+
def distance_between(from, to)
|
9
|
+
return 0.0 if from == to # fixes a "zero-distance" bug
|
10
|
+
|
11
|
+
distance_between_sphere(from, to)
|
12
|
+
end
|
13
|
+
|
14
|
+
def distance_between_sphere(from, to)
|
15
|
+
lat_sin = Math.sin(deg2rad(from.lat)) * Math.sin(deg2rad(to.lat))
|
16
|
+
lat_cos = Math.cos(deg2rad(from.lat)) * Math.cos(deg2rad(to.lat))
|
17
|
+
lng_cos = Math.cos(deg2rad(to.lng) - deg2rad(from.lng))
|
18
|
+
EARTH_RADIUS_IN_METERS * Math.acos(lat_sin + lat_cos * lng_cos)
|
19
|
+
rescue Errno::EDOM
|
20
|
+
0.0
|
21
|
+
end
|
22
|
+
|
23
|
+
def deg2rad(degrees)
|
24
|
+
degrees.to_f / 180.0 * Math::PI
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
5
28
|
def initialize(lat, lng)
|
6
29
|
@lat = lat
|
7
30
|
@lng = lng
|
@@ -18,7 +41,9 @@ module MapPrint
|
|
18
41
|
@get_slippy_map_tile_number = {x: x.to_i, y: y.to_i, offset: {x: x - x.to_i, y: y - y.to_i}}
|
19
42
|
end
|
20
43
|
|
21
|
-
def
|
44
|
+
def distance_to(other)
|
45
|
+
self.class.distance_between(self, other)
|
22
46
|
end
|
47
|
+
alias_method :distance_from, :distance_to
|
23
48
|
end
|
24
49
|
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
module MapPrint
|
2
2
|
class LayerHandler
|
3
3
|
PROVIDERS = {
|
4
|
-
'bing' =>
|
5
|
-
'osm' =>
|
4
|
+
'bing' => Providers::Bing,
|
5
|
+
'osm' => Providers::OpenStreetMap
|
6
6
|
}
|
7
7
|
|
8
8
|
def initialize(layers, south_west, north_east, zoom)
|
9
9
|
@layers = layers.sort_by { |layer| layer[:level] }
|
10
|
-
@south_west =
|
11
|
-
@north_east =
|
10
|
+
@south_west = LatLng.new(south_west[:lat], south_west[:lng])
|
11
|
+
@north_east = LatLng.new(north_east[:lat], north_east[:lng])
|
12
12
|
@zoom = zoom
|
13
13
|
end
|
14
14
|
|
15
15
|
def process
|
16
16
|
@layers.each do |layer|
|
17
17
|
provider_class = PROVIDERS[layer[:type]]
|
18
|
-
provider = provider_class.new(@south_west, @north_east, @zoom, layer[:urls].first)
|
18
|
+
provider = provider_class.new(@south_west, @north_east, @zoom, layer[:urls] && layer[:urls].first)
|
19
19
|
layer[:image] = provider.download
|
20
20
|
end
|
21
21
|
|
@@ -1,9 +1,108 @@
|
|
1
1
|
module MapPrint
|
2
2
|
class LegendHandler
|
3
|
-
def initialize(legend
|
3
|
+
def initialize(legend)
|
4
|
+
@legend = legend
|
5
|
+
validate_data!
|
4
6
|
end
|
5
7
|
|
6
8
|
def process
|
9
|
+
size = @legend[:size]
|
10
|
+
tempfile = Tempfile.new ['legend', '.png']
|
11
|
+
`convert -size #{size[:width]}x#{size[:height]} xc:white #{tempfile.path}`
|
12
|
+
image = MiniMagick::Image.new tempfile.path
|
13
|
+
|
14
|
+
x_step = size[:width] / @legend[:columns]
|
15
|
+
y_step = size[:height] / @legend[:rows]
|
16
|
+
image_geometry = ''
|
17
|
+
textbox_offset = 0
|
18
|
+
text_size = "#{@legend[:textbox_size][:width]}x#{@legend[:textbox_size][:height]}" if @legend[:textbox_size]
|
19
|
+
|
20
|
+
if @legend[:image_size]
|
21
|
+
image_geometry += "#{@legend[:image_size][:width]}x#{@legend[:image_size][:height]}"
|
22
|
+
textbox_offset += @legend[:image_size][:width] if @legend[:image_size][:width]
|
23
|
+
end
|
24
|
+
textbox_offset += @legend[:textbox_offset] if @legend[:textbox_offset]
|
25
|
+
|
26
|
+
if @legend[:orientation] == 'vertical'
|
27
|
+
print_vertical(image, x_step, y_step, image_geometry, textbox_offset, text_size)
|
28
|
+
else
|
29
|
+
print_horizontal(image, x_step, y_step, image_geometry, textbox_offset, text_size)
|
30
|
+
end
|
31
|
+
image
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def validate_data!
|
36
|
+
raise NoLegendData.new('No legend data present') if @legend.nil? || @legend.empty?
|
37
|
+
raise InvalidSize.new('No legend width present') unless @legend[:size] && @legend[:size][:width]
|
38
|
+
raise InvalidSize.new('No legend height present') unless @legend[:size][:height]
|
39
|
+
raise MissingLayoutInformation.new('Missing column layout information') unless @legend[:columns]
|
40
|
+
raise MissingLayoutInformation.new('Missing rows layout information') unless @legend[:rows]
|
41
|
+
end
|
42
|
+
|
43
|
+
def print_vertical(legend_image, x_step, y_step, image_geometry, textbox_offset, text_size)
|
44
|
+
return unless @legend[:elements].is_a?(Array)
|
45
|
+
x = 0
|
46
|
+
y = 0
|
47
|
+
z = 1
|
48
|
+
|
49
|
+
@legend[:elements].each do |legend_item|
|
50
|
+
image_file = MiniMagick::Image.open(legend_item[:image])
|
51
|
+
result = legend_image.composite(image_file) do |c|
|
52
|
+
c.geometry image_geometry + "+#{x}+#{y}"
|
53
|
+
end
|
54
|
+
result.write legend_image.path
|
55
|
+
|
56
|
+
position = "#{x + textbox_offset},#{y}"
|
57
|
+
draw_text(legend_image, legend_item[:text], position, text_size)
|
58
|
+
|
59
|
+
if z % @legend[:rows] == 0
|
60
|
+
x += x_step
|
61
|
+
y = 0
|
62
|
+
else
|
63
|
+
y += y_step
|
64
|
+
end
|
65
|
+
z += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def print_horizontal(legend_image, x_step, y_step, image_geometry, textbox_offset, text_size)
|
70
|
+
return unless @legend[:elements].is_a?(Array)
|
71
|
+
x = 0
|
72
|
+
y = 0
|
73
|
+
z = 1
|
74
|
+
|
75
|
+
@legend[:elements].each do |legend_item|
|
76
|
+
image_file = MiniMagick::Image.open(legend_item[:image])
|
77
|
+
result = legend_image.composite(image_file) do |c|
|
78
|
+
c.geometry image_geometry + "+#{x}+#{y}"
|
79
|
+
end
|
80
|
+
result.write legend_image.path
|
81
|
+
|
82
|
+
position = "#{x + textbox_offset},#{y}"
|
83
|
+
draw_text(legend_image, legend_item[:text], position, text_size)
|
84
|
+
|
85
|
+
if z % @legend[:columns] == 0
|
86
|
+
y += y_step
|
87
|
+
x = 0
|
88
|
+
else
|
89
|
+
x += x_step
|
90
|
+
end
|
91
|
+
z += 1
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def draw_text(image, text, position, text_size)
|
96
|
+
options = @legend[:textbox_style] || {}
|
97
|
+
image.combine_options do |c|
|
98
|
+
c.fill options[:fill_color] if options[:fill_color]
|
99
|
+
c.stroke options[:color] if options[:color]
|
100
|
+
c.font options[:font] || 'Arial'
|
101
|
+
c.pointsize options[:pointsize] if options[:pointsize]
|
102
|
+
c.gravity options[:gravity] || 'NorthWest'
|
103
|
+
c.size text_size
|
104
|
+
c.draw "text #{position} '#{text}'"
|
105
|
+
end
|
7
106
|
end
|
8
107
|
end
|
9
108
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module MapPrint
|
5
|
+
class Logger
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@logger = ::Logger.new File.new('map_print.log', 'a+')
|
10
|
+
end
|
11
|
+
|
12
|
+
def debug(*args)
|
13
|
+
@logger.debug *args
|
14
|
+
end
|
15
|
+
|
16
|
+
def info(*args)
|
17
|
+
@logger.info *args
|
18
|
+
end
|
19
|
+
|
20
|
+
def warn(*args)
|
21
|
+
@logger.warn *args
|
22
|
+
end
|
23
|
+
|
24
|
+
def error(*args)
|
25
|
+
@logger.error *args
|
26
|
+
end
|
27
|
+
|
28
|
+
def fatal(*args)
|
29
|
+
@logger.fatal *args
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.debug(*args)
|
33
|
+
self.instance.debug *args
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.info(*args)
|
37
|
+
self.instance.info *args
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.warn(*args)
|
41
|
+
self.instance.warn *args
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.error(*args)
|
45
|
+
self.instance.error *args
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.fatal(*args)
|
49
|
+
self.instance.fatal *args
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -16,19 +16,31 @@ module MapPrint
|
|
16
16
|
print_map
|
17
17
|
print_images(@context.images, @pdf)
|
18
18
|
print_texts(@context.texts, @pdf)
|
19
|
-
|
19
|
+
|
20
|
+
scalebar_image = @context.print_scalebar
|
21
|
+
if scalebar_image
|
22
|
+
@pdf.image scalebar_image.path, at: [@context.scalebar[:position][:x], @pdf.bounds.top - @context.scalebar[:position][:y]]
|
23
|
+
end
|
24
|
+
|
25
|
+
legend_image = @context.print_legend
|
26
|
+
if legend_image
|
27
|
+
@pdf.image legend_image.path, at: [@context.legend[:position][:x], @pdf.bounds.top - @context.legend[:position][:y]]
|
28
|
+
end
|
20
29
|
|
21
30
|
@pdf.render_file(@context.output_path)
|
22
31
|
end
|
23
32
|
|
33
|
+
private
|
24
34
|
def print_map
|
25
35
|
map_image = @context.print_layers
|
26
36
|
map_image = @context.print_geojson(MiniMagick::Image.new(map_image.path))
|
27
37
|
|
28
|
-
size = @context.map[:size]
|
38
|
+
size = @context.map[:size] || {}
|
29
39
|
size[:width] ||= map_image.width
|
30
40
|
size[:height] ||= map_image.height
|
31
|
-
|
41
|
+
|
42
|
+
position = @context.map[:position] || {}
|
43
|
+
@pdf.image map_image.path, at: [position[:x] || 0, @pdf.bounds.top - (position[:y] || 0)], fit: size.values
|
32
44
|
end
|
33
45
|
end
|
34
46
|
end
|
@@ -11,14 +11,25 @@ module MapPrint
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def print
|
14
|
-
|
14
|
+
raise ParameterError.new('Missing png_options width attribute') unless @context.png_options && @context.png_options[:width]
|
15
|
+
raise ParameterError.new('Missing png_options height attribute') unless @context.png_options[:height]
|
16
|
+
`convert -size #{@context.png_options[:width]}x#{@context.png_options[:height]} xc:#{@context.png_options[:background_color] || 'transparent'} #{@context.output_path}`
|
15
17
|
@png = MiniMagick::Image.new @context.output_path
|
16
18
|
|
17
19
|
print_map
|
18
20
|
|
19
21
|
print_images(@context.images, @png)
|
20
22
|
print_texts(@context.texts, @png)
|
21
|
-
|
23
|
+
|
24
|
+
scalebar_image = @context.print_scalebar
|
25
|
+
if scalebar_image
|
26
|
+
overlay_image(MiniMagick::Image.new(scalebar_image.path), @context.scalebar[:position])
|
27
|
+
end
|
28
|
+
|
29
|
+
legend_image = @context.print_legend
|
30
|
+
if legend_image
|
31
|
+
overlay_image(MiniMagick::Image.new(legend_image.path), @context.legend[:position])
|
32
|
+
end
|
22
33
|
end
|
23
34
|
|
24
35
|
def print_map
|
@@ -33,12 +44,16 @@ module MapPrint
|
|
33
44
|
geometry += size[:height].to_s if size[:height]
|
34
45
|
end
|
35
46
|
|
36
|
-
|
37
|
-
|
38
|
-
|
47
|
+
overlay_image(map_image, @context.map[:position], geometry)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def overlay_image(image, position, size_geometry='')
|
52
|
+
geometry = size_geometry
|
53
|
+
geometry += "+#{position[:x] || 0}+#{position[:y] || 0}" if position
|
39
54
|
|
40
|
-
result = @png.composite(
|
41
|
-
c.geometry geometry
|
55
|
+
result = @png.composite(image) do |c|
|
56
|
+
c.geometry geometry unless geometry.nil? || geometry.empty?
|
42
57
|
end
|
43
58
|
result.write @context.output_path
|
44
59
|
end
|
@@ -2,12 +2,9 @@ require 'tempfile'
|
|
2
2
|
|
3
3
|
module MapPrint
|
4
4
|
module Providers
|
5
|
-
|
6
5
|
class Base
|
7
6
|
|
8
|
-
attr_accessor :provider
|
9
|
-
|
10
|
-
attr_accessor :south_west, :north_east, :zoom
|
7
|
+
attr_accessor :provider, :south_west, :north_east, :zoom
|
11
8
|
|
12
9
|
def initialize(south_west, north_east, zoom, base_url=nil)
|
13
10
|
@south_west, @north_east, @zoom = south_west, north_east, zoom
|
@@ -48,8 +45,6 @@ module MapPrint
|
|
48
45
|
file.close
|
49
46
|
result_file
|
50
47
|
end
|
51
|
-
|
52
48
|
end
|
53
|
-
|
54
49
|
end
|
55
50
|
end
|
@@ -1,9 +1,111 @@
|
|
1
|
+
require_relative 'png_handlers/texts'
|
1
2
|
module MapPrint
|
2
3
|
class ScalebarHandler
|
3
|
-
|
4
|
+
include MapPrint::PngHandlers::Texts
|
5
|
+
|
6
|
+
ZOOM_METERS_PER_PIXEL = {
|
7
|
+
0 => 156543.03,
|
8
|
+
1 => 78271.52,
|
9
|
+
2 => 39135.76,
|
10
|
+
3 => 19567.88,
|
11
|
+
4 => 9783.94,
|
12
|
+
5 => 4891.97,
|
13
|
+
6 => 2445.98,
|
14
|
+
7 => 1222.99,
|
15
|
+
8 => 611.50,
|
16
|
+
9 => 305.75,
|
17
|
+
10 => 152.87,
|
18
|
+
11 => 76.437,
|
19
|
+
12 => 38.219,
|
20
|
+
13 => 19.109,
|
21
|
+
14 => 9.5546,
|
22
|
+
15 => 4.7773,
|
23
|
+
16 => 2.3887,
|
24
|
+
17 => 1.1943,
|
25
|
+
18 => 0.5972
|
26
|
+
}
|
27
|
+
def initialize(scalebar, zoom)
|
28
|
+
@scalebar = scalebar
|
29
|
+
@zoom = zoom
|
30
|
+
if @scalebar[:padding]
|
31
|
+
@padding_left = @scalebar[:padding][:left] || 0
|
32
|
+
@padding_right = @scalebar[:padding][:right] || 0
|
33
|
+
@padding_top = @scalebar[:padding][:top] || 0
|
34
|
+
@padding_bottom = @scalebar[:padding][:bottom] || 0
|
35
|
+
else
|
36
|
+
@padding_left = 0
|
37
|
+
@padding_right = 0
|
38
|
+
@padding_top = 0
|
39
|
+
@padding_bottom = 0
|
40
|
+
end
|
41
|
+
validate_data!
|
4
42
|
end
|
5
43
|
|
6
44
|
def process
|
45
|
+
size = @scalebar[:size]
|
46
|
+
tempfile = Tempfile.new ['scalebar', '.png']
|
47
|
+
`convert -alpha on -channel a -evaluate set #{(@scalebar[:background_opacity] || 1) *100}% -density 300 -size #{size[:width]}x#{size[:height]} xc:#{@scalebar[:background_color] || 'transparent'} #{tempfile.path}`
|
48
|
+
image = MiniMagick::Image.new tempfile.path
|
49
|
+
|
50
|
+
pixels_for_distance = get_distance_in_units
|
51
|
+
quarter = (size[:width] - @padding_left - @padding_right) / 4
|
52
|
+
|
53
|
+
y_position = size[:height] - (@scalebar[:bar_height] || 10) - @padding_bottom
|
54
|
+
image.combine_options do |c|
|
55
|
+
c.stroke 'black'
|
56
|
+
c.fill 'white'
|
57
|
+
c.draw "rectangle #{@padding_left},#{size[:height] - @padding_bottom} #{@padding_left + quarter},#{y_position}"
|
58
|
+
c.fill 'black'
|
59
|
+
c.draw "rectangle #{@padding_left + quarter},#{size[:height] - @padding_bottom} #{@padding_left + 2*quarter},#{y_position}"
|
60
|
+
c.fill 'white'
|
61
|
+
c.draw "rectangle #{@padding_left + 2*quarter},#{size[:height] - @padding_bottom} #{@padding_left + 3*quarter},#{y_position}"
|
62
|
+
c.fill 'black'
|
63
|
+
c.draw "rectangle #{@padding_left + 3*quarter},#{size[:height] - @padding_bottom} #{@padding_left + 4*quarter},#{y_position}"
|
64
|
+
end
|
65
|
+
|
66
|
+
text_options = { pointsize: 12, gravity: 'NorthWest' }
|
67
|
+
draw_text(image, "0", "#{@padding_left},#{@padding_top}", text_options)
|
68
|
+
draw_text(image, (pixels_for_distance/4).round(-2).to_s, "#{-quarter + @padding_left - @padding_right},#{@padding_top}", text_options.merge(gravity: 'North'))
|
69
|
+
draw_text(image, (pixels_for_distance/2).round(-2).to_s, "#{@padding_left - @padding_right},#{@padding_top}", text_options.merge(gravity: 'North'))
|
70
|
+
draw_text(image, (pixels_for_distance*0.75).round(-2).to_s + distance_units_to_s, "#{@padding_right},#{@padding_top}", text_options.merge(gravity: 'NorthEast'))
|
71
|
+
|
72
|
+
image
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def validate_data!
|
77
|
+
raise NoScalebarData.new('No scalebar data present') if @scalebar.nil? || @scalebar.empty?
|
78
|
+
raise InvalidScalebarSize.new('No scalebar width present') unless @scalebar[:size] && @scalebar[:size][:width]
|
79
|
+
raise InvalidScalebarSize.new('No scalebar height present') unless @scalebar[:size][:height]
|
80
|
+
raise InvalidScalebarZoom.new('Zoom must be between 0..18') unless (0..18).include?(@zoom)
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_distance_in_units
|
84
|
+
padding = @padding_left + @padding_right
|
85
|
+
meters = (@scalebar[:size][:width] - padding) * ZOOM_METERS_PER_PIXEL[@zoom]
|
86
|
+
case @scalebar[:units]
|
87
|
+
when 'km'
|
88
|
+
meters / 1000
|
89
|
+
when 'miles'
|
90
|
+
meters * 0.0006213712
|
91
|
+
when 'feet'
|
92
|
+
meters * 3.28084
|
93
|
+
else # asssume meters
|
94
|
+
meters
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def distance_units_to_s
|
99
|
+
case @scalebar[:units]
|
100
|
+
when 'km'
|
101
|
+
'km'
|
102
|
+
when 'miles'
|
103
|
+
'mi'
|
104
|
+
when 'feet'
|
105
|
+
'ft'
|
106
|
+
else
|
107
|
+
'm'
|
108
|
+
end
|
7
109
|
end
|
8
110
|
end
|
9
111
|
end
|
data/lib/map_print/tiles/tile.rb
CHANGED
@@ -54,14 +54,9 @@ module MapPrint
|
|
54
54
|
|
55
55
|
def get_pixel_difference(lat_lng)
|
56
56
|
tile_lat_lng = tile_number_to_lat_lng
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
puts tile_lat_lng
|
61
|
-
puts '================'
|
62
|
-
|
63
|
-
x_pixels = GeoDistance.distance(lat_lng.lat, lat_lng.lng, lat_lng.lat, tile_lat_lng[:lng]).meters.number / METERS_PER_PIXELS[@z]
|
64
|
-
y_pixels = GeoDistance.distance(lat_lng.lat, lat_lng.lng, tile_lat_lng[:lat], lat_lng.lng).meters.number / METERS_PER_PIXELS[@z]
|
57
|
+
|
58
|
+
x_pixels = lat_lng.distance_to(LatLng.new(lat_lng.lat, tile_lat_lng[:lng])) / METERS_PER_PIXELS[@z]
|
59
|
+
y_pixels = lat_lng.distance_to(LatLng.new(tile_lat_lng[:lat], lat_lng.lng)) / METERS_PER_PIXELS[@z]
|
65
60
|
|
66
61
|
{ x: x_pixels, y: y_pixels }
|
67
62
|
end
|
@@ -30,6 +30,14 @@ module MapPrint
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
def x_size
|
34
|
+
x_array.size
|
35
|
+
end
|
36
|
+
|
37
|
+
def y_size
|
38
|
+
y_array.size
|
39
|
+
end
|
40
|
+
|
33
41
|
def tiles
|
34
42
|
return @tiles if @tiles
|
35
43
|
|
@@ -43,14 +51,7 @@ module MapPrint
|
|
43
51
|
@tiles
|
44
52
|
end
|
45
53
|
|
46
|
-
|
47
|
-
x_array.size
|
48
|
-
end
|
49
|
-
|
50
|
-
def y_size
|
51
|
-
y_array.size
|
52
|
-
end
|
53
|
-
|
54
|
+
private
|
54
55
|
def ne_offset
|
55
56
|
@ne_lat_lng.get_slippy_map_tile_number(@zoom)[:offset]
|
56
57
|
end
|
@@ -75,16 +76,12 @@ module MapPrint
|
|
75
76
|
@y_array ||= y1 < y2 ? y1..y2 : (y2..y1).to_a
|
76
77
|
end
|
77
78
|
|
78
|
-
private
|
79
|
-
|
80
79
|
def tile_class
|
81
80
|
if @type == 'osm'
|
82
81
|
OSMTile
|
83
|
-
elsif @type =~
|
82
|
+
elsif @type =~ /bing/
|
84
83
|
BingTile
|
85
84
|
end
|
86
85
|
end
|
87
|
-
|
88
86
|
end
|
89
|
-
|
90
87
|
end
|
data/lib/map_print/version.rb
CHANGED
data/lib/map_print.rb
CHANGED
data/map_print.gemspec
CHANGED
@@ -30,10 +30,10 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency 'bundler', '~> 1.10'
|
31
31
|
spec.add_development_dependency 'rake', '~> 10.0'
|
32
32
|
spec.add_development_dependency 'minitest', '~> 5.8'
|
33
|
+
spec.add_development_dependency 'webmock', '1.22.6'
|
33
34
|
spec.add_dependency 'prawn', '~> 2.0'
|
34
35
|
spec.add_dependency 'prawn-fast-png', '~> 0.2.3'
|
35
36
|
spec.add_dependency 'mini_magick', '~> 4.3'
|
36
|
-
spec.add_dependency 'geo-distance', '~> 0.1'
|
37
37
|
spec.add_dependency 'parallel', '~> 1.6'
|
38
38
|
spec.add_dependency 'thor', '~> 0.19'
|
39
39
|
end
|
data/minimum_map.pdf
ADDED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: map_print
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Fast
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.22.6
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.22.6
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: prawn
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,20 +108,6 @@ dependencies:
|
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '4.3'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: geo-distance
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0.1'
|
104
|
-
type: :runtime
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0.1'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: parallel
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- ".gitignore"
|
151
151
|
- ".ruby-gemset"
|
152
152
|
- ".ruby-version"
|
153
|
+
- ".travis.yml"
|
153
154
|
- Gemfile
|
154
155
|
- Gemfile.lock
|
155
156
|
- LICENSE.txt
|
@@ -161,12 +162,12 @@ files:
|
|
161
162
|
- lib/base.rb
|
162
163
|
- lib/map_print.rb
|
163
164
|
- lib/map_print/core.rb
|
165
|
+
- lib/map_print/exceptions.rb
|
164
166
|
- lib/map_print/geo_json_handler.rb
|
165
167
|
- lib/map_print/lat_lng.rb
|
166
168
|
- lib/map_print/layer_handler.rb
|
167
169
|
- lib/map_print/legend_handler.rb
|
168
|
-
- lib/map_print/
|
169
|
-
- lib/map_print/osm/tile_factory.rb
|
170
|
+
- lib/map_print/logger.rb
|
170
171
|
- lib/map_print/pdf_handler.rb
|
171
172
|
- lib/map_print/pdf_handlers/images.rb
|
172
173
|
- lib/map_print/pdf_handlers/texts.rb
|
@@ -183,7 +184,7 @@ files:
|
|
183
184
|
- lib/map_print/tiles/tile_factory.rb
|
184
185
|
- lib/map_print/version.rb
|
185
186
|
- map_print.gemspec
|
186
|
-
-
|
187
|
+
- minimum_map.pdf
|
187
188
|
homepage: http://github.com/afast/map_print
|
188
189
|
licenses:
|
189
190
|
- MIT
|
data/lib/map_print/osm/tile.rb
DELETED
@@ -1,125 +0,0 @@
|
|
1
|
-
require 'open-uri'
|
2
|
-
require 'fileutils'
|
3
|
-
|
4
|
-
module MapPrint
|
5
|
-
module OSM
|
6
|
-
class Tile
|
7
|
-
|
8
|
-
BIT_TO_QUADKEY = { [false, false] => "0", [false, true] => "1", [true, false] => "2", [true, true] => "3"}
|
9
|
-
|
10
|
-
METERS_PER_PIXELS = {
|
11
|
-
0 => 26862156543.031,
|
12
|
-
1 => 8078271.521,
|
13
|
-
2 => 7739135.761,
|
14
|
-
3 => 6919567.881,
|
15
|
-
4 => 889783.941,
|
16
|
-
5 => 214891.9771,
|
17
|
-
6 => 222445.9801721,
|
18
|
-
7 => 161731222.991,
|
19
|
-
8 => 11611.5001,
|
20
|
-
9 => 52305.751,
|
21
|
-
10 => 152.871,
|
22
|
-
11 => 76.4371,
|
23
|
-
12 => 38.2191,
|
24
|
-
13 => 519.1091,
|
25
|
-
14 => 9.55461,
|
26
|
-
15 => 4.77731,
|
27
|
-
16 => 2.38871,
|
28
|
-
17 => 1.19431,
|
29
|
-
18 => 0.5972
|
30
|
-
}
|
31
|
-
|
32
|
-
class << self
|
33
|
-
|
34
|
-
def meters_per_pixel(zoom)
|
35
|
-
METERS_PER_PIXELS[zoom]
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize(x, y, z, base_url)
|
41
|
-
@base_url = base_url
|
42
|
-
@x = x
|
43
|
-
@y = y
|
44
|
-
@z = z
|
45
|
-
end
|
46
|
-
|
47
|
-
def coords
|
48
|
-
{x: @x, y: @y, z: @z}
|
49
|
-
end
|
50
|
-
|
51
|
-
def download
|
52
|
-
unless File.exists?(file_path)
|
53
|
-
content = open(get_url).read
|
54
|
-
write_file(content)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def get_pixel_difference(lat_lng)
|
59
|
-
tile_lat_lng = tile_number_to_lat_lng
|
60
|
-
|
61
|
-
x_pixels = GeoDistance.distance(lat_lng.lat, lat_lng.lng, lat_lng.lat, tile_lat_lng[:lng]).meters.number / METERS_PER_PIXELS[@z]
|
62
|
-
y_pixels = GeoDistance.distance(lat_lng.lat, lat_lng.lng, tile_lat_lng[:lat], lat_lng.lng).meters.number / METERS_PER_PIXELS[@z]
|
63
|
-
|
64
|
-
{ x: x_pixels, y: y_pixels }
|
65
|
-
end
|
66
|
-
|
67
|
-
def tile_number_to_lat_lng
|
68
|
-
n = 2.0 ** @z
|
69
|
-
lon_deg = @x / n * 360.0 - 180.0
|
70
|
-
lat_rad = Math::atan(Math::sinh(Math::PI * (1 - 2 * @y / n)))
|
71
|
-
lat_deg = 180.0 * (lat_rad / Math::PI)
|
72
|
-
|
73
|
-
{ lat: lat_deg, lng: lon_deg }
|
74
|
-
end
|
75
|
-
|
76
|
-
def file_path
|
77
|
-
File.join(folder_name, "#{@y}.png")
|
78
|
-
end
|
79
|
-
|
80
|
-
def tile2quad
|
81
|
-
quadkey_chars = []
|
82
|
-
|
83
|
-
tx = @x.to_i
|
84
|
-
ty = @y.to_i
|
85
|
-
|
86
|
-
@z.times do
|
87
|
-
quadkey_chars.push BIT_TO_QUADKEY[[ty.odd?, tx.odd?]] # bit order y,x
|
88
|
-
tx >>= 1 ; ty >>= 1
|
89
|
-
end
|
90
|
-
|
91
|
-
quadkey_chars.join.reverse
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def write_file(content)
|
97
|
-
FileUtils.mkdir_p(folder_name)
|
98
|
-
|
99
|
-
File.open file_path, 'wb' do |f|
|
100
|
-
f.write content
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def provider_name
|
105
|
-
if @base_url =~ /openstreetmap/
|
106
|
-
'osm'
|
107
|
-
elsif @base_url =~ /virtualearth/
|
108
|
-
'bing'
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def folder_name
|
113
|
-
"cache/#{provider_name}/#{@z}/#{@x}"
|
114
|
-
end
|
115
|
-
|
116
|
-
def get_url
|
117
|
-
if provider_name == 'osm'
|
118
|
-
@base_url.gsub('${x}', @x.to_s).gsub('${y}', @y.to_s).gsub('${z}', @z.to_s)
|
119
|
-
elsif provider_name == 'bing'
|
120
|
-
@base_url.gsub('${quadkey}', tile2quad)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module MapPrint
|
2
|
-
module OSM
|
3
|
-
class TileFactory
|
4
|
-
|
5
|
-
def initialize(base_url, sw_lat_lng, ne_lat_lng, zoom)
|
6
|
-
@base_url = base_url
|
7
|
-
@sw_lat_lng = sw_lat_lng
|
8
|
-
@ne_lat_lng = ne_lat_lng
|
9
|
-
@zoom = zoom
|
10
|
-
end
|
11
|
-
|
12
|
-
def px_offset
|
13
|
-
return @px_offset if @px_offset
|
14
|
-
offset = {}
|
15
|
-
|
16
|
-
offset[:top] = (ne_offset[:y] * 256).to_i
|
17
|
-
offset[:right] = 256 - (ne_offset[:x] * 256).to_i
|
18
|
-
offset[:bottom] = 256 - (sw_offset[:y] * 256).to_i
|
19
|
-
offset[:left] = (sw_offset[:x] * 256).to_i
|
20
|
-
@px_offset = offset
|
21
|
-
end
|
22
|
-
|
23
|
-
def download
|
24
|
-
Parallel.each(tiles, in_processes: 20) do |tile|
|
25
|
-
tile.download
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def tiles
|
30
|
-
return @tiles if @tiles
|
31
|
-
|
32
|
-
@tiles = []
|
33
|
-
y_array.each do |y|
|
34
|
-
x_array.each do |x|
|
35
|
-
@tiles << Tile.new(x, y, @zoom, @base_url)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
@tiles
|
40
|
-
end
|
41
|
-
|
42
|
-
def x_size
|
43
|
-
x_array.size
|
44
|
-
end
|
45
|
-
|
46
|
-
def y_size
|
47
|
-
y_array.size
|
48
|
-
end
|
49
|
-
|
50
|
-
def ne_offset
|
51
|
-
@ne_lat_lng.get_slippy_map_tile_number(@zoom)[:offset]
|
52
|
-
end
|
53
|
-
|
54
|
-
def sw_offset
|
55
|
-
@sw_lat_lng.get_slippy_map_tile_number(@zoom)[:offset]
|
56
|
-
end
|
57
|
-
|
58
|
-
def x_array
|
59
|
-
return @x_array if @x_array
|
60
|
-
|
61
|
-
x1 = @sw_lat_lng.get_slippy_map_tile_number(@zoom)[:x]
|
62
|
-
x2 = @ne_lat_lng.get_slippy_map_tile_number(@zoom)[:x]
|
63
|
-
@x_array ||= x1 < x2 ? x1..x2 : (x2..x1).to_a
|
64
|
-
end
|
65
|
-
|
66
|
-
def y_array
|
67
|
-
return @y_array if @y_array
|
68
|
-
|
69
|
-
y1 = @sw_lat_lng.get_slippy_map_tile_number(@zoom)[:y]
|
70
|
-
y2 = @ne_lat_lng.get_slippy_map_tile_number(@zoom)[:y]
|
71
|
-
@y_array ||= y1 < y2 ? y1..y2 : (y2..y1).to_a
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
data/marker.png
DELETED
Binary file
|