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.
@@ -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