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
@@ -1,301 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
require 'rest-client'
|
3
|
-
require 'digest/sha1'
|
4
|
-
require 'sys/filesystem'
|
5
|
-
|
6
|
-
|
7
|
-
module CartoCSSHelper
|
8
|
-
class Downloader
|
9
|
-
def self.get_file_with_downloaded_osm_data_for_location(latitude, longitude, size)
|
10
|
-
query = get_query_to_download_data_around_location(latitude, longitude, size)
|
11
|
-
return get_overpass_query_results_file_location(query, "download data for #{latitude} #{longitude} (#{size})")
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.download_osm_data_for_location(latitude, longitude, size, accept_cache=true)
|
15
|
-
filename = CartoCSSHelper::Configuration.get_path_to_folder_for_cache + "#{latitude} #{longitude} #{size}.osm"
|
16
|
-
if File.exists?(filename)
|
17
|
-
if accept_cache
|
18
|
-
return filename
|
19
|
-
end
|
20
|
-
delete_file(filename, 'query refusing to accept cache was used')
|
21
|
-
end
|
22
|
-
query = get_query_to_download_data_around_location(latitude, longitude, size)
|
23
|
-
text = get_overpass_query_results(query, "download data for #{latitude} #{longitude} (#{size})")
|
24
|
-
file = File.new(filename, 'w')
|
25
|
-
file.write text
|
26
|
-
file.close
|
27
|
-
return filename
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.get_query_to_download_data_around_location(latitude, longitude, size)
|
31
|
-
min_latitude = latitude - size.to_f/2
|
32
|
-
max_latitude = latitude + size.to_f/2
|
33
|
-
min_longitude = longitude - size.to_f/2
|
34
|
-
max_longitude = longitude + size.to_f/2
|
35
|
-
bb = "#{min_latitude},#{min_longitude},#{max_latitude},#{max_longitude}"
|
36
|
-
query = "[timeout:#{Downloader.get_allowed_timeout_in_seconds}];"
|
37
|
-
query += "\n"
|
38
|
-
query += "(node(#{bb});<;);"
|
39
|
-
query += "\n"
|
40
|
-
query += 'out;'
|
41
|
-
query += "\n"
|
42
|
-
query += '/*'
|
43
|
-
query += "\nbbox size: #{size}"
|
44
|
-
query += "\nhttp://www.openstreetmap.org/#map=17/#{latitude}/#{longitude}"
|
45
|
-
query += "\n"
|
46
|
-
query += '*/'
|
47
|
-
query += "\n"
|
48
|
-
return query
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.locate_element_with_given_tags_and_type(tags, type, latitude, longitude)
|
52
|
-
max_range_in_km_for_radius = 1600
|
53
|
-
|
54
|
-
#special support for following tag values: :any_value
|
55
|
-
range = 10*1000
|
56
|
-
loop do
|
57
|
-
list = Downloader.get_overpass_query_results(Downloader.get_query_to_get_location(tags, type, latitude, longitude, range), "find #{tags} within #{range/1000}km from #{latitude}, #{longitude}")
|
58
|
-
if list.length != 0
|
59
|
-
return self.list_returned_by_overpass_to_a_single_location(list)
|
60
|
-
end
|
61
|
-
range=range+[2*range, 200000].min
|
62
|
-
if range >= max_range_in_km_for_radius*1000
|
63
|
-
list = Downloader.get_overpass_query_results(Downloader.get_query_to_get_location(tags, type, latitude, longitude, :infinity), "find #{tags} across the world")
|
64
|
-
if list.length != 0
|
65
|
-
return self.list_returned_by_overpass_to_a_single_location(list)
|
66
|
-
else
|
67
|
-
raise 'failed to find such location'
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.list_returned_by_overpass_to_a_single_location(list)
|
74
|
-
list = list.match(/((|-)[\d\.]+)\s+((|-)[\d\.]+)/).to_a
|
75
|
-
lat = Float(list[1])
|
76
|
-
lon = Float(list[3])
|
77
|
-
return lat, lon
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.get_query_to_get_location(tags, type, latitude, longitude, range)
|
81
|
-
#special support for following tag values: :any_value
|
82
|
-
locator = "[timeout:#{Downloader.get_allowed_timeout_in_seconds}][out:csv(::lat,::lon;false)];"
|
83
|
-
locator += "\n"
|
84
|
-
if type == 'closed_way'
|
85
|
-
type = 'way'
|
86
|
-
end
|
87
|
-
locator += Downloader.get_query_element_to_get_location(tags, latitude, longitude, type, range)
|
88
|
-
locator +='out center;'
|
89
|
-
locator += "\n"
|
90
|
-
locator += '/*'
|
91
|
-
range_string = ''
|
92
|
-
if range == :infinity
|
93
|
-
range_string = 'infinity'
|
94
|
-
else
|
95
|
-
range_string = "#{range/1000}km"
|
96
|
-
end
|
97
|
-
locator += "\nrange: #{range_string}"
|
98
|
-
locator += "\nhttp://www.openstreetmap.org/#map=17/#{latitude}/#{longitude}"
|
99
|
-
locator += "\n"
|
100
|
-
locator += '*/'
|
101
|
-
locator += "\n"
|
102
|
-
return locator
|
103
|
-
end
|
104
|
-
|
105
|
-
def self.get_query_element_to_get_location(tags, latitude, longitude, type, range)
|
106
|
-
#special support for following tag values: :any_value
|
107
|
-
#TODO - escape value with quotation signs in them
|
108
|
-
element="(#{type}"
|
109
|
-
element += "\n"
|
110
|
-
tags.each {|tag|
|
111
|
-
if tag[1] == :any_value
|
112
|
-
element+="\t['#{tag[0]}']"
|
113
|
-
else
|
114
|
-
element+="\t['#{tag[0]}'='#{tag[1]}']"
|
115
|
-
end
|
116
|
-
element += "\n"
|
117
|
-
}
|
118
|
-
if range != :infinity
|
119
|
-
element+="\t(around:#{range},#{latitude},#{longitude});"
|
120
|
-
element += "\n"
|
121
|
-
end
|
122
|
-
element+=');'
|
123
|
-
element += "\n"
|
124
|
-
return element
|
125
|
-
end
|
126
|
-
|
127
|
-
def self.get_overpass_query_results_file_location(query, description, debug=false)
|
128
|
-
filename = get_query_cache_filename(query)
|
129
|
-
get_overpass_query_results(query, description, debug)
|
130
|
-
return filename
|
131
|
-
end
|
132
|
-
|
133
|
-
def self.get_overpass_query_results(query, description, debug=false)
|
134
|
-
cached = get_overpass_query_results_from_cache(query)
|
135
|
-
if cached == ''
|
136
|
-
if File.exists?(get_query_cache_refused_response_filename(query))
|
137
|
-
raise OverpassRefusedResponse
|
138
|
-
end
|
139
|
-
end
|
140
|
-
return cached unless cached == nil
|
141
|
-
|
142
|
-
check_for_free_space
|
143
|
-
|
144
|
-
puts 'Running Overpass query (connection initiated on ' + Time.now.to_s + ') ' + description
|
145
|
-
if debug
|
146
|
-
puts query
|
147
|
-
puts
|
148
|
-
end
|
149
|
-
begin
|
150
|
-
cached = Downloader.run_overpass_query query, description
|
151
|
-
rescue OverpassRefusedResponse
|
152
|
-
mark_query_as_refused(query)
|
153
|
-
write_to_cache(query, '')
|
154
|
-
raise OverpassRefusedResponse
|
155
|
-
end
|
156
|
-
write_to_cache(query, cached)
|
157
|
-
return cached
|
158
|
-
end
|
159
|
-
|
160
|
-
def self.get_query_cache_refused_response_filename(query)
|
161
|
-
return get_query_cache_filename(query)+'_response_refused'
|
162
|
-
end
|
163
|
-
|
164
|
-
def self.mark_query_as_refused(query)
|
165
|
-
file = File.new(get_query_cache_refused_response_filename(query), 'w')
|
166
|
-
file.write ''
|
167
|
-
file.close
|
168
|
-
end
|
169
|
-
|
170
|
-
def self.write_to_cache(query, response)
|
171
|
-
file = File.new(get_query_cache_filename(query), 'w')
|
172
|
-
file.write response
|
173
|
-
file.close
|
174
|
-
end
|
175
|
-
|
176
|
-
def self.get_timestamp_of_file(timestamp_filename)
|
177
|
-
if !File.exists?(timestamp_filename)
|
178
|
-
return nil
|
179
|
-
end
|
180
|
-
f = File.new(timestamp_filename)
|
181
|
-
timestamp = f.mtime.to_i
|
182
|
-
f.close
|
183
|
-
return timestamp
|
184
|
-
end
|
185
|
-
|
186
|
-
def self.get_overpass_query_results_from_cache(query)
|
187
|
-
query_cache_filename = get_query_cache_filename(query)
|
188
|
-
if File.exists?(query_cache_filename)
|
189
|
-
file = File.new(query_cache_filename)
|
190
|
-
cached = file.read
|
191
|
-
file.close
|
192
|
-
return cached
|
193
|
-
end
|
194
|
-
return nil
|
195
|
-
end
|
196
|
-
|
197
|
-
def self.get_query_cache_filename(query)
|
198
|
-
# noinspection RubyResolve
|
199
|
-
hash = Digest::SHA1.hexdigest query
|
200
|
-
query_cache_filename = CartoCSSHelper::Configuration.get_path_to_folder_for_overpass_cache + hash + '_query.cache'
|
201
|
-
return query_cache_filename
|
202
|
-
end
|
203
|
-
|
204
|
-
def self.check_for_free_space
|
205
|
-
if not_enough_free_space
|
206
|
-
attempt_cleanup
|
207
|
-
if not_enough_free_space
|
208
|
-
raise 'less than 2GB of free space on disk with cache folder'
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def self.not_enough_free_space
|
214
|
-
minimum_gb = 1
|
215
|
-
return get_available_space_for_cache_in_gb < minimum_gb
|
216
|
-
end
|
217
|
-
|
218
|
-
def self.get_available_space_for_cache_in_gb
|
219
|
-
stat = Sys::Filesystem.stat(CartoCSSHelper::Configuration.get_path_to_folder_for_cache)
|
220
|
-
return stat.block_size * stat.blocks_available / 1024 / 1024 / 1024
|
221
|
-
end
|
222
|
-
|
223
|
-
def self.attempt_cleanup
|
224
|
-
if not_enough_free_space
|
225
|
-
delete_large_overpass_caches 500
|
226
|
-
end
|
227
|
-
if not_enough_free_space
|
228
|
-
delete_large_overpass_caches 100
|
229
|
-
end
|
230
|
-
if not_enough_free_space
|
231
|
-
delete_large_overpass_caches 50
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
def self.delete_file(filename, reason)
|
236
|
-
open(CartoCSSHelper::Configuration.get_path_to_folder_for_output+'log.txt', 'a') { |file|
|
237
|
-
message = "deleting #{filename}, #{File.size(filename)/1024/1024}MB - #{reason}"
|
238
|
-
puts message
|
239
|
-
file.puts(message)
|
240
|
-
File.delete(filename)
|
241
|
-
}
|
242
|
-
end
|
243
|
-
|
244
|
-
def self.delete_large_overpass_caches(threshold_in_MB)
|
245
|
-
#todo - find library that deals with caches like this, bug here may be unfunny
|
246
|
-
Dir.glob(CartoCSSHelper::Configuration.get_path_to_folder_for_overpass_cache+'*') {|file|
|
247
|
-
if File.size(file) > (1024 * 1024 * threshold_in_MB)
|
248
|
-
delete_file(file, "removing everpass cache entries larger than #{threshold_in_MB} MB to make free space on the disk")
|
249
|
-
end
|
250
|
-
}
|
251
|
-
end
|
252
|
-
|
253
|
-
class OverpassRefusedResponse < IOError; end
|
254
|
-
|
255
|
-
def self.run_overpass_query(query, description, retry_count=0, retry_max=5)
|
256
|
-
start = Time.now.to_s
|
257
|
-
begin
|
258
|
-
url = Downloader.format_query_into_url(query)
|
259
|
-
timeout = Downloader.get_allowed_timeout_in_seconds+10
|
260
|
-
return RestClient::Request.execute(:method => :get, :url => url, :timeout => timeout)
|
261
|
-
rescue RestClient::RequestTimeout
|
262
|
-
puts 'Overpass API refused to process this request. It will be not attemped again, most likely query is too complex.'
|
263
|
-
raise OverpassRefusedResponse
|
264
|
-
rescue RestClient::RequestFailed, RestClient::ServerBrokeConnection => e
|
265
|
-
puts query
|
266
|
-
puts e.response
|
267
|
-
puts e.http_code
|
268
|
-
puts start
|
269
|
-
puts "Rerunning #{description} started at #{Time.now.to_s} (#{retry_count}/#{retry_max}) after #{e}"
|
270
|
-
if retry_count < retry_max
|
271
|
-
sleep 60*5
|
272
|
-
Downloader.run_overpass_query(query, description, retry_count+1, retry_max)
|
273
|
-
else
|
274
|
-
e.raise
|
275
|
-
end
|
276
|
-
rescue ArgumentError => e
|
277
|
-
puts 'ArgumentError from rest-client, most likely caused by https://github.com/rest-client/rest-client/issues/359'
|
278
|
-
puts 'try overpass query that will return smaller amount of data'
|
279
|
-
puts e
|
280
|
-
e.raise
|
281
|
-
rescue => e
|
282
|
-
puts 'query failed'
|
283
|
-
puts query
|
284
|
-
puts
|
285
|
-
puts url
|
286
|
-
puts e
|
287
|
-
e.raise
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def self.get_allowed_timeout_in_seconds
|
292
|
-
return 10 * 60
|
293
|
-
end
|
294
|
-
|
295
|
-
def self.format_query_into_url(query)
|
296
|
-
query = query.gsub(/\n/, '')
|
297
|
-
query = query.gsub(/\t/, '')
|
298
|
-
return 'http://overpass-api.de/api/interpreter?data=' + URI.escape(query)
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
module CartoCSSHelper
|
2
|
-
class TilemillHandler
|
3
|
-
def self.run_tilemill_export_image(lat, lon, zlevel, bbox_size, image_size, export_filename, debug=false)
|
4
|
-
if File.exists?(export_filename)
|
5
|
-
if debug
|
6
|
-
puts 'wanted file exists'
|
7
|
-
end
|
8
|
-
return
|
9
|
-
end
|
10
|
-
silence = '> /dev/null 2>&1'
|
11
|
-
if debug
|
12
|
-
silence = ''
|
13
|
-
end
|
14
|
-
latitude_bb_size = bbox_size[0]
|
15
|
-
longitude_bb_size = bbox_size[0]
|
16
|
-
#--bbox=[xmin,ymin,xmax,ymax]
|
17
|
-
xmin = lon-longitude_bb_size/2
|
18
|
-
ymin = lat-latitude_bb_size/2
|
19
|
-
xmax = lon+longitude_bb_size/2
|
20
|
-
ymax = lat+latitude_bb_size/2
|
21
|
-
bbox = "#{xmin},#{ymin},#{xmax},#{ymax}"
|
22
|
-
params = "--format=png --width=#{image_size} --height=#{image_size} --static_zoom=#{zlevel} --bbox=\"#{bbox}\""
|
23
|
-
project_name = CartoCSSHelper::Configuration.get_tilemill_project_name
|
24
|
-
command = "node /usr/share/tilemill/index.js export #{project_name} '#{export_filename}' #{params} #{silence}"
|
25
|
-
if debug
|
26
|
-
puts command
|
27
|
-
end
|
28
|
-
system command
|
29
|
-
unless File.exists?(export_filename)
|
30
|
-
if !debug
|
31
|
-
puts 'rerunning failed image generation with enabled debug'
|
32
|
-
return self.run_tilemill_export_image(lat, lon, zlevel, bbox_size, image_size, export_filename, true)
|
33
|
-
end
|
34
|
-
raise 'generation of file ' + export_filename + ' failed'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|