Skip to content

Commit

Permalink
Merge pull request #2 from brycejohnston/alt-boundary
Browse files Browse the repository at this point in the history
Fix boundary detection and transform point
  • Loading branch information
brycejohnston authored Jan 28, 2017
2 parents 802a77f + f5be0d9 commit 156c2c6
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 101 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# geojson2image

Ruby library for generating images from GeoJSON.
This gem currently is in alpha / pre-release level status (not fully functional).
This gem is currently alpha / pre-release stage.

## Installation

You will need ImageMagick installed. Then Add this line to your application's Gemfile:
You will need [ImageMagick](http://imagemagick.org/) installed. Then Add this line to your application's Gemfile:

```ruby
gem 'geojson2image'
Expand All @@ -28,6 +28,7 @@ g2i = Geojson2image::Convert.new(
json: gjson,
width: 500,
height: 500,
padding: 50,
background_color: 'white',
fill_color: 'rgba(0, 158, 40, 0.3)',
stroke_color: 'rgb(0, 107, 27)',
Expand Down
182 changes: 83 additions & 99 deletions lib/geojson2image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,168 +4,149 @@

module Geojson2image
class Convert
attr_accessor :parsed_json, :img_width, :img_height, :width, :height,
:background_color, :border_color, :border_width, :output, :canvas
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, background_color: nil,
fill_color: nil, stroke_color: nil, stroke_width: nil, output: nil)
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)
@img_width = width || 500
@img_height = height || 500
@width = (@img_width * 0.9).to_i
@height = (@img_height * 0.9).to_i
@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 compute_boundary(boundary, boundary2)
if boundary.nil?
return boundary2
else
return [
[boundary[0], boundary2[0]].min,
[boundary[1], boundary2[1]].max,
[boundary[2], boundary2[2]].min,
[boundary[3], boundary2[3]].max
]
end
end

def get_boundary(json)
def get_points(json)
case json['type']
when 'GeometryCollection'
return_boundary = nil
json['geometries'].each do |geometry|
return_boundary = compute_boundary(return_boundary, get_boundary(geometry))
get_points(geometry)
end
return return_boundary

when 'FeatureCollection'
return_boundary = nil
json['features'].each do |feature|
return_boundary = compute_boundary(return_boundary, get_boundary(feature))
get_points(feature)
end
return return_boundary

when 'Feature'
return get_boundary(json['geometry'])
get_points(json['geometry'])

when 'Point'
return [
json['coordinates'][0],
json['coordinates'][0],
json['coordinates'][1],
json['coordinates'][1]
]
@coordinates << [json['coordinates'][0], json['coordinates'][1]]

when 'MultiPoint'
return_boundary = nil
json['coordinates'].each do |point|
return_boundary = compute_boundary(return_boundary, [point[0], point[0], point[1], point[1]])
@coordinates << [point[0], point[1]]
end
return return_boundary

when 'LineString'
return_boundary = nil
json['coordinates'].each do |point|
return_boundary = compute_boundary(return_boundary, [point[0], point[0], point[1], point[1]])
@coordinates << [point[0], point[1]]
end
return return_boundary

when 'MultiLineString'
return_boundary = nil
json['coordinates'].each do |linestrings|
linestrings.each do |point|
return_boundary = compute_boundary(return_boundary, [point[0], point[0], point[1], point[1]])
@coordinates << [point[0], point[1]]
end
end
return return_boundary

when 'Polygon'
return_boundary = nil
json['coordinates'].each do |linestrings|
linestrings.each do |point|
return_boundary = compute_boundary(return_boundary, [point[0], point[0], point[1], point[1]])
@coordinates << [point[0], point[1]]
end
end
return return_boundary

when 'MultiPolygon'
return_boundary = nil
json['coordinates'].each do |polygons|
polygons.each do |linestrings|
linestrings.each do |point|
return_boundary = compute_boundary(return_boundary, [point[0], point[0], point[1], point[1]])
@coordinates << [point[0], point[1]]
end
end
end
return return_boundary

else
puts "get_boundary invalid GeoJSON parse error"
end
end

def pixel_x(x)
(x.to_f + 180) / 360
end
def get_boundary
quarter_pi = Math::PI / 4.0

def pixel_y(y)
sin_y = Math.sin(y.to_f * Math::PI / 180)
return (0.5 - Math.log((1 + sin_y) / (1 - sin_y)) / (4 * Math::PI))
end
@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

def adjust_point(point)
point += 180
point = (point > 360 ? point - 360 : point)
end
@coordinates[i][0] = lon
@coordinates[i][1] = Math.log(Math.tan(quarter_pi + 0.5 * lat))

def transform_point(point, boundary)
if point[0] == 180 || point[0] == -180
return false
@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

x_delta = pixel_x(boundary[1]) - pixel_x(boundary[0])
y_delta = pixel_y(boundary[3]) - pixel_y(boundary[2])

new_point = []
new_point[0] = ((pixel_x(adjust_point(point[0]) + boundary[4]) - pixel_x(adjust_point(boundary[0]) + boundary[4])) * width / x_delta).floor + (@width * 0.05)
new_point[1] = ((pixel_y(boundary[3]) - pixel_y(point[1])) * height / y_delta).floor + (@height * 0.05)
@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]

return new_point
@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 draw(json, boundary)
x_delta = boundary[1] - boundary[0]
y_delta = boundary[3] - boundary[2]
max_delta = [x_delta, y_delta].max
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, boundary)
draw(geometry)
end

when 'FeatureCollection'
return_boundary = nil
json['features'].each do |feature|
draw(feature, boundary)
draw(feature)
end

when 'Feature'
draw(json['geometry'], boundary, json['properties'])
draw(json['geometry'])

when 'Point'
point_size = 10
point = json['coordinates']
new_point = transform_point(point, boundary)
new_point = transform_point(point)
draw_point = "color #{new_point[0]},#{new_point[1]} point"
@convert.draw(draw_point)

Expand All @@ -175,14 +156,14 @@ def draw(json, boundary)
"type" => "Point",
"coordinates" => coordinate
}
draw(point, boundary)
draw(point)
end

when 'LineString'
last_point = null

json['coordinates'].each do |point|
new_point = transform_point(point, boundary)
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)
Expand All @@ -196,7 +177,7 @@ def draw(json, boundary)
"type" => "LineString",
"coordinates" => coordinate
}
draw(linestring, boundary)
draw(linestring)
end

when 'Polygon'
Expand All @@ -207,8 +188,8 @@ def draw(json, boundary)
end

linestrings.each do |point|
new_point = transform_point(point, boundary)
border_points << "#{new_point[0].floor},#{new_point[1].floor}"
new_point = transform_point(point)
border_points << "#{new_point[0]},#{new_point[1]}"
end

border = "polygon " + border_points.join(", ")
Expand All @@ -221,7 +202,7 @@ def draw(json, boundary)
"type" => "Polygon",
"coordinates" => polygon
}
draw(poly, boundary)
draw(poly)
end

else
Expand All @@ -231,28 +212,31 @@ def draw(json, boundary)

def to_image
@convert = MiniMagick::Tool::Convert.new
@convert.size("#{@img_width}x#{@img_height}")
@convert.size("#{@width}x#{@height}")
@convert.xc(@background_color)
@convert.fill(@fill_color)
@convert.stroke(@stroke_color)
@convert.strokewidth(@stroke_width)

boundary = get_boundary(@parsed_json)
boundary[4] = 0
get_points(@parsed_json)
get_boundary

if boundary[1] > boundary[0]
draw(@parsed_json, boundary)
else
boundary[1] += 360
draw(@parsed_json, boundary)
padding_both = @padding * 2

boundary[1] -= 360
boundary[0] -= 360
draw(@parsed_json, boundary)
end
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
@convert.call
cmd_string = @convert.call
end

end
Expand Down

0 comments on commit 156c2c6

Please sign in to comment.