cartocss_helper 1.2.1 → 4.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.
@@ -1,38 +1,28 @@
1
1
  require_relative 'configuration.rb'
2
+ require_relative 'util/systemhelper.rb'
3
+
4
+ include SystemHelper
2
5
 
3
6
  module CartoCSSHelper
4
7
  module Git
5
- def checkout(branch, debug=false)
6
- silence = '> /dev/null 2>&1'
7
- if debug
8
- silence = ''
9
- end
10
- Dir.chdir(Configuration.get_path_to_tilemill_project_folder) {
8
+ def checkout(branch, debug = false)
9
+ Dir.chdir(Configuration.get_path_to_cartocss_project_folder) do
11
10
  require 'open3'
12
- command = "git checkout #{branch} #{silence}"
13
- Open3.popen3(command) {|_, stdout, stderr, wait_thr |
14
- error = stderr.read.chomp
15
- if error != '' or wait_thr.value.success? != true
16
- raise 'failed checkout to ' + branch + ' due to ' + error
17
- end
18
- return
19
- }
20
- raise 'impossible happened'
21
- }
11
+ command = "git checkout #{branch}"
12
+ begin
13
+ execute_command(command, debug, ignore_stderr_presence: true) # or maybe just do not run if it is currently in the wanted branch?
14
+ rescue FailedCommandException => e
15
+ raise "failed checkout to #{branch} due to #{e}"
16
+ end
17
+ end
22
18
  end
23
19
 
24
20
  def get_commit_hash
25
- Dir.chdir(Configuration.get_path_to_tilemill_project_folder) {
26
- Open3.popen3('git log -n 1 --pretty=format:"%H"') {|_, stdout, stderr, wait_thr|
27
- commit = stdout.read.chomp
28
- error = stderr.read.chomp
29
- if error != '' or wait_thr.value.success? != true
30
- raise error
31
- end
32
- return commit
33
- }
34
- }
21
+ Dir.chdir(Configuration.get_path_to_cartocss_project_folder) do
22
+ command = 'git log -n 1 --pretty=format:"%H"'
23
+ return execute_command(command)
24
+ end
35
25
  raise 'impossible happened'
36
26
  end
37
27
  end
38
- end
28
+ end
@@ -7,60 +7,59 @@ module CartoCSSHelper
7
7
  tags = get_tags_from_mss
8
8
  tags.merge(get_tags_from_yaml)
9
9
  tags.merge(get_tags_from_osm2pqsql)
10
- #unrecovable = Set.new [['area', 'yes'], ['area', 'no']] #TODO - how this should be handled?
11
- #tags.merge(unrecovable)
10
+ # unrecovable = Set.new [['area', 'yes'], ['area', 'no']] #TODO - how this should be handled?
11
+ # tags.merge(unrecovable)
12
12
  return tags.to_a.sort
13
13
  end
14
14
 
15
15
  def get_tags_from_mss
16
16
  tags = Set.new
17
- filenames = Dir[Configuration.get_path_to_tilemill_project_folder+'*.mss']
18
- filenames.each { |filename|
19
- tags.merge(get_tags_from_mss_file filename)
20
- }
17
+ filenames = Dir[Configuration.get_path_to_cartocss_project_folder + '*.mss']
18
+ filenames.each do |filename|
19
+ tags.merge(get_tags_from_mss_file(filename))
20
+ end
21
21
  return tags
22
22
  end
23
23
 
24
24
  def get_tags_from_yaml
25
25
  tags = Set.new
26
- filenames = Dir[Configuration.get_path_to_tilemill_project_folder+'*.yaml']
27
- filenames.each { |filename|
28
- tags.merge(get_tags_from_yaml_file filename)
29
- }
30
- return tags
26
+ filenames = Dir[Configuration.get_path_to_cartocss_project_folder + '*.yaml']
27
+ filenames.each do |filename|
28
+ tags.merge(get_tags_from_yaml_file(filename))
29
+ end
30
+ return tags
31
31
  end
32
32
 
33
33
  def get_tags_from_osm2pqsql
34
34
  tags = Set.new
35
- filenames = Dir[Configuration.get_path_to_tilemill_project_folder+'*.style']
36
- filenames.each { |filename|
37
- tags.merge(get_tags_from_osm2pqsql_file filename)
38
- }
39
- return tags
35
+ filenames = Dir[Configuration.get_path_to_cartocss_project_folder + '*.style']
36
+ filenames.each do |filename|
37
+ tags.merge(get_tags_from_osm2pqsql_file(filename))
38
+ end
39
+ return tags
40
40
  end
41
41
 
42
42
  def get_tags_from_mss_file(style_filename)
43
43
  possible_key_values = get_tags_from_osm2pqsql
44
44
  tags = Set.new
45
- #puts style_filename
45
+ # puts style_filename
46
46
  style_file = open(style_filename)
47
47
  style = style_file.read
48
48
  matched = style.scan(/\[feature = '(man_made|[^_]+)_([^']+)'\]/)
49
- matched.each { |element|
49
+ matched.each do |element|
50
50
  key = element[0]
51
51
  if possible_key_values.include?([key, get_generic_tag_value])
52
52
  tags.add([key, element[1]])
53
53
  end
54
- }
54
+ end
55
55
  matched = style.scan(/(\w+) = '(\w+)'/)
56
- matched.each { |element|
56
+ matched.each do |element|
57
57
  key = element[0]
58
- if key != 'feature'
59
- if possible_key_values.include?([key, get_generic_tag_value])
60
- tags.add([key, element[1]])
61
- end
58
+ next unless key != 'feature'
59
+ if possible_key_values.include?([key, get_generic_tag_value])
60
+ tags.add([key, element[1]])
62
61
  end
63
- }
62
+ end
64
63
  return tags
65
64
  end
66
65
 
@@ -69,32 +68,31 @@ module CartoCSSHelper
69
68
  tags = Set.new
70
69
  yaml_file = open(yaml_filename)
71
70
  yaml = yaml_file.read
72
- #(.*\.|) #WHERE p.highway = 'turning_circle'
73
- #("|) #natural and other SQL keywords
74
- #([^"() ]+) #tag
75
- #repeat
71
+ # (.*\.|) #WHERE p.highway = 'turning_circle'
72
+ # ("|) #natural and other SQL keywords
73
+ # ([^"() ]+) #tag
74
+ # repeat
76
75
  # =
77
- #'([^'()]+)' #quoted value
76
+ # '([^'()]+)' #quoted value
78
77
  matched = yaml.scan(/(.*\.|)("|)([^"() ]+)("|) = '([^'()]+)'/)
79
- matched.each { |element|
78
+ matched.each do |element|
80
79
  key = element[2]
81
80
  value = element[4]
82
81
  if possible_key_values.include?([key, get_generic_tag_value])
83
82
  tags.add([key, value])
84
83
  end
85
- }
84
+ end
86
85
  matched = yaml.scan(/("|)([^"() ]+)("|) (NOT |)IN \(([^()]*)\)/)
87
- matched.each { |element|
86
+ matched.each do |element|
88
87
  tag = element[1]
89
88
  key = tag.gsub(/.*\./, '')
90
89
  values = element[4]
91
90
  values_matched = values.scan(/'([^']+)'/)
92
- if possible_key_values.include?([key, get_generic_tag_value])
93
- values_matched.each { |value|
94
- tags.add([key, value[0]])
95
- }
91
+ next unless possible_key_values.include?([key, get_generic_tag_value])
92
+ values_matched.each do |value|
93
+ tags.add([key, value[0]])
96
94
  end
97
- }
95
+ end
98
96
  return tags
99
97
  end
100
98
 
@@ -104,13 +102,13 @@ module CartoCSSHelper
104
102
 
105
103
  def get_tags_from_osm2pqsql_file(style_filename)
106
104
  tags = Set.new
107
- #puts style_filename
105
+ # puts style_filename
108
106
  style_file = open(style_filename)
109
107
  style = style_file.read
110
108
  matched = style.scan(/^(node,way|node|way)\s*([^ ]+)\s*text\s*($|linear|polygon|nocache)/)
111
- matched.each { |element|
109
+ matched.each do |element|
112
110
  tags.add([element[1], get_generic_tag_value])
113
- }
111
+ end
114
112
  return tags
115
113
  end
116
114
  end
@@ -2,15 +2,18 @@
2
2
  require_relative 'configuration.rb'
3
3
  require_relative 'heuristic.rb'
4
4
  require_relative 'data_file_handling.rb'
5
- require_relative 'tilemill_handler.rb'
5
+ require_relative 'renderer_handler.rb'
6
6
  require 'fileutils'
7
7
  include CartoCSSHelper::Configuration
8
8
  include CartoCSSHelper::Heuristic
9
9
 
10
10
  module CartoCSSHelper
11
+ # Defines and generates images for synthethic comparison
12
+ # it is mostly glue between renderer and generator of synthethic data files
11
13
  class Scene
12
14
  attr_reader :tags, :zlevel, :on_water, :type
13
- def initialize(tags, zlevel, on_water, type)
15
+ def initialize(tags, zlevel, on_water, type, show_what_is_generated = false)
16
+ @show_what_is_generated = show_what_is_generated
14
17
  raise 'tags not in hash' unless tags.respond_to?(:has_key?)
15
18
  @tags = tags
16
19
  @zlevel = zlevel
@@ -20,57 +23,52 @@ module CartoCSSHelper
20
23
  @tags['area'] = 'yes'
21
24
  @type = 'closed_way'
22
25
  end
26
+ @generated_image_location = nil
23
27
  end
24
28
 
25
29
  def is_output_different(another_scene)
26
- if @on_water != another_scene.on_water
27
- raise 'on_water mismatch'
28
- end
29
- #Returns true if the contents of a file A and a file B are identical.
30
- return !FileUtils.compare_file(self.get_image_filename, another_scene.get_image_filename)
30
+ raise 'on_water mismatch' if @on_water != another_scene.on_water
31
+ return !is_output_identical(another_scene)
32
+ end
33
+
34
+ def is_output_identical(another_scene)
35
+ raise 'on_water mismatch' if @on_water != another_scene.on_water
36
+ # Returns true if the contents of a file A and a file B are identical.
37
+ return FileUtils.compare_file(get_image_filename, another_scene.get_image_filename)
31
38
  end
32
39
 
33
40
  def flush_cache
34
- File.delete(self.get_filename)
41
+ File.delete(get_filename)
42
+ end
43
+
44
+ def on_water_string
45
+ on_water_string = ''
46
+ on_water_string = 'on_water' if @on_water
47
+ return on_water_string
35
48
  end
36
49
 
37
- def get_image_filename(silent=true, debug=false)
50
+ def get_image_filename(debug = false) # TODO: misleading name - should be location
38
51
  lat = 0
39
52
  lon = 20
40
- on_water_string = ''
41
- if @on_water
42
- lon = 0
43
- on_water_string = 'on_water'
44
- end
45
- export_filename = self.get_filename
46
- if File.exists?(export_filename)
47
- return export_filename
53
+ lon = 0 if @on_water
54
+ if @generated_image_location != nil
55
+ return @generated_image_location if File.exist?(@generated_image_location)
48
56
  end
49
- description = "tags: #{@tags.to_s}, zlevel: #{@zlevel}, type: #{@type} #{on_water_string}"
50
- if !silent
51
- puts "generating: #{description}"
57
+ description = "tags: #{@tags}, zlevel: #{@zlevel}, type: #{@type} #{on_water_string}"
58
+ puts "generating: #{description}" if @show_what_is_generated
59
+ generate_map(lat, lon, debug)
60
+ unless File.exist?(@generated_image_location) && @generated_image_location != nil
61
+ raise "get_image failed - #{description}. File <\n#{@generated_image_location}\n> was expected."
52
62
  end
53
- generate_map(lat, lon, debug)
54
- if !File.exists?(export_filename)
55
- description = "get_image failed - #{description}. File <\n#{export_filename}\n> was expected."
56
- if debug
57
- raise description
58
- else
59
- puts description
60
- return get_image_filename(false, true)
61
- end
62
- end
63
- return export_filename
63
+ return @generated_image_location
64
64
  end
65
65
 
66
66
  protected
67
67
 
68
68
  def get_filename
69
69
  water_part = ''
70
- if @on_water
71
- water_part = '_water'
72
- end
73
- return Configuration.get_path_to_folder_for_branch_specific_cache+@tags.to_a.sort.to_s+'_'+@zlevel.to_s+water_part+'_'+@type+'.png'
70
+ water_part = '_water' if @on_water
71
+ return @tags.to_a.sort.to_s + '_' + @zlevel.to_s + water_part + '_' + @type + '.png'
74
72
  end
75
73
 
76
74
  def generate_map(lat, lon, debug)
@@ -85,9 +83,9 @@ module CartoCSSHelper
85
83
  end
86
84
 
87
85
  def generate_image(lat, lon, debug)
88
- export_filename = self.get_filename
89
- bbox_size = self.get_bbox_size
90
- TilemillHandler.run_tilemill_export_image(lat, lon, @zlevel, [bbox_size, bbox_size], 200, export_filename, debug)
86
+ export_filename = get_filename
87
+ bbox_size = get_bbox_size
88
+ @generated_image_location = RendererHandler.request_image_from_renderer(lat, lon, @zlevel, [bbox_size, bbox_size], 200, export_filename, debug)
91
89
  end
92
90
  end
93
91
  end
@@ -0,0 +1,46 @@
1
+ require_relative 'util/generic_cached_downloader.rb'
2
+
3
+ module CartoCSSHelper
4
+ class NotesDownloader
5
+ def self.run_note_query(lat, lon, range, invalidate_cache: false)
6
+ timeout = NotesDownloader.get_allowed_timeout_in_seconds
7
+ downloader = GenericCachedDownloader.new(timeout: timeout, stop_on_timeout: false)
8
+
9
+ url = NotesDownloader.format_query_into_url(lat, lon, range)
10
+ cache_filename = CartoCSSHelper::Configuration.get_path_to_folder_for_notes_api_cache + url.delete("/") + ".cache"
11
+ return downloader.get_specified_resource(url, cache_filename, invalidate_cache: invalidate_cache)
12
+ end
13
+
14
+ def self.cache_timestamp(lat, lon, range)
15
+ downloader = GenericCachedDownloader.new
16
+
17
+ url = NotesDownloader.format_query_into_url(lat, lon, range)
18
+ cache_filename = CartoCSSHelper::Configuration.get_path_to_folder_for_notes_api_cache + url.delete("/") + ".cache"
19
+ return downloader.get_cache_timestamp(cache_filename)
20
+ end
21
+
22
+ def self.get_allowed_timeout_in_seconds
23
+ return 10 * 60
24
+ end
25
+
26
+ def self.format_query_into_url(lat, lon, range)
27
+ # documentated at http://wiki.openstreetmap.org/wiki/API_v0.6#Map_Notes_API
28
+ bbox = osm_api_bbox_string(lat, lon, range)
29
+ base_url = "http://api.openstreetmap.org/api/0.6/notes"
30
+ return "#{base_url}?bbox=#{bbox}?closed=0"
31
+ end
32
+
33
+ def self.osm_api_bbox_string(latitude, longitude, size)
34
+ # bbox=left,bottom,right,top
35
+ min_latitude = latitude - size / 2
36
+ max_latitude = latitude + size / 2
37
+ min_longitude = longitude - size / 2
38
+ max_longitude = longitude + size / 2
39
+ return "#{min_longitude},#{min_latitude},#{max_longitude},#{max_latitude}"
40
+ end
41
+
42
+ def self.get_overpass_instance_url
43
+ return CartoCSSHelper::Configuration.get_overpass_instance_url
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'util/generic_downloader.rb'
2
+ require_relative 'util/generic_cached_downloader.rb'
3
+ require 'uri' # for URI.escape
4
+
5
+ module CartoCSSHelper
6
+ class OverpassDownloader
7
+ class OverpassRefusedResponse < IOError; end
8
+
9
+ def self.run_overpass_query(query, description, cache_filename)
10
+ url = OverpassDownloader.format_query_into_url(query)
11
+ timeout = OverpassDownloader.get_allowed_timeout_in_seconds
12
+ downloader = GenericCachedDownloader.new(timeout: timeout, stop_on_timeout: false)
13
+ return downloader.get_specified_resource(url, cache_filename, description: description)
14
+ rescue RequestTimeout => e
15
+ puts 'Overpass API refused to process this request. It will be not attempted again, most likely query is too complex. It is also possible that Overpass servers are unavailable'
16
+ puts
17
+ puts query
18
+ puts
19
+ puts url
20
+ puts
21
+ puts e
22
+ raise OverpassRefusedResponse
23
+ end
24
+
25
+ def self.get_allowed_timeout_in_seconds
26
+ return 10 * 60
27
+ end
28
+
29
+ def self.format_query_into_url(query)
30
+ query = query.gsub(/\/\/.*\n/, '') # add proper parsing - it will mutilate // inside quotes etc
31
+ query = query.gsub('\\', '\\\\')
32
+ query = query.delete("\n")
33
+ query = query.delete("\t")
34
+ base_overpass_url = OverpassDownloader.get_overpass_instance_url
35
+ return base_overpass_url + '/interpreter?data=' + URI.escape(query)
36
+ end
37
+
38
+ def self.get_overpass_instance_url
39
+ return CartoCSSHelper::Configuration.get_overpass_instance_url
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,248 @@
1
+ # encoding: UTF-8
2
+ require 'digest/sha1'
3
+ require 'sys/filesystem'
4
+ require_relative 'overpass_downloader.rb'
5
+ require_relative 'util/systemhelper.rb'
6
+
7
+ module CartoCSSHelper
8
+ class OverpassQueryGenerator
9
+ class NoLocationFound < StandardError
10
+ end
11
+ # TODO: - split into cache handling and Overpass handling
12
+ def self.get_query_to_find_data_pair(bb, tags_a, tags_b, type_a, type_b, distance_in_meters: 20)
13
+ filter_a = OverpassQueryGenerator.turn_list_of_tags_in_overpass_filter(tags_a)
14
+ filter_b = OverpassQueryGenerator.turn_list_of_tags_in_overpass_filter(tags_b)
15
+
16
+ query = "[timeout:#{OverpassDownloader.get_allowed_timeout_in_seconds}][out:csv(::lat,::lon;false)];
17
+ #{type_a}(#{bb})#{filter_a};
18
+ node(around:#{distance_in_meters})->.anodes;
19
+ #{type_b}(#{bb})#{filter_b};
20
+ node(around:#{distance_in_meters}).anodes;
21
+ out;"
22
+
23
+ return query
24
+ end
25
+
26
+ def self.overpass_bbox_string(latitude, longitude, size)
27
+ min_latitude = latitude - size / 2
28
+ max_latitude = latitude + size / 2
29
+ min_longitude = longitude - size / 2
30
+ max_longitude = longitude + size / 2
31
+ return "#{min_latitude},#{min_longitude},#{max_latitude},#{max_longitude}"
32
+ end
33
+
34
+ def self.find_data_pair(tags_a, tags_b, latitude, longitude, type_a, type_b, bb_size: 0.1, distance_in_meters: 20)
35
+ return nil, nil if bb_size > 0.5
36
+ bb = overpass_bbox_string(latitude, longitude, bb_size.to_f)
37
+ query = OverpassQueryGenerator.get_query_to_find_data_pair(bb, tags_a, tags_b, type_a, type_b, distance_in_meters: distance_in_meters)
38
+ description = "find #{VisualDiff.tag_dict_to_string(tags_a)} nearby #{VisualDiff.tag_dict_to_string(tags_b)} - bb size: #{bb_size}"
39
+ list = OverpassQueryGenerator.get_overpass_query_results(query, description)
40
+ if list.length != 0
41
+ return OverpassQueryGenerator.list_returned_by_overpass_to_a_single_location(list)
42
+ end
43
+ return OverpassQueryGenerator.find_data_pair(tags_a, tags_b, latitude, longitude, type_a, type_b, bb_size: bb_size * 2, distance_in_meters: distance_in_meters)
44
+ end
45
+
46
+ def self.get_file_with_downloaded_osm_data_for_location(latitude, longitude, size)
47
+ query = get_query_to_download_data_around_location(latitude, longitude, size)
48
+ description = "download data for #{latitude} #{longitude} (#{size})"
49
+ get_overpass_query_results(query, description)
50
+
51
+ filename = get_query_cache_filename(query)
52
+ return filename
53
+ end
54
+
55
+ def self.download_osm_data_for_location(latitude, longitude, size, accept_cache = true)
56
+ filename = CartoCSSHelper::Configuration.get_path_to_folder_for_cache + "#{latitude} #{longitude} #{size}.osm"
57
+ if File.exist?(filename)
58
+ return filename if accept_cache
59
+ SystemHelper.delete_file(filename, 'query refusing to accept cache was used')
60
+ end
61
+ query = get_query_to_download_data_around_location(latitude, longitude, size)
62
+ text = get_overpass_query_results(query, "download data for #{latitude} #{longitude} (#{size})")
63
+ File.new(filename, 'w') do |file|
64
+ file.write text
65
+ end
66
+ return filename
67
+ end
68
+
69
+ def self.get_query_to_download_data_around_location(latitude, longitude, size)
70
+ bb = overpass_bbox_string(latitude, longitude, size)
71
+ query = "[timeout:#{OverpassDownloader.get_allowed_timeout_in_seconds}];"
72
+ query += "\n"
73
+ query += "(node(#{bb});<;);"
74
+ query += "\n"
75
+ query += 'out;'
76
+ query += "\n"
77
+ query += '/*'
78
+ query += "\nbbox size: #{size}"
79
+ query += "\nhttp://www.openstreetmap.org/#map=17/#{latitude}/#{longitude}"
80
+ query += "\n"
81
+ query += '*/'
82
+ query += "\n"
83
+ return query
84
+ end
85
+
86
+ def self.get_elements_near_given_location(tags, type, latitude, longitude, range_in_meters)
87
+ description = "find #{tags} #{type} within #{range_in_meters / 1000}km from #{latitude}, #{longitude}"
88
+ query = OverpassQueryGenerator.get_query_to_get_location(tags, type, latitude, longitude, range_in_meters)
89
+ list = OverpassQueryGenerator.get_overpass_query_results(query, description)
90
+ return list_returned_by_overpass_to_array(list)
91
+ end
92
+
93
+ def self.get_elements_across_world(tags, type)
94
+ description = "find #{tags} #{type} across the world"
95
+ query = OverpassQueryGenerator.get_query_to_get_location(tags, type, 0, 0, :infinity)
96
+ list = OverpassQueryGenerator.get_overpass_query_results(query, description)
97
+ return list_returned_by_overpass_to_array(list)
98
+ end
99
+
100
+ def self.locate_element_with_given_tags_and_type(tags, type, latitude, longitude, max_range_in_km_for_radius = 1600)
101
+ # special support for following tag values: :any_value
102
+ range = 10 * 1000
103
+ while range <= max_range_in_km_for_radius * 1000
104
+ list = OverpassQueryGenerator.get_elements_near_given_location(tags, type, latitude, longitude, range)
105
+ return list[0] if list.length != 0
106
+ range += [2 * range, 200_000].min
107
+ end
108
+ list = get_elements_across_world(tags, type)
109
+ if list.length != 0
110
+ return list[0]
111
+ else
112
+ raise NoLocationFound, "failed to find #{tags} #{type} across the world"
113
+ end
114
+ end
115
+
116
+ def self.list_returned_by_overpass_to_a_single_location(list)
117
+ list = list_returned_by_overpass_to_array(list)
118
+ return list[0]
119
+ end
120
+
121
+ def self.list_returned_by_overpass_to_array(list)
122
+ list = list.split("\n")
123
+ new_list = list.map do |x|
124
+ x = x.split("\t")
125
+ x[0] = x[0].to_f
126
+ x[1] = x[1].to_f
127
+ x
128
+ end
129
+ return new_list
130
+ end
131
+
132
+ def self.get_query_to_get_location(tags, type, latitude, longitude, range)
133
+ return get_query_to_get_location_set_format(tags, type, latitude, longitude, range, "[out:csv(::lat,::lon;false)]")
134
+ end
135
+
136
+ def self.get_query_to_get_location_set_format(tags, type, latitude, longitude, range, format)
137
+ # special support for following tag values: :any_value
138
+ locator = "[timeout:#{OverpassDownloader.get_allowed_timeout_in_seconds}]#{format};"
139
+ locator += "\n"
140
+ type = 'way' if type == 'closed_way'
141
+ locator += OverpassQueryGenerator.get_query_element_to_get_location(tags, latitude, longitude, type, range)
142
+ locator += 'out center;'
143
+ locator += "\n"
144
+ locator += '/*'
145
+ range_string = if range == :infinity
146
+ 'infinity'
147
+ else
148
+ "#{range / 1000}km"
149
+ end
150
+ locator += "\nrange: #{range_string}"
151
+ locator += "\nhttp://www.openstreetmap.org/#map=17/#{latitude}/#{longitude}"
152
+ locator += "\n"
153
+ locator += '*/'
154
+ locator += "\n"
155
+ return locator
156
+ end
157
+
158
+ def self.turn_list_of_tags_in_overpass_filter(tags)
159
+ element = ''
160
+ tags.each do |tag|
161
+ element += if tag[1] == :any_value
162
+ "\t['#{tag[0]}']"
163
+ else
164
+ "\t['#{tag[0]}'='#{tag[1]}']"
165
+ end
166
+ element += "\n"
167
+ end
168
+ return element
169
+ end
170
+
171
+ def self.get_query_element_to_get_location(tags, latitude, longitude, type, range)
172
+ # special support for following tag values: :any_value
173
+ # TODO - escape value with quotation signs in them
174
+ element = "(#{type}"
175
+ element += OverpassQueryGenerator.turn_list_of_tags_in_overpass_filter(tags)
176
+ element += "\n"
177
+ if range != :infinity
178
+ element += "\t(around:#{range},#{latitude},#{longitude});"
179
+ element += "\n"
180
+ end
181
+ element += ');'
182
+ element += "\n"
183
+ return element
184
+ end
185
+
186
+ def self.get_query_cache_refused_response_filename(query)
187
+ return get_query_cache_filename(query) + '_response_refused'
188
+ end
189
+
190
+ def self.mark_query_as_refused(query)
191
+ file = File.new(get_query_cache_refused_response_filename(query), 'w')
192
+ file.write ''
193
+ file.close
194
+ end
195
+
196
+ def self.get_query_cache_filename(query)
197
+ hash = Digest::SHA1.hexdigest query
198
+ query_cache_filename = CartoCSSHelper::Configuration.get_path_to_folder_for_overpass_cache + hash + '_query.cache'
199
+ return query_cache_filename
200
+ end
201
+
202
+ def self.get_overpass_query_results(query, description, debug = false)
203
+ if File.exist?(get_query_cache_refused_response_filename(query))
204
+ raise OverpassDownloader::OverpassRefusedResponse
205
+ end
206
+
207
+ check_for_free_space
208
+
209
+ if debug
210
+ puts query
211
+ puts
212
+ end
213
+
214
+ cache_filename = get_query_cache_filename(query)
215
+ description = 'Running Overpass query (connection initiated on ' + Time.now.to_s + ') ' + description
216
+ begin
217
+ return OverpassDownloader.run_overpass_query query, description, cache_filename
218
+ rescue OverpassDownloader::OverpassRefusedResponse
219
+ mark_query_as_refused(query)
220
+ raise OverpassDownloader::OverpassRefusedResponse
221
+ end
222
+ end
223
+
224
+ def self.check_for_free_space # TODO: it really does not belong here... - do system helpera? aż do końca klasy
225
+ if SystemHelper.not_enough_free_space
226
+ attempt_cleanup
227
+ if SystemHelper.not_enough_free_space
228
+ raise 'not enough free space on disk with cache folder'
229
+ end
230
+ end
231
+ end
232
+
233
+ def self.attempt_cleanup
234
+ delete_large_overpass_caches 500 if SystemHelper.not_enough_free_space
235
+ delete_large_overpass_caches 100 if SystemHelper.not_enough_free_space
236
+ delete_large_overpass_caches 50 if SystemHelper.not_enough_free_space
237
+ end
238
+
239
+ def self.delete_large_overpass_caches(threshold_in_MB)
240
+ # TODO: - find library that deals with caches like this, bug here may be unfunny
241
+ Dir.glob(CartoCSSHelper::Configuration.get_path_to_folder_for_overpass_cache + '*') do |file|
242
+ if File.size(file) > (1024 * 1024 * threshold_in_MB)
243
+ SystemHelper.delete_file(file, "removing everpass cache entries larger than #{threshold_in_MB} MB to make free space on the disk")
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end