require 'concurrent' require 'ferrum' require 'fileutils' require 'pathname' require 'uri' require 'webrick' require 'webrick/https' module Natour class MapGeoAdmin def initialize(port: 0) @doc_root = Pathname(Dir.mktmpdir) FileUtils.cp_r("#{__dir__}/data/js", @doc_root) event = Concurrent::Event.new @server = StdoutUtils.suppress_output do WEBrick::HTTPServer.new( StartCallback: -> { event.set }, Logger: WEBrick::Log.new(File.open(File::NULL, 'w')), AccessLog: [], DocumentRoot: @doc_root, BindAddress: 'localhost', Port: port, SSLEnable: true, SSLCertName: [%w[CN localhost]] ) end @server.mount('/map', MapServlet) @thread = Thread.new { @server.start } event.wait @browser = Ferrum::Browser.new( slowmo: 2.0, timout: 30, window_size: [5000, 5000], browser_options: { 'no-sandbox': nil, 'ignore-certificate-errors': nil } ) end def close @browser.quit @server.shutdown @thread.join FileUtils.remove_entry(@doc_root) end def save_image(filename, overwrite: false, gps_files: [], gps_colors: [], map_layers: [], image_size: [1200, 900]) FileUtils.cp(gps_files, @doc_root) uri = URI("https://#{@server[:BindAddress]}:#{@server[:Port]}/map") uri.query = URI.encode_www_form( 'gps-files': gps_files.map { |gps_file| Pathname(gps_file).basename }.join(','), 'gps-colors': gps_colors.join(','), 'map-layers': map_layers.join(','), 'map-size': image_size.map { |dim| dim.is_a?(String) ? dim : "#{dim}px" }.join(',') ) @browser.goto(uri) tmp_filename = @doc_root.join(Pathname(filename).basename) @browser.screenshot( path: tmp_filename, quality: 100, selector: '.map' ) FileUtils.mkdir_p(Pathname(filename).dirname) mode = File::WRONLY | File::CREAT | File::TRUNC | File::BINARY mode |= File::EXCL unless overwrite File.open(filename, mode) do |file| file.write(File.read(tmp_filename, mode: 'rb')) end end def self.open(*args) map = MapGeoAdmin.new(*args) return map unless block_given? yield(map) ensure map&.close end class MapServlet < WEBrick::HTTPServlet::AbstractServlet def do_GET(request, response) # rubocop:disable Naming/MethodName raise WEBrick::HTTPStatus::NotFound unless request.path == '/map' files = request.query.fetch('gps-files', '').split(',') colors = request.query.fetch('gps-colors', '').split(',') (files.size - colors.size).times { colors << (colors.last || 'blueviolet') } layers = ['ch.swisstopo.pixelkarte-farbe'] layers |= request.query.fetch('map-layers', '').split(',') width, height = request.query.fetch('map-size', '').split(',') raise WEBrick::HTTPStatus::BadRequest unless width && height doc = [] doc << '
' doc << '' doc << '' doc << '' doc << '' doc << '' doc << '' doc << '' response.body = doc.join("\n") response['Content-Type'] = 'text/html' end end end end