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.
- checksums.yaml +7 -0
- data/lib/cartocss_helper.rb +56 -71
- data/lib/cartocss_helper/configuration.rb +84 -37
- data/lib/cartocss_helper/data_file_handling.rb +32 -35
- data/lib/cartocss_helper/git.rb +17 -27
- data/lib/cartocss_helper/heuristic.rb +39 -41
- data/lib/cartocss_helper/image_generator.rb +36 -38
- data/lib/cartocss_helper/notes_downloader.rb +46 -0
- data/lib/cartocss_helper/overpass_downloader.rb +42 -0
- data/lib/cartocss_helper/overpass_query_generator.rb +248 -0
- data/lib/cartocss_helper/renderer_handler.rb +116 -0
- data/lib/cartocss_helper/style_specific/default_osm_style.rb +722 -546
- data/lib/cartocss_helper/style_specific/style_specific.rb +4 -3
- data/lib/cartocss_helper/tag_lister.rb +136 -112
- data/lib/cartocss_helper/util/filehelper.rb +1 -1
- data/lib/cartocss_helper/util/generic_cached_downloader.rb +49 -0
- data/lib/cartocss_helper/util/generic_downloader.rb +65 -0
- data/lib/cartocss_helper/util/logger.rb +30 -0
- data/lib/cartocss_helper/util/rest-client_wrapper.rb +53 -0
- data/lib/cartocss_helper/util/systemhelper.rb +55 -0
- data/lib/cartocss_helper/validator.rb +142 -81
- data/lib/cartocss_helper/visualise_changes_diff_from_images.rb +36 -34
- data/lib/cartocss_helper/visualise_changes_image_generation.rb +72 -100
- data/lib/data/testing_locations.rb +17 -15
- data/readme.md +34 -9
- metadata +140 -39
- data/lib/cartocss_helper/downloader.rb +0 -301
- data/lib/cartocss_helper/tilemill_handler.rb +0 -38
data/lib/cartocss_helper/git.rb
CHANGED
@@ -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
|
-
|
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}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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.
|
26
|
-
|
27
|
-
|
28
|
-
|
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.
|
18
|
-
filenames.each
|
19
|
-
tags.merge(get_tags_from_mss_file
|
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.
|
27
|
-
filenames.each
|
28
|
-
tags.merge(get_tags_from_yaml_file
|
29
|
-
|
30
|
-
return
|
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.
|
36
|
-
filenames.each
|
37
|
-
tags.merge(get_tags_from_osm2pqsql_file
|
38
|
-
|
39
|
-
return
|
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
|
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
|
56
|
+
matched.each do |element|
|
57
57
|
key = element[0]
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
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
|
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
|
-
|
93
|
-
|
94
|
-
|
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
|
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 '
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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(
|
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(
|
50
|
+
def get_image_filename(debug = false) # TODO: misleading name - should be location
|
38
51
|
lat = 0
|
39
52
|
lon = 20
|
40
|
-
|
41
|
-
if @
|
42
|
-
|
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
|
50
|
-
if
|
51
|
-
|
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
|
-
|
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
|
-
|
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 =
|
89
|
-
bbox_size =
|
90
|
-
|
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
|