atlas_middleware 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +3 -0
- data/README +3 -0
- data/lib/arc_server_soap.rb +47 -0
- data/lib/atlas_middleware.rb +5 -0
- data/lib/cgns_search.rb +30 -0
- data/lib/esri/soap/map_server/classes.rb +4328 -0
- data/lib/esri/soap/map_server/client.rb +326 -0
- data/lib/esri/soap/map_server/map_server.rb +39 -0
- data/lib/esri/soap/map_server/mapping_registry.rb +4300 -0
- data/lib/map_server_legend_info.rb +17 -0
- data/lib/pdf_template.rb +206 -0
- data/lib/postal_code_lookup.rb +54 -0
- data/lib/print_map/job.rb +49 -0
- data/lib/print_map/legend_images_collector.rb +34 -0
- data/lib/print_map/map_exporter.rb +20 -0
- data/lib/print_map/pdf_generator.rb +146 -0
- data/lib/print_map/worker.rb +71 -0
- data/lib/print_map_service.rb +48 -0
- data/lib/rest_proxy.rb +15 -0
- metadata +113 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'arcserver'
|
2
|
+
|
3
|
+
class MapServerLegendInfo
|
4
|
+
def call(env)
|
5
|
+
request = Rack::Request.new(env)
|
6
|
+
|
7
|
+
begin
|
8
|
+
map_server = ArcServer::MapServer.new(request['url'])
|
9
|
+
map_name = map_server.get_default_map_name
|
10
|
+
legend = map_server.get_default_map_name(:map_name => map_name)
|
11
|
+
rescue Exception => e
|
12
|
+
return [500, { "Content-Type" => "text/plain" }, e.message]
|
13
|
+
end
|
14
|
+
|
15
|
+
[200, { "Content-Type" => "application/json" }, legend.to_json]
|
16
|
+
end
|
17
|
+
end
|
data/lib/pdf_template.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'prawn'
|
7
|
+
require 'prawn/format'
|
8
|
+
require 'prawn/layout'
|
9
|
+
#require 'prawn/fast_png'
|
10
|
+
require 'open-uri'
|
11
|
+
require 'esri/soap/map_server/map_server'
|
12
|
+
require 'RMagick'
|
13
|
+
require 'perftools'
|
14
|
+
require 'base64'
|
15
|
+
|
16
|
+
module Prawn
|
17
|
+
module Images
|
18
|
+
class PNG
|
19
|
+
alias_method :prawn_fast_png_old_initialize, :initialize
|
20
|
+
|
21
|
+
def initialize(data) #:nodoc:
|
22
|
+
@prawn_fast_png_data = data
|
23
|
+
prawn_fast_png_old_initialize(data)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def unfilter_image_data
|
28
|
+
img = Magick::Image.from_blob(@prawn_fast_png_data).first
|
29
|
+
|
30
|
+
# get only one color value per pixel (Intensity) for grayscale+alpha images
|
31
|
+
format = color_type == 4 ? 'I' : 'RGB'
|
32
|
+
|
33
|
+
img_data = img.export_pixels_to_str(0, 0, width, height, format)
|
34
|
+
alpha_channel = img.export_pixels_to_str(0, 0, width, height, 'A')
|
35
|
+
|
36
|
+
img.destroy!
|
37
|
+
@prawn_fast_png_data = nil
|
38
|
+
|
39
|
+
@img_data = Zlib::Deflate.deflate(img_data)
|
40
|
+
@alpha_channel = Zlib::Deflate.deflate(alpha_channel)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def collect_legend_images
|
48
|
+
images = []
|
49
|
+
service = 'http://sampleserver1.arcgisonline.com/ArcGIS/services/Portland/ESRI_LandBase_WebMercator/MapServer'
|
50
|
+
legend_infos = ESRI::Soap::MapServer.get_legend_info(service, :image_return_type => :data)
|
51
|
+
|
52
|
+
legend_infos.each do |legend_info|
|
53
|
+
if legend_info.legendGroups[0].legendClasses.length == 1
|
54
|
+
img_data = Base64.decode64(Base64.decode64(legend_info.legendGroups[0].legendClasses[0].symbolImage.imageData))
|
55
|
+
images << {
|
56
|
+
:label => legend_info.name,
|
57
|
+
:image => Magick::Image.from_blob(img_data).first
|
58
|
+
}
|
59
|
+
else
|
60
|
+
legend_info.legendGroups[0].legendClasses.each do |legend_class|
|
61
|
+
img_data = Base64.decode64(Base64.decode64(legend_class.symbolImage.imageData))
|
62
|
+
images << {
|
63
|
+
:label => legend_class.label,
|
64
|
+
:image => Magick::Image.from_blob(img_data).first
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
images
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_legend(pdf, opts = {})
|
74
|
+
images = collect_legend_images
|
75
|
+
|
76
|
+
columns = opts[:columns] || 4
|
77
|
+
rows = opts[:rows] || 4
|
78
|
+
gutter = opts[:gutter] || 0
|
79
|
+
|
80
|
+
pdf.define_grid(:columns => columns, :rows => rows, :gutter => gutter)
|
81
|
+
|
82
|
+
pdf.grid.rows.times do |i|
|
83
|
+
pdf.grid.columns.times do |j|
|
84
|
+
cell = pdf.grid(i,j)
|
85
|
+
pdf.bounding_box cell.top_left, :width => cell.width, :height => cell.height do
|
86
|
+
return if images.empty?
|
87
|
+
image = images.shift
|
88
|
+
|
89
|
+
pdf.image StringIO.new(image[:image].to_blob), :at => [pdf.bounds.top_left], :fit => [16, 16]
|
90
|
+
pdf.text_box image[:label],
|
91
|
+
:width => cell.width,
|
92
|
+
:height => cell.height,
|
93
|
+
:overflow => :ellipses,
|
94
|
+
:at => [pdf.bounds.left + 20, pdf.bounds.top - 2]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def portrait_pdf
|
101
|
+
print 'creating portrait pdf...'
|
102
|
+
Prawn::Document.generate '/home/colin/portrait.pdf', :page_layout => :portrait do |pdf|
|
103
|
+
box = pdf.bounds
|
104
|
+
# html tag definitions
|
105
|
+
pdf.tags :h1 => { :font_size => '25', :font_style => :bold }
|
106
|
+
# html style definitions
|
107
|
+
pdf.styles :footer => { :color => "#999", :font_style => :italic }
|
108
|
+
|
109
|
+
# header
|
110
|
+
pdf.bounding_box [box.left, box.top], :width => box.right, :height => 30 do
|
111
|
+
pdf.text "<h1>Title</h1>"
|
112
|
+
end
|
113
|
+
|
114
|
+
# map
|
115
|
+
pdf.bounding_box [box.left, pdf.cursor], :width => box.right, :height => box.width do
|
116
|
+
pdf.stroke_bounds
|
117
|
+
pdf.image "/home/colin/export.png", :at => [box.left, pdf.cursor], :width => box.width, :height => box.width
|
118
|
+
end
|
119
|
+
|
120
|
+
pdf.move_down 10
|
121
|
+
# toc
|
122
|
+
pdf.bounding_box [box.left, pdf.cursor], :width => box.right, :height => 140 do
|
123
|
+
pdf.padded_box 5 do
|
124
|
+
create_legend(pdf, :columns => 4, :rows => 5)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# footer
|
129
|
+
pdf.canvas do
|
130
|
+
footer = {
|
131
|
+
:top_left => [box.absolute_left - 20, box.absolute_bottom - 15],
|
132
|
+
:width => box.right + 40,
|
133
|
+
:height => 15
|
134
|
+
}
|
135
|
+
|
136
|
+
pdf.bounding_box footer[:top_left], :width => footer[:width], :height => footer[:height] do
|
137
|
+
pdf.text '<span class="footer">NB Aquatic Bio-Web</span>'
|
138
|
+
end
|
139
|
+
|
140
|
+
pdf.bounding_box footer[:top_left], :width => footer[:width], :height => footer[:height] do
|
141
|
+
pdf.text "<span class=\"footer\">Generated on #{Time.now.strftime("%Y/%m/%d")}</span>", :align => :right
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
puts 'done'
|
146
|
+
end
|
147
|
+
|
148
|
+
def landscape_pdf
|
149
|
+
print 'creating landscape pdf...'
|
150
|
+
Prawn::Document.generate '/home/colin/landscape.pdf', :page_layout => :landscape do |pdf|
|
151
|
+
box = pdf.bounds
|
152
|
+
# html tag definitions
|
153
|
+
pdf.tags :h1 => { :font_size => '25', :font_style => :bold }
|
154
|
+
# html style definitions
|
155
|
+
pdf.styles :footer => { :color => "#999", :font_style => :italic }
|
156
|
+
|
157
|
+
# map
|
158
|
+
pdf.bounding_box [box.left, box.top], :width => box.height, :height => box.height do
|
159
|
+
pdf.stroke_bounds
|
160
|
+
pdf.image "/home/colin/export.png", :at => [box.left, box.top], :width => box.height, :height => box.height
|
161
|
+
end
|
162
|
+
|
163
|
+
pdf.bounding_box [box.height + 10, box.top], :width => box.right - box.height - 10, :height => box.height do
|
164
|
+
# header
|
165
|
+
pdf.text "<h1>Title</h1>"
|
166
|
+
pdf.move_down 10
|
167
|
+
|
168
|
+
# toc
|
169
|
+
pdf.bounding_box [pdf.bounds.left, pdf.cursor], :width => pdf.bounds.right, :height => pdf.cursor do
|
170
|
+
create_legend(pdf, :columns => 1, :rows => 20)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# footer
|
175
|
+
pdf.canvas do
|
176
|
+
footer = {
|
177
|
+
:top_left => [box.absolute_left - 20, box.absolute_bottom - 15],
|
178
|
+
:width => box.right + 40,
|
179
|
+
:height => 15
|
180
|
+
}
|
181
|
+
|
182
|
+
pdf.bounding_box footer[:top_left], :width => footer[:width], :height => footer[:height] do
|
183
|
+
pdf.text '<span class="footer">NB Aquatic Bio-Web</span>'
|
184
|
+
end
|
185
|
+
|
186
|
+
pdf.bounding_box footer[:top_left], :width => footer[:width], :height => footer[:height] do
|
187
|
+
pdf.text "<span class=\"footer\">Generated on #{Time.now.strftime("%Y/%m/%d")}</span>", :align => :right
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
puts 'done'
|
192
|
+
end
|
193
|
+
|
194
|
+
PerfTools::CpuProfiler.start("/home/colin/pdf_perf") do
|
195
|
+
start_time = Time.now
|
196
|
+
portrait_pdf
|
197
|
+
end_time = Time.now
|
198
|
+
puts end_time - start_time
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
#start_time = Time.now
|
203
|
+
#landscape_pdf
|
204
|
+
#end_time = Time.now
|
205
|
+
#puts end_time - start_time
|
206
|
+
#end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rack'
|
2
|
+
#require 'rack/cache'
|
3
|
+
require 'activesupport'
|
4
|
+
require 'httparty'
|
5
|
+
|
6
|
+
class PostalCodeLookup
|
7
|
+
class ServiceException < Exception; end
|
8
|
+
|
9
|
+
include HTTParty
|
10
|
+
base_uri "geoservices.cgdi.ca"
|
11
|
+
format :xml
|
12
|
+
default_params :version => '1.0.0',
|
13
|
+
:request => 'GetPostalCode',
|
14
|
+
:sortarea => 'FSA'
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
request = Rack::Request.new(env)
|
18
|
+
|
19
|
+
headers = { "Content-Type" => "text/plain" }
|
20
|
+
r = Rack::Response # shorthand
|
21
|
+
begin
|
22
|
+
response = r.new(200, headers, find(request.params['code']))
|
23
|
+
rescue Exception => e
|
24
|
+
response = r.new(500, headers, e.message)
|
25
|
+
end
|
26
|
+
# response.max_age = 1.month
|
27
|
+
|
28
|
+
response.to_a
|
29
|
+
end
|
30
|
+
|
31
|
+
def find(code)
|
32
|
+
result = PostalCodeLookup.get('/cgi-bin/postalcode/postalcode.cgi', {
|
33
|
+
:query => { :code => code }
|
34
|
+
})
|
35
|
+
|
36
|
+
if error = result["ServiceExceptionReport"]
|
37
|
+
raise ServiceException.new(error['ServiceException'])
|
38
|
+
else
|
39
|
+
postal_code = result['PostalCodeLookup']['PostalCodeResultSet']['PostalCode']
|
40
|
+
location = postal_code["gml:centerOf"]["gml:Point"]
|
41
|
+
epsg = location["srsName"].split('#').pop
|
42
|
+
lng, lat = location["gml:coordinates"].split(',')
|
43
|
+
return {
|
44
|
+
:postal_code => postal_code["gml:name"],
|
45
|
+
:epsg => epsg,
|
46
|
+
:srs_name => location["srsName"],
|
47
|
+
:latitude => lat,
|
48
|
+
:longitude => lng,
|
49
|
+
:placename => postal_code["Placename"],
|
50
|
+
:province_or_territory => postal_code["ProvinceOrTerritory"]
|
51
|
+
}.to_json
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'arcserver'
|
2
|
+
require 'validatable'
|
3
|
+
|
4
|
+
module PrintMap
|
5
|
+
class Job
|
6
|
+
include Validatable
|
7
|
+
include ArcServer::UrlHelper
|
8
|
+
# services validations
|
9
|
+
validates_length_of :services, :minimum => 1, :message => 'at least one service needs to be requested'
|
10
|
+
validates_true_for :services, :logic => lambda {
|
11
|
+
services.all? { |url| map_server?(url) }
|
12
|
+
}, :message => 'all services need to be valid MapServer urls'
|
13
|
+
# bbox validations
|
14
|
+
validates_length_of :bbox, :is => 4, :message => "bad format for 'bbox' - try bbox=xmin,ymin,xmax,ymax"
|
15
|
+
validates_true_for :bbox, :logic => lambda {
|
16
|
+
env['bbox'].to_s.split(',').all? { |b| b.to_s.match(/^[-|+]?\d*\.?\d*$/) }
|
17
|
+
}, :message => 'all bbox values must be valid positive/negative numbers'
|
18
|
+
|
19
|
+
attr_reader :env
|
20
|
+
|
21
|
+
def initialize(env = {})
|
22
|
+
@env = env
|
23
|
+
end
|
24
|
+
|
25
|
+
def services
|
26
|
+
@services ||= env['services'].to_s.split(',')
|
27
|
+
end
|
28
|
+
|
29
|
+
def bbox
|
30
|
+
@bbox ||= env['bbox'].to_s.split(',').collect{ |b| b.to_f }
|
31
|
+
end
|
32
|
+
|
33
|
+
def page_layout
|
34
|
+
@page_layout ||= (env['page_layout'] || 'portrait').to_sym
|
35
|
+
end
|
36
|
+
|
37
|
+
def page_size
|
38
|
+
@page_size ||= env['page_size'] || 'A4'
|
39
|
+
end
|
40
|
+
|
41
|
+
def dpi
|
42
|
+
@dpi ||= env['dpi'] || 96
|
43
|
+
end
|
44
|
+
|
45
|
+
def title
|
46
|
+
@title = env['title'] || 'Untitled Map'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'arcserver/map_server'
|
2
|
+
|
3
|
+
module PrintMap
|
4
|
+
class LegendImagesCollector
|
5
|
+
def collect(opts = {})
|
6
|
+
puts 'creating map server object'
|
7
|
+
map_server = ArcServer::MapServer.new(opts[:url])
|
8
|
+
|
9
|
+
images = []
|
10
|
+
puts 'getting legend'
|
11
|
+
map_server.get_legend_info.each do |legend_info|
|
12
|
+
puts 'processing result'
|
13
|
+
legend_classes = legend_info[:legend_groups][0][:legend_classes]
|
14
|
+
if legend_classes.length == 1
|
15
|
+
img_data = Base64.decode64(legend_classes[0][:symbol_image][:image_data])
|
16
|
+
images << {
|
17
|
+
:label => legend_info[:name],
|
18
|
+
:data => img_data
|
19
|
+
}
|
20
|
+
else
|
21
|
+
legend_classes.each do |legend_class|
|
22
|
+
img_data = Base64.decode64(legend_class[:symbol_image][:image_data])
|
23
|
+
images << {
|
24
|
+
:label => legend_class[:label],
|
25
|
+
:data => img_data
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
images
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'RMagick'
|
3
|
+
|
4
|
+
module PrintMap
|
5
|
+
class MapExporter
|
6
|
+
def export(opts = {})
|
7
|
+
service = opts[:service]
|
8
|
+
query = {
|
9
|
+
:bbox => opts[:bbox],
|
10
|
+
:f => :image,
|
11
|
+
:format => :png24,
|
12
|
+
:transparent => true,
|
13
|
+
:size => opts[:size],
|
14
|
+
:dpi => opts[:dpi]
|
15
|
+
}
|
16
|
+
response = HTTParty.get("#{service}/export", :query => query)
|
17
|
+
Magick::Image.from_blob(response.body).first
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'prawn'
|
2
|
+
require 'prawn/format'
|
3
|
+
require 'prawn/layout'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
module PrintMap
|
7
|
+
class PdfGenerator
|
8
|
+
def generate(opts = {})
|
9
|
+
title = opts[:title]
|
10
|
+
map_image = opts[:map_image]
|
11
|
+
layout = opts[:page_layout]
|
12
|
+
legend_images = opts[:legend_images]
|
13
|
+
|
14
|
+
pdf = Prawn::Document.new(:page_layout => layout) do |pdf|
|
15
|
+
# html tag definitions
|
16
|
+
pdf.tags :h1 => { :font_size => '25', :font_style => :bold }
|
17
|
+
# html style definitions
|
18
|
+
pdf.styles :footer => { :color => "#999", :font_style => :italic }
|
19
|
+
# generate the pdf
|
20
|
+
send("generate_#{layout}_pdf", pdf, title, map_image, legend_images)
|
21
|
+
write_footer(pdf)
|
22
|
+
end
|
23
|
+
|
24
|
+
pdf
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_portrait_pdf(pdf, title, map_image, legend_images)
|
28
|
+
box = pdf.bounds
|
29
|
+
# header
|
30
|
+
pdf.bounding_box [box.left, box.top], :width => box.right, :height => 30 do
|
31
|
+
pdf.text "<h1>#{title}</h1>"
|
32
|
+
end
|
33
|
+
|
34
|
+
# map
|
35
|
+
pdf.bounding_box [box.left, pdf.cursor], :width => box.right, :height => box.width do
|
36
|
+
pdf.stroke_bounds
|
37
|
+
pdf.image StringIO.new(map_image.to_blob), :at => [box.left, pdf.cursor], :width => box.width, :height => box.width
|
38
|
+
end
|
39
|
+
|
40
|
+
pdf.move_down 10
|
41
|
+
# toc
|
42
|
+
pdf.bounding_box [box.left, pdf.cursor], :width => box.right, :height => 140 do
|
43
|
+
pdf.padded_box 5 do
|
44
|
+
create_legend(pdf, legend_images, :columns => 4, :rows => 5)
|
45
|
+
pdf.stroke_bounds
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_landscape_pdf(pdf, title, image, legend_images)
|
51
|
+
box = pdf.bounds
|
52
|
+
# map
|
53
|
+
pdf.bounding_box [box.left, box.top], :width => box.height, :height => box.height do
|
54
|
+
pdf.stroke_bounds
|
55
|
+
pdf.image StringIO.new(image.to_blob), :at => [box.left, box.top], :width => box.height, :height => box.height
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
pdf.bounding_box [box.height + 10, box.top], :width => box.right - box.height - 10, :height => box.height do
|
60
|
+
# header
|
61
|
+
pdf.text "<h1>#{title}</h1>"
|
62
|
+
pdf.move_down 10
|
63
|
+
|
64
|
+
# toc
|
65
|
+
pdf.bounding_box [pdf.bounds.left, pdf.cursor], :width => pdf.bounds.right, :height => pdf.cursor do
|
66
|
+
create_legend(pdf, legend_images, :columns => 1, :rows => 20)
|
67
|
+
pdf.stroke_bounds
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_footer(pdf)
|
73
|
+
box = pdf.bounds
|
74
|
+
pdf.canvas do
|
75
|
+
footer = {
|
76
|
+
:top_left => [box.absolute_left - 20, box.absolute_bottom - 15],
|
77
|
+
:width => box.right + 40,
|
78
|
+
:height => 15
|
79
|
+
}
|
80
|
+
|
81
|
+
pdf.bounding_box footer[:top_left], :width => footer[:width], :height => footer[:height] do
|
82
|
+
pdf.text '<span class="footer">NB Aquatic Bio-Web</span>'
|
83
|
+
end
|
84
|
+
|
85
|
+
pdf.bounding_box footer[:top_left], :width => footer[:width], :height => footer[:height] do
|
86
|
+
pdf.text "<span class=\"footer\">Generated on #{Time.now.strftime("%Y/%m/%d")}</span>", :align => :right
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_legend(pdf, legend_images, opts = {})
|
92
|
+
columns = opts[:columns] || 4
|
93
|
+
rows = opts[:rows] || 4
|
94
|
+
gutter = opts[:gutter] || 0
|
95
|
+
|
96
|
+
pdf.define_grid(:columns => columns, :rows => rows, :gutter => gutter)
|
97
|
+
|
98
|
+
pdf.grid.rows.times do |i|
|
99
|
+
pdf.grid.columns.times do |j|
|
100
|
+
cell = pdf.grid(i,j)
|
101
|
+
pdf.bounding_box cell.top_left, :width => cell.width, :height => cell.height do
|
102
|
+
return if legend_images.empty?
|
103
|
+
image = legend_images.shift
|
104
|
+
|
105
|
+
pdf.image StringIO.new(image[:data]), :at => [pdf.bounds.top_left], :fit => [16, 16]
|
106
|
+
pdf.text_box image[:label],
|
107
|
+
:width => cell.width,
|
108
|
+
:height => cell.height,
|
109
|
+
:overflow => :ellipses,
|
110
|
+
:at => [pdf.bounds.left + 20, pdf.bounds.top - 2]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module Prawn
|
119
|
+
module Images
|
120
|
+
class PNG
|
121
|
+
alias_method :prawn_fast_png_old_initialize, :initialize
|
122
|
+
|
123
|
+
def initialize(data) #:nodoc:
|
124
|
+
@prawn_fast_png_data = data
|
125
|
+
prawn_fast_png_old_initialize(data)
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
def unfilter_image_data
|
130
|
+
img = Magick::Image.from_blob(@prawn_fast_png_data).first
|
131
|
+
|
132
|
+
# get only one color value per pixel (Intensity) for grayscale+alpha images
|
133
|
+
format = color_type == 4 ? 'I' : 'RGB'
|
134
|
+
|
135
|
+
img_data = img.export_pixels_to_str(0, 0, width, height, format)
|
136
|
+
alpha_channel = img.export_pixels_to_str(0, 0, width, height, 'A')
|
137
|
+
|
138
|
+
img.destroy!
|
139
|
+
@prawn_fast_png_data = nil
|
140
|
+
|
141
|
+
@img_data = Zlib::Deflate.deflate(img_data)
|
142
|
+
@alpha_channel = Zlib::Deflate.deflate(alpha_channel)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'prawn'
|
2
|
+
require 'RMagick'
|
3
|
+
require 'print_map/map_exporter'
|
4
|
+
require 'print_map/pdf_generator'
|
5
|
+
require 'print_map/legend_images_collector'
|
6
|
+
|
7
|
+
module PrintMap
|
8
|
+
class Worker
|
9
|
+
attr_reader :current_job
|
10
|
+
attr_reader :map_exporter, :pdf_generator, :legend_images_collector
|
11
|
+
|
12
|
+
def initialize(config = {})
|
13
|
+
@map_exporter = config[:map_exporter] || MapExporter.new
|
14
|
+
@pdf_generator = config[:pdf_generator] || PdfGenerator.new
|
15
|
+
@legend_images_collector = config[:legend_images_collector] || LegendImagesCollector.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def do_job(job)
|
19
|
+
@current_job = job
|
20
|
+
puts 'create composite map image'
|
21
|
+
map_image = create_composite_map_image
|
22
|
+
puts 'collect legend images'
|
23
|
+
legend_images = collect_legend_images
|
24
|
+
puts 'generate pdf'
|
25
|
+
pdf = generate_pdf(map_image, legend_images)
|
26
|
+
puts 'cleanup'
|
27
|
+
map_image.destroy!
|
28
|
+
pdf
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_composite_map_image
|
32
|
+
width, height = 540, 510
|
33
|
+
base_image = Magick::Image.new(width, height)
|
34
|
+
base_image.background_color = 'transparent'
|
35
|
+
base_image.format = 'PNG'
|
36
|
+
|
37
|
+
current_job.services.each do |map_service|
|
38
|
+
export_options = {
|
39
|
+
:service => map_service,
|
40
|
+
:dpi => current_job.dpi,
|
41
|
+
:bbox => current_job.bbox.join(','),
|
42
|
+
:size => [width, height].join(',')
|
43
|
+
}
|
44
|
+
map_service_image = map_exporter.export(export_options)
|
45
|
+
base_image.composite!(map_service_image, Magick::CenterGravity, Magick::OverCompositeOp)
|
46
|
+
map_service_image.destroy!
|
47
|
+
end
|
48
|
+
|
49
|
+
base_image
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_pdf(map_image, legend_images)
|
53
|
+
opts = {
|
54
|
+
:title => current_job.title,
|
55
|
+
:map_image => map_image,
|
56
|
+
:page_layout => current_job.page_layout,
|
57
|
+
:legend_images => legend_images
|
58
|
+
}
|
59
|
+
pdf_generator.generate(opts)
|
60
|
+
end
|
61
|
+
|
62
|
+
def collect_legend_images
|
63
|
+
images = []
|
64
|
+
current_job.services.each do |url|
|
65
|
+
puts 'fetching legends at ' + url
|
66
|
+
images << legend_images_collector.collect(:url => url)
|
67
|
+
end
|
68
|
+
images.flatten
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'print_map/job'
|
2
|
+
require 'print_map/worker'
|
3
|
+
|
4
|
+
class PrintMapService
|
5
|
+
attr_reader :worker, :create_job
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
@worker = opts[:worker] ||= PrintMap::Worker.new
|
9
|
+
@create_job = opts[:create_job] ||= proc { |opts| PrintMap::Job.new(opts) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
puts 'calling print map'
|
14
|
+
job = create_job.call(Rack::Request.new(env).params)
|
15
|
+
if job.valid?
|
16
|
+
puts 'valid job'
|
17
|
+
pdf = worker.do_job(job)
|
18
|
+
puts 'rendering to client'
|
19
|
+
# now stream the pdf to the client
|
20
|
+
[
|
21
|
+
200, {
|
22
|
+
"Content-Type" => "application/pdf",
|
23
|
+
"Content-Disposition" => "attachment; filename=\"#{job.title}.pdf\""
|
24
|
+
},
|
25
|
+
pdf.render
|
26
|
+
]
|
27
|
+
else
|
28
|
+
create_invalid_job_response(job)
|
29
|
+
end
|
30
|
+
|
31
|
+
rescue Exception => e
|
32
|
+
create_error_response(e)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_invalid_job_response(job)
|
36
|
+
body = "Print map failed because of the following errors:\n#{job.errors.full_messages.join("\n")}"
|
37
|
+
puts 'failed to generate print map:'
|
38
|
+
puts body
|
39
|
+
[500, { "Content-Type" => "text/plain" }, body]
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_error_response(e)
|
43
|
+
body = "An internal error occurred:\n#{e.message}"
|
44
|
+
puts 'failed to generate print map:'
|
45
|
+
puts body
|
46
|
+
[500, { "Content-Type" => "text/plain" }, body]
|
47
|
+
end
|
48
|
+
end
|
data/lib/rest_proxy.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
class RestProxy
|
5
|
+
def call(env)
|
6
|
+
request = Rack::Request.new(env)
|
7
|
+
url = request.params.delete('url')
|
8
|
+
call_proxy(url)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call_proxy(url)
|
12
|
+
response = HTTParty.get(url, :query => { :f => :json })
|
13
|
+
[response.code, response.headers, response.body]
|
14
|
+
end
|
15
|
+
end
|