cartocss_helper 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cartocss_helper/configuration.rb +103 -0
- data/lib/cartocss_helper/data_file_handling.rb +97 -0
- data/lib/cartocss_helper/downloader.rb +198 -0
- data/lib/cartocss_helper/git.rb +32 -0
- data/lib/cartocss_helper/heuristic.rb +117 -0
- data/lib/cartocss_helper/image_generator.rb +121 -0
- data/lib/cartocss_helper/style_specific/default_osm_style.rb +553 -0
- data/lib/cartocss_helper/style_specific/style_specific.rb +11 -0
- data/lib/cartocss_helper/tag_lister.rb +223 -0
- data/lib/cartocss_helper/validator.rb +172 -0
- data/lib/cartocss_helper/visualise_changes_diff_from_images.rb +158 -0
- data/lib/cartocss_helper/visualise_changes_image_generation.rb +148 -0
- data/lib/cartocss_helper.rb +70 -0
- data/license.txt +9 -0
- data/readme.md +25 -0
- metadata +111 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
module CartoCSSHelper
|
2
|
+
class StyleSpecificData
|
3
|
+
attr_reader :min_z, :max_z, :list_of_documented_tags, :list_of_documented_compositions
|
4
|
+
def initialize(min_z, max_z, list_of_documented_tags, list_of_documented_compositions)
|
5
|
+
@min_z = min_z
|
6
|
+
@max_z = max_z
|
7
|
+
@list_of_documented_tags = list_of_documented_tags
|
8
|
+
@list_of_documented_compositions = list_of_documented_compositions
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require_relative 'configuration.rb'
|
2
|
+
require_relative 'image_generator.rb'
|
3
|
+
include CartoCSSHelper::Configuration
|
4
|
+
include CartoCSSHelper::Heuristic
|
5
|
+
|
6
|
+
module CartoCSSHelper
|
7
|
+
class TagRenderingStatus
|
8
|
+
attr_accessor :key, :value, :state, :composite
|
9
|
+
def initialize(key, value, state, composite=nil)
|
10
|
+
@key = key
|
11
|
+
@value = value
|
12
|
+
@state = state
|
13
|
+
@composite = composite
|
14
|
+
end
|
15
|
+
def print
|
16
|
+
composite_string = ''
|
17
|
+
if composite
|
18
|
+
composite_string = "- rendered with #{@composite.to_s.gsub('=>', '=')}, maybe also other sets of tags"
|
19
|
+
end
|
20
|
+
puts "#{@key}=#{@value} #{@state} #{composite_string}"
|
21
|
+
end
|
22
|
+
def code_print
|
23
|
+
string = "Status.new('#{@key}', '#{@value}', :#{@state}"
|
24
|
+
if @composite != nil
|
25
|
+
string << ", #{@composite.to_s}"
|
26
|
+
end
|
27
|
+
string << '),'
|
28
|
+
puts string
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Info
|
33
|
+
def get_expected_composite(key, value)
|
34
|
+
CartoCSSHelper::Configuration.get_style_specific_data.list_of_documented_tags.each { |documented|
|
35
|
+
if documented.key == key
|
36
|
+
if documented.value == value
|
37
|
+
return documented.composite
|
38
|
+
end
|
39
|
+
end
|
40
|
+
}
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_render_state_of_tags(git_branch)
|
45
|
+
Git.checkout git_branch
|
46
|
+
get_render_state_of_tags.each { |state|
|
47
|
+
state.code_print
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_render_state_of_tags
|
52
|
+
states = []
|
53
|
+
@last_composite = nil
|
54
|
+
Heuristic.get_tags.each { |tag|
|
55
|
+
key = tag[0]
|
56
|
+
value = tag[1]
|
57
|
+
#print_render_state_of_tag key, value
|
58
|
+
state = get_render_state_of_tag(key, value)
|
59
|
+
states.push(TagRenderingStatus.new(key, value, state, @last_composite))
|
60
|
+
}
|
61
|
+
return states
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_render_state_of_tag(key, value)
|
65
|
+
if is_rendered key, value
|
66
|
+
@last_composite = nil
|
67
|
+
return :primary
|
68
|
+
else
|
69
|
+
if is_rendered_as_composite key, value, get_expected_composite(key, value)
|
70
|
+
@last_composite = how_rendered_as_composite key, value, get_expected_composite(key, value)
|
71
|
+
return :composite
|
72
|
+
else
|
73
|
+
@last_composite = nil
|
74
|
+
return :ignored
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_render_state_of_tag(key, value)
|
80
|
+
if is_rendered key, value
|
81
|
+
@last_composite = nil
|
82
|
+
if value == get_generic_tag_value
|
83
|
+
puts "#{key}=#{value} - primary generic tag value"
|
84
|
+
elsif is_key_rendered_and_value_ignored(key, value)
|
85
|
+
puts "#{key}=#{value} - primary, but rendering the same as generic value"
|
86
|
+
elsif
|
87
|
+
puts "#{key}=#{value} - primary"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
if is_rendered_as_composite key, value, @last_composite
|
91
|
+
@last_composite = how_rendered_as_composite key, value, @last_composite
|
92
|
+
puts "#{key}=#{value} - composite with #{@last_composite} - and maybe other tags"
|
93
|
+
else
|
94
|
+
@last_composite = nil
|
95
|
+
puts "#{key}=#{value} - not displayed"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def is_key_rendered_and_value_ignored(key, value)
|
101
|
+
if not is_rendered key,get_generic_tag_value
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
[false, true].each { |on_water|
|
105
|
+
[Configuration.get_max_z].each { |zlevel|
|
106
|
+
['area', 'closed_way', 'way', 'node'].each{ |type|
|
107
|
+
if rendered_on_zlevel({key => value}, type, zlevel, on_water)
|
108
|
+
if !is_key_rendered_and_value_ignored_set(key, value, type, zlevel, on_water)
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
|
118
|
+
def is_key_rendered_and_value_ignored_set(key, value, type, zlevel, on_water)
|
119
|
+
with_tested_value = Scene.new({key => value}, zlevel, on_water, type)
|
120
|
+
with_generic_value = Scene.new({key => get_generic_tag_value}, zlevel, on_water, type)
|
121
|
+
return !with_tested_value.is_output_different(with_generic_value)
|
122
|
+
end
|
123
|
+
|
124
|
+
def is_rendered(key, value)
|
125
|
+
[false, true].each { |on_water|
|
126
|
+
[Configuration.get_max_z].each { |zlevel|
|
127
|
+
['area', 'closed_way', 'way', 'node'].each{ |type|
|
128
|
+
if rendered_on_zlevel({key => value}, type, zlevel, on_water)
|
129
|
+
return true
|
130
|
+
end
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
|
137
|
+
def is_rendered_as_composite(key, value, suggested_composite=nil)
|
138
|
+
reason = how_rendered_as_composite key, value, suggested_composite
|
139
|
+
if reason == nil
|
140
|
+
return false
|
141
|
+
end
|
142
|
+
return true
|
143
|
+
end
|
144
|
+
|
145
|
+
protected
|
146
|
+
|
147
|
+
def how_rendered_as_composite(key, value, suggested_composite)
|
148
|
+
[false, true].each { |on_water|
|
149
|
+
[Configuration.get_max_z].each { |zlevel|
|
150
|
+
result = how_rendered_on_zlevel_as_composite({key => value}, 'closed_way', zlevel, on_water, suggested_composite)
|
151
|
+
if result != nil
|
152
|
+
return result
|
153
|
+
end
|
154
|
+
result = how_rendered_on_zlevel_as_composite({key => value}, 'way', zlevel, on_water, suggested_composite)
|
155
|
+
if result != nil
|
156
|
+
return result
|
157
|
+
end
|
158
|
+
result = how_rendered_on_zlevel_as_composite({key => value}, 'node', zlevel, on_water, suggested_composite)
|
159
|
+
if result != nil
|
160
|
+
return result
|
161
|
+
end
|
162
|
+
}
|
163
|
+
}
|
164
|
+
if suggested_composite != nil
|
165
|
+
return how_rendered_as_composite key, value, nil
|
166
|
+
end
|
167
|
+
return nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def rendered_on_zlevel(tags, type, zlevel, on_water)
|
171
|
+
empty = Scene.new({}, zlevel, on_water, type)
|
172
|
+
tested = Scene.new(tags, zlevel, on_water, type)
|
173
|
+
return tested.is_output_different(empty)
|
174
|
+
end
|
175
|
+
|
176
|
+
def how_rendered_on_zlevel_as_composite(tags, type, zlevel, on_water, suggested_composite)
|
177
|
+
if suggested_composite != nil
|
178
|
+
if is_rendered_with_this_composite tags, type, suggested_composite, zlevel, on_water
|
179
|
+
return suggested_composite
|
180
|
+
end
|
181
|
+
return nil
|
182
|
+
end
|
183
|
+
CartoCSSHelper::Configuration.get_style_specific_data.list_of_documented_compositions.each { |composite|
|
184
|
+
if is_rendered_with_this_composite tags, type, composite, zlevel, on_water
|
185
|
+
return composite
|
186
|
+
end
|
187
|
+
}
|
188
|
+
return nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def is_rendered_with_this_composite(tags, type, provided_composite, zlevel, on_water)
|
192
|
+
#puts "<<<\n#{tags}\n#{composite}<<<\n\n"
|
193
|
+
# noinspection RubyResolve
|
194
|
+
# see https://youtrack.jetbrains.com/issue/RUBY-16061
|
195
|
+
tags_with_composite = Marshal.load(Marshal.dump(tags))
|
196
|
+
# noinspection RubyResolve
|
197
|
+
# see https://youtrack.jetbrains.com/issue/RUBY-16061
|
198
|
+
composite = Marshal.load(Marshal.dump(provided_composite))
|
199
|
+
composite.each { |key, value|
|
200
|
+
if tags_with_composite[key] != nil
|
201
|
+
return false #shadowing
|
202
|
+
end
|
203
|
+
tags_with_composite[key] = value
|
204
|
+
}
|
205
|
+
with_composite = Scene.new(tags_with_composite, zlevel, on_water, type)
|
206
|
+
only_composite = Scene.new(composite, zlevel, on_water, type)
|
207
|
+
empty = Scene.new({}, zlevel, on_water, type)
|
208
|
+
if with_composite.is_output_different(empty)
|
209
|
+
if with_composite.is_output_different(only_composite)
|
210
|
+
if composite['area'] != nil
|
211
|
+
return true
|
212
|
+
end
|
213
|
+
composite['area'] = 'yes'
|
214
|
+
composite_interpreted_as_area = Scene.new(composite, zlevel, on_water, type)
|
215
|
+
if with_composite.is_output_different(composite_interpreted_as_area)
|
216
|
+
return true
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
return false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative 'heuristic.rb'
|
3
|
+
require_relative 'image_generator.rb'
|
4
|
+
require_relative 'configuration.rb'
|
5
|
+
require_relative 'tag_lister.rb'
|
6
|
+
|
7
|
+
include CartoCSSHelper::Heuristic
|
8
|
+
include CartoCSSHelper::Configuration
|
9
|
+
|
10
|
+
module CartoCSSHelper
|
11
|
+
module Validator
|
12
|
+
def run_tests(git_branch)
|
13
|
+
run_expected_rendering_test(git_branch)
|
14
|
+
run_dy_test(git_branch)
|
15
|
+
run_closed_way_test(git_branch)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_expected_rendering_test(git_branch)
|
19
|
+
Git.checkout git_branch
|
20
|
+
puts 'unexpectedly rendered/unrendered keys:'
|
21
|
+
compare_expected_with_real_rendering
|
22
|
+
puts
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_dy_test(git_branch)
|
26
|
+
Git.checkout git_branch
|
27
|
+
puts "bad dy values (tall names like 'É' are not displayed, short names like 'a' are):"
|
28
|
+
run_global_check(:check_dy)
|
29
|
+
puts
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_closed_way_test(git_branch)
|
33
|
+
Git.checkout git_branch
|
34
|
+
puts 'failed display of closed way, unlosed way works:'
|
35
|
+
run_global_check(:check_problems_with_closed_line)
|
36
|
+
puts
|
37
|
+
end
|
38
|
+
|
39
|
+
def compare_expected_with_real_rendering
|
40
|
+
list_of_documented = CartoCSSHelper::Configuration.get_style_specific_data.list_of_documented_tags
|
41
|
+
info = Info.new
|
42
|
+
list_of_rendered = info.get_render_state_of_tags
|
43
|
+
|
44
|
+
ensure_that_tags_documented_and_rendered_have_the_same_state(list_of_documented, list_of_rendered)
|
45
|
+
ensure_that_all_rendered_tags_are_documented(list_of_documented, list_of_rendered)
|
46
|
+
ensure_that_all_documented_are_really_rendered(list_of_documented, list_of_rendered)
|
47
|
+
end
|
48
|
+
|
49
|
+
def ensure_that_tags_documented_and_rendered_have_the_same_state(list_of_documented, list_of_rendered)
|
50
|
+
list_of_rendered.each { |tag_info|
|
51
|
+
compare_expected_with_tag_data list_of_documented, tag_info
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def ensure_that_all_rendered_tags_are_documented(list_of_documented, list_of_rendered)
|
56
|
+
list_of_rendered.each { |tag_info|
|
57
|
+
if tag_info.state != :ignored
|
58
|
+
if !is_tag_documented(list_of_documented, tag_info.key, tag_info.value)
|
59
|
+
puts 'documented'
|
60
|
+
puts "\tmissing"
|
61
|
+
puts 'real'
|
62
|
+
print "\t"
|
63
|
+
tag_info.print
|
64
|
+
puts
|
65
|
+
end
|
66
|
+
end
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def ensure_that_all_documented_are_really_rendered(list_of_documented, list_of_rendered)
|
71
|
+
list_of_documented.each { |documented|
|
72
|
+
if !is_tag_documented(list_of_rendered, documented.key, documented.value)
|
73
|
+
puts 'documented'
|
74
|
+
print "\t"
|
75
|
+
documented.print
|
76
|
+
puts 'real'
|
77
|
+
puts "\tmissing"
|
78
|
+
puts
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def is_tag_documented(list, key, value)
|
84
|
+
list.each { |tag_info|
|
85
|
+
if key == tag_info.key
|
86
|
+
if value == tag_info.value
|
87
|
+
return true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
}
|
91
|
+
return false
|
92
|
+
end
|
93
|
+
|
94
|
+
def compare_expected_with_tag_data(list_of_expected, tag_info)
|
95
|
+
list_of_expected.each { |expected|
|
96
|
+
if expected.key == tag_info.key
|
97
|
+
if expected.value == tag_info.value
|
98
|
+
if expected.equal? tag_info
|
99
|
+
puts 'expected'
|
100
|
+
print "\t"
|
101
|
+
expected.print
|
102
|
+
puts 'real'
|
103
|
+
print "\t"
|
104
|
+
tag_info.print
|
105
|
+
puts
|
106
|
+
end
|
107
|
+
return
|
108
|
+
end
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def run_global_check(function)
|
114
|
+
Heuristic.get_tags.each { |element|
|
115
|
+
(Configuration.get_max_z..Configuration.get_max_z).each { |zlevel| #get_min_z should be used - but as renderer is extremely slow this hack was used TODO
|
116
|
+
send(function, element[0], element[1], zlevel)
|
117
|
+
}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
#TODO - what about composite tags?
|
122
|
+
def check_problems_with_closed_line(key, value, zlevel, on_water = false)
|
123
|
+
way = Scene.new({key => value}, zlevel, on_water, 'way')
|
124
|
+
closed_way = Scene.new({key => value}, zlevel, on_water, 'closed_way')
|
125
|
+
empty = Scene.new({}, zlevel, on_water, 'node')
|
126
|
+
if way.is_output_different(empty)
|
127
|
+
if !closed_way.is_output_different(empty)
|
128
|
+
puts key+'='+value
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def check_dy(key, value, zlevel, interactive=false, on_water=false)
|
134
|
+
if !is_object_displaying_anything key, value, zlevel, on_water
|
135
|
+
#puts key+"="+value+" - not displayed as node on z#{zlevel}"
|
136
|
+
return
|
137
|
+
end
|
138
|
+
if !is_object_displaying_name key, value, 'a', zlevel, on_water
|
139
|
+
#puts key+"="+value+" - label is missing on z#{zlevel} nodes"
|
140
|
+
return
|
141
|
+
end
|
142
|
+
test_name = 'ÉÉÉÉÉÉ ÉÉÉÉÉÉ ÉÉÉÉÉÉ'
|
143
|
+
while !is_object_displaying_name key, value, test_name, zlevel, on_water
|
144
|
+
puts key+'='+value+" - name is missing for name '#{test_name}' on z#{zlevel}"
|
145
|
+
if interactive
|
146
|
+
puts 'press enter'
|
147
|
+
gets
|
148
|
+
else
|
149
|
+
return
|
150
|
+
end
|
151
|
+
with_name = Scene.new({key => value, 'name' => test_name}, zlevel, on_water, 'node')
|
152
|
+
with_name.flush_cache
|
153
|
+
puts 'calculating'
|
154
|
+
end
|
155
|
+
#puts key+"="+value+" - name is OK for name '#{name}'"
|
156
|
+
end
|
157
|
+
|
158
|
+
def is_object_displaying_name(key, value, name, zlevel, on_water)
|
159
|
+
name_tags = {key => value, 'name' => name}
|
160
|
+
with_name = Scene.new(name_tags, zlevel, on_water, 'node')
|
161
|
+
nameless_tags = {key => value}
|
162
|
+
nameless = Scene.new(nameless_tags, zlevel, on_water, 'node')
|
163
|
+
return with_name.is_output_different(nameless)
|
164
|
+
end
|
165
|
+
|
166
|
+
def is_object_displaying_anything(key, value, zlevel, on_water)
|
167
|
+
object = Scene.new({key => value}, zlevel, on_water, 'node')
|
168
|
+
empty = Scene.new({}, zlevel, on_water, 'node')
|
169
|
+
return object.is_output_different(empty)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
module CartoCSSHelper
|
4
|
+
class ImageForComparison
|
5
|
+
attr_reader :file_location, :description
|
6
|
+
def initialize(file_location, description)
|
7
|
+
@file_location = file_location
|
8
|
+
@description = description
|
9
|
+
end
|
10
|
+
def identical(another_image)
|
11
|
+
#Returns true if the contents of a file A and a file B are identical.
|
12
|
+
return FileUtils.compare_file(self.file_location, another_image.file_location)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class PairOfComparedImages
|
17
|
+
attr_reader :left_file_location, :right_file_location
|
18
|
+
attr_accessor :description
|
19
|
+
def initialize(left_image, right_image)
|
20
|
+
raise 'description mismatch' unless left_image.description == right_image.description
|
21
|
+
@left_file_location = left_image.file_location
|
22
|
+
@right_file_location = right_image.file_location
|
23
|
+
@description = left_image.description
|
24
|
+
end
|
25
|
+
def merge_description_from_next_image(image)
|
26
|
+
@description << ', ' << image.description
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class FullSetOfComparedImages
|
31
|
+
def initialize(before, after, header, filename, image_size)
|
32
|
+
@header = header
|
33
|
+
@filename = filename
|
34
|
+
@compared = compress(before, after)
|
35
|
+
|
36
|
+
@image_size = image_size
|
37
|
+
@margin = 20
|
38
|
+
@standard_pointsize = 10
|
39
|
+
@header_space = @standard_pointsize*1.5
|
40
|
+
@diff_note_space = @standard_pointsize
|
41
|
+
|
42
|
+
render
|
43
|
+
end
|
44
|
+
|
45
|
+
def compress(before, after)
|
46
|
+
returned = [PairOfComparedImages.new(before[0], after[0])]
|
47
|
+
(1...before.length).each { |i|
|
48
|
+
if before[i].identical(before[i-1]) && after[i].identical(after[i-1])
|
49
|
+
returned[-1].merge_description_from_next_image(before[i])
|
50
|
+
else
|
51
|
+
returned.push(PairOfComparedImages.new(before[i], after[i]))
|
52
|
+
end
|
53
|
+
}
|
54
|
+
return returned
|
55
|
+
end
|
56
|
+
|
57
|
+
def render
|
58
|
+
create_canvas
|
59
|
+
offset = 0
|
60
|
+
render_header
|
61
|
+
offset += @header_space + @margin*1.5
|
62
|
+
render_diff_note offset
|
63
|
+
offset += @diff_note_space + @margin*0.5
|
64
|
+
render_images_with_labels offset
|
65
|
+
offset += (@compared.length)*(@margin + @image_size)
|
66
|
+
render_footer offset
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_canvas
|
70
|
+
y = get_needed_y
|
71
|
+
x = @image_size*2+3*@margin
|
72
|
+
|
73
|
+
@canvas = Magick::Image.new(x, y)
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_needed_y
|
77
|
+
y = 0
|
78
|
+
y += @header_space
|
79
|
+
y += @margin*1.5
|
80
|
+
y += @diff_note_space
|
81
|
+
y += @margin*0.5
|
82
|
+
y += (@compared.length) * (@image_size + @margin)
|
83
|
+
y += @diff_note_space
|
84
|
+
return y
|
85
|
+
end
|
86
|
+
|
87
|
+
def render_header
|
88
|
+
header_drawer = Magick::Draw.new
|
89
|
+
header_drawer.pointsize(@header_space*3/5)
|
90
|
+
header_drawer.text(@margin, @header_space, @header)
|
91
|
+
header_drawer.draw(@canvas)
|
92
|
+
end
|
93
|
+
|
94
|
+
def render_diff_note(y_offset)
|
95
|
+
drawer = Magick::Draw.new
|
96
|
+
drawer.pointsize(@diff_note_space)
|
97
|
+
#drawer.text(@margin, y_offset, 'before')
|
98
|
+
drawer.draw(@canvas)
|
99
|
+
#drawer.text(@margin*2 + @image_size, y_offset, 'after')
|
100
|
+
drawer.draw(@canvas)
|
101
|
+
end
|
102
|
+
|
103
|
+
def render_images_with_labels(y_offset)
|
104
|
+
(0...@compared.length).each { |i|
|
105
|
+
render_row_of_labelled_images(@compared[i], y_offset + i * (@margin + @image_size))
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def render_row_of_labelled_images(processed, y_offset)
|
110
|
+
left_image = Magick::Image.read(processed.left_file_location)[0]
|
111
|
+
right_image = Magick::Image.read(processed.right_file_location)[0]
|
112
|
+
drawer = Magick::Draw.new
|
113
|
+
drawer.pointsize(@diff_note_space*4/5)
|
114
|
+
if left_image == right_image
|
115
|
+
drawer.text(@margin + @image_size/2, y_offset, "#{processed.description} - unchanged")
|
116
|
+
drawer.draw(@canvas)
|
117
|
+
else
|
118
|
+
drawer.text(@margin, y_offset, "#{processed.description} - before")
|
119
|
+
drawer.draw(@canvas)
|
120
|
+
drawer.text(@margin*2 + @image_size, y_offset, "#{processed.description} - after")
|
121
|
+
drawer.draw(@canvas)
|
122
|
+
end
|
123
|
+
render_row_of_images(y_offset, left_image, right_image)
|
124
|
+
end
|
125
|
+
|
126
|
+
def render_label(y_offset, label)
|
127
|
+
label_drawer = Magick::Draw.new
|
128
|
+
label_drawer.pointsize(@standard_pointsize)
|
129
|
+
label_drawer.text(@margin, y_offset, label)
|
130
|
+
label_drawer.draw(@canvas)
|
131
|
+
end
|
132
|
+
|
133
|
+
# noinspection RubyResolve
|
134
|
+
def render_row_of_images(y_offset, left_image, right_image)
|
135
|
+
if left_image == right_image
|
136
|
+
@canvas.composite!(left_image, @margin*1.5 + @image_size/2, y_offset, Magick::OverCompositeOp)
|
137
|
+
else
|
138
|
+
@canvas.composite!(left_image, @margin, y_offset, Magick::OverCompositeOp)
|
139
|
+
@canvas.composite!(right_image, @margin*2+@image_size, y_offset, Magick::OverCompositeOp)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def render_footer(y_offset)
|
144
|
+
label_drawer = Magick::Draw.new
|
145
|
+
label_drawer.pointsize(@standard_pointsize)
|
146
|
+
label_drawer.text(@margin, y_offset, 'generated using https://github.com/mkoniecz/CartoCSSHelper')
|
147
|
+
label_drawer.draw(@canvas)
|
148
|
+
end
|
149
|
+
|
150
|
+
def popup
|
151
|
+
@canvas.display
|
152
|
+
end
|
153
|
+
|
154
|
+
def save
|
155
|
+
@canvas.write(@filename)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|