map_print 0.3.0 → 0.9.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 +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
|
[](https://badge.fury.io/rb/map_print)
|
4
|
+
[](https://travis-ci.org/afast/map_print)
|
5
|
+
[](https://codeclimate.com/github/afast/map_print)
|
6
|
+
[](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
|