cartocss_helper 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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