lib/cartocss_helper/visualise_changes_image_generation.rb
# frozen_string_literal: true
require_relative 'visualise_changes_diff_from_images'
require_relative 'git'
require_relative 'renderer_handler'
require_relative 'overpass_query_generator'
require_relative 'util/filehelper'
module CartoCSSHelper
class VisualDiff
@@job_pooling = false
@@jobs = []
def self.enable_job_pooling
# it results in avoiding loading the same database mutiple times
# useful if the same database will be used multiple times (for example the same place in multiple comparisons)
# use run_jobs function to run jobs
@@job_pooling = true
end
def self.disable_job_pooling
@@job_pooling = false
end
class MapGenerationJob
attr_reader :filename
def initialize(filename, latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size, image_size)
@latitude = latitude
@longitude = longitude
@zlevels = zlevels
@header = header
@old_branch = old_branch
@new_branch = new_branch
@image_size = image_size
@filename = filename
@data_source = CartoCSSHelper::VisualDiff::FileDataSource.new(@latitude, @longitude, download_bbox_size, @filename)
end
def run_job
CartoCSSHelper::VisualDiff.visualise_for_given_source(@latitude, @longitude, @zlevels, @header, @new_branch, @old_branch, @image_size, @data_source)
end
def print
puts "#{@filename.gsub(Configuration.get_path_to_folder_for_cache, '#')} [#{@latitude};#{@longitude}], z: #{@zlevels}, text: #{@header}, '#{@old_branch}'->'#{@new_branch}', bbox:#{@download_bbox_size}, #{@image_size}px"
end
end
def self.add_job(filename, latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size, image_size, prefix)
print prefix
new_job = MapGenerationJob.new(filename, latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size, image_size)
new_job.print
raise "#{filename} does not exists" unless File.exist?(filename)
raise "#{latitude} is not a number" unless latitude.is_a? Numeric
raise "#{longitude} is not a number" unless longitude.is_a? Numeric
raise "#{zlevels} is not a range" unless zlevels.class == Range
raise "#{header} is not a string" unless header.class == String
raise "#{new_branch} is not a string" unless new_branch.class == String
raise "#{old_branch} is not a string" unless old_branch.class == String
raise "#{download_bbox_size} is not a number" unless download_bbox_size.is_a? Numeric
raise "#{image_size} is not a integer" unless image_size.is_a? Integer
@@jobs.push(new_job)
end
def self.run_jobs
new_job_array = []
return if @@jobs == []
@@jobs[0].run_job
for x in 1..@@jobs.length - 1
if @@jobs[0].filename == @@jobs[x].filename
# requires loading the same file as just run job
# it may be safely run without reloading database
@@jobs[x].run_job
else
new_job_array << @@jobs[x].filename
end
end
@@jobs = new_job_array
end
def self.shuffle_jobs(seed)
@@jobs.shuffle!(random: Random.new(seed))
end
def self.make_header(tags, type, on_water)
on_water_string = ''
on_water_string = ' on water' if on_water
return "#{VisualDiff.tag_dict_to_string(tags)} #{type}#{on_water_string}"
end
def self.visualise_on_synthethic_data(tags, type, on_water, zlevel_range, new_branch, old_branch)
header = make_header(tags, type, on_water)
puts "visualise_on_synthethic_data <#{header}> #{old_branch} -> #{new_branch}"
Git.checkout(old_branch)
old = VisualDiff.collect_images_for_synthethic_test(tags, type, on_water, zlevel_range)
Git.checkout(new_branch)
new = VisualDiff.collect_images_for_synthethic_test(tags, type, on_water, zlevel_range)
VisualDiff.pack_image_sets old, new, header, new_branch, old_branch, 200
end
def self.collect_images_for_synthethic_test(tags, type, on_water, zlevel_range)
collection = []
zlevel_range.each do |zlevel|
scene = Scene.new(tags, zlevel, on_water, type)
collection.push(ImageForComparison.new(scene.get_image_filename, "z#{zlevel}"))
end
return collection
end
class FileDataSource
attr_reader :download_bbox_size, :data_filename
def initialize(latitude, longitude, download_bbox_size, filename)
@download_bbox_size = download_bbox_size
@latitude = latitude
@longitude = longitude
@data_filename = filename
@loaded = false
end
def load
unless @loaded
DataFileLoader.load_data_into_database(@data_filename)
puts "\tgenerating images"
@loaded = true
end
end
def get_timestamp
return GenericCachedDownloader.new.get_cache_timestamp(@data_filename)
end
end
def self.visualise_on_overpass_data(tags, type, wanted_latitude, wanted_longitude, zlevels, new_branch, old_branch = 'master')
# special support for some tag values - see CartoCSSHelper::OverpassQueryGenerator.turn_list_of_tags_in_overpass_filter for details
header_prefix = "#{VisualDiff.tag_dict_to_string(tags)} #{type} [#{wanted_latitude}, #{wanted_longitude}] -> "
target_location = '[?, ?]'
header_sufix = " #{old_branch}->#{new_branch} #{zlevels}"
puts "visualise_on_overpass_data <#{header_prefix}#{header_sufix}> #{old_branch} -> #{new_branch}"
begin
latitude, longitude = OverpassQueryGenerator.locate_element_with_given_tags_and_type tags, type, wanted_latitude, wanted_longitude
target_location = "[#{latitude}, #{longitude}]"
rescue OverpassQueryGenerator::NoLocationFound, OverpassDownloader::OverpassRefusedResponse
puts 'No nearby instances of tags and tag is not extremely rare - no generation of nearby location and wordwide search was impossible. No diff image will be generated for this location.'
return false
end
visualise_for_location(latitude, longitude, zlevels, header_prefix + target_location + header_sufix, new_branch, old_branch)
return true
end
def self.visualise_for_location(latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size = 0.4, image_size = 400)
filename = OverpassQueryGenerator.get_file_with_downloaded_osm_data_for_location(latitude, longitude, download_bbox_size)
visualise_for_location_from_file(filename, latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size, image_size)
end
def self.visualise_for_location_from_file(filename, latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size = 0.4, image_size = 400)
prefix = ''
prefix = 'pool <- ' if @@job_pooling
add_job(filename, latitude, longitude, zlevels, header, new_branch, old_branch, download_bbox_size, image_size, prefix)
run_jobs unless @@job_pooling
end
def self.visualise_for_given_source(latitude, longitude, zlevels, header, new_branch, old_branch, image_size, source)
Git.checkout old_branch
old = VisualDiff.collect_images_for_real_data_test(latitude, longitude, zlevels, source, image_size)
Git.checkout new_branch
new = VisualDiff.collect_images_for_real_data_test(latitude, longitude, zlevels, source, image_size)
VisualDiff.pack_image_sets old, new, header, new_branch, old_branch, image_size
end
def self.get_render_bbox_size(zlevel, wanted_image_size, latitude)
longitude_equator_rendered_length_in_pixels = 256 * 2**zlevel
longitude_size = 360 * wanted_image_size.to_f / longitude_equator_rendered_length_in_pixels
latitude_size = longitude_size * Math.cos(latitude * Math::PI / 180)
return [latitude_size, longitude_size]
end
def self.collect_images_for_real_data_test(latitude, longitude, zlevels, source, image_size = 400)
collection = []
zlevels.each do |zlevel|
render_bbox_size = VisualDiff.get_render_bbox_size(zlevel, image_size, latitude)
filename = "#{latitude} #{longitude} #{zlevel}zlevel #{image_size}px #{source.get_timestamp} #{source.download_bbox_size}.png"
unless RendererHandler.image_available_from_cache(latitude, longitude, zlevel, render_bbox_size, image_size, filename)
source.load
end
file_location = RendererHandler.request_image_from_renderer(latitude, longitude, zlevel, render_bbox_size, image_size, filename)
collection.push(ImageForComparison.new(file_location, "z#{zlevel}"))
end
return collection
end
def self.pack_image_sets(old, new, header, new_branch, old_branch, image_size)
old_branch = FileHelper.make_string_usable_as_filename(old_branch)
new_branch = FileHelper.make_string_usable_as_filename(new_branch)
header_for_filename = FileHelper.make_string_usable_as_filename(header)
filename_sufix = "#{old_branch} -> #{new_branch}"
filename = CartoCSSHelper::Configuration.get_path_to_folder_for_output + "#{header_for_filename} #{filename_sufix} #{image_size}px #{RendererHandler.renderer_marking}.png"
diff = FullSetOfComparedImages.new(old, new, header, filename, image_size)
diff.save
end
def self.tag_dict_to_string(dict)
return OverpassQueryGenerator.turn_list_of_tags_in_overpass_filter(dict)
end
end
end