cartocss_helper 0.0.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.
- 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
|