cartocss_helper 1.2.1 → 4.0.0

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