require "geojson2image/version" require "oj" require "mini_magick" module Geojson2image class Convert attr_accessor :parsed_json, :width, :height, :background_color, :border_color, :border_width, :padding, :output, :min_xy, :max_xy, :coordinates, :width_padding, :height_padding, :global_ratio def initialize(json: nil, width: nil, height: nil, padding: nil, background_color: nil, fill_color: nil, stroke_color: nil, stroke_width: nil, output: nil) begin @parsed_json = Oj.load(json) @width = width || 500 @height = height || 500 @padding = padding || 50 @background_color = background_color || 'white' @fill_color = fill_color || 'white' @stroke_color = stroke_color || 'black' @stroke_width = stroke_width || 3 @output = output || "output.jpg" @min_xy = [-1, -1] @max_xy = [-1, -1] @coordinates = [] @width_padding = 0 @height_padding = 0 @global_ratio = 0 rescue Oj::ParseError puts "GeoJSON parse error" end end def get_points(json) case json['type'] when 'GeometryCollection' json['geometries'].each do |geometry| get_points(geometry) end when 'FeatureCollection' json['features'].each do |feature| get_points(feature) end when 'Feature' get_points(json['geometry']) when 'Point' @coordinates << [json['coordinates'][0], json['coordinates'][1]] when 'MultiPoint' json['coordinates'].each do |point| @coordinates << [point[0], point[1]] end when 'LineString' json['coordinates'].each do |point| @coordinates << [point[0], point[1]] end when 'MultiLineString' json['coordinates'].each do |linestrings| linestrings.each do |point| @coordinates << [point[0], point[1]] end end when 'Polygon' json['coordinates'].each do |linestrings| linestrings.each do |point| @coordinates << [point[0], point[1]] end end when 'MultiPolygon' json['coordinates'].each do |polygons| polygons.each do |linestrings| linestrings.each do |point| @coordinates << [point[0], point[1]] end end end else puts "get_boundary invalid GeoJSON parse error" end end def get_boundary quarter_pi = Math::PI / 4.0 @coordinates.each_with_index do |point,i| lon = @coordinates[i][0].to_f * Math::PI / 180 lat = @coordinates[i][1].to_f * Math::PI / 180 @coordinates[i][0] = lon @coordinates[i][1] = Math.log(Math.tan(quarter_pi + 0.5 * lat)) @min_xy[0] = (min_xy[0] == -1 ? @coordinates[i][0] : [min_xy[0], @coordinates[i][0]].min) @min_xy[1] = (min_xy[1] == -1 ? @coordinates[i][1] : [min_xy[1], @coordinates[i][1]].min) end @coordinates.each_with_index do |point,i| @coordinates[i][0] = @coordinates[i][0] - @min_xy[0] @coordinates[i][1] = @coordinates[i][1] - @min_xy[1] @max_xy[0] = (max_xy[0] == -1 ? @coordinates[i][0] : [max_xy[0], @coordinates[i][0]].max) @max_xy[1] = (max_xy[1] == -1 ? @coordinates[i][1] : [max_xy[1], @coordinates[i][1]].max) end end def transform_point(point) quarter_pi = Math::PI / 4.0 lon = point[0] * Math::PI / 180 lat = point[1] * Math::PI / 180 xy = [] xy[0] = lon - @min_xy[0] val = Math.log(Math.tan(quarter_pi + 0.5 * lat)) xy[1] = val - @min_xy[1] xy[0] = (@width_padding + (xy[0] * @global_ratio)).to_i xy[1] = (@height - @height_padding - (xy[1] * @global_ratio)).to_i return xy end def draw(json) case json['type'] when 'GeometryCollection' json['geometries'].each do |geometry| draw(geometry) end when 'FeatureCollection' return_boundary = nil json['features'].each do |feature| draw(feature) end when 'Feature' draw(json['geometry']) when 'Point' point = json['coordinates'] new_point = transform_point(point) draw_point = "color #{new_point[0]},#{new_point[1]} point" @convert.draw(draw_point) when 'MultiPoint' json['coordinates'].each do |coordinate| point = { "type" => "Point", "coordinates" => coordinate } draw(point) end when 'LineString' last_point = null json['coordinates'].each do |point| new_point = transform_point(point) if !last_point.nil? polyline = "polyline #{last_point[0]},#{last_point[1]}, #{new_point[0]},#{new_point[1]}" @convert.draw(polyline) end last_point = new_point end when 'MultiLineString' json['coordinates'].each do |coordinate| linestring = { "type" => "LineString", "coordinates" => coordinate } draw(linestring) end when 'Polygon' json['coordinates'].each do |linestrings| border_points = [] if linestrings[0] != linestrings[linestrings.count - 1] linestrings << linestrings[0] end linestrings.each do |point| new_point = transform_point(point) border_points << "#{new_point[0]},#{new_point[1]}" end border = "polygon " + border_points.join(", ") @convert.draw(border) end when 'MultiPolygon' json['coordinates'].each do |polygon| poly = { "type" => "Polygon", "coordinates" => polygon } draw(poly) end else puts "draw invalid GeoJSON parse error - #{json['type']}" end end def to_image @convert = MiniMagick::Tool::Convert.new @convert.size("#{@width}x#{@height}") @convert.xc(@background_color) @convert.fill(@fill_color) @convert.stroke(@stroke_color) @convert.strokewidth(@stroke_width) get_points(@parsed_json) get_boundary padding_both = @padding * 2 map_width = @width - padding_both map_height = @height - padding_both map_width_ratio = map_width / @max_xy[0] map_height_ratio = map_height / @max_xy[1] @global_ratio = [map_width_ratio, map_height_ratio].min @width_padding = (@width - (@global_ratio * @max_xy[0])) / 2 @height_padding = (@height - (@global_ratio * @max_xy[1])) / 2 draw(@parsed_json) @convert << @output cmd_string = @convert.call end end end