module KmlPolygon

require 'kml_polygon/version'

Constants

DEGREES

constant to convert to degrees

EARTH_MEAN_RADIUS

Mean Radius of Earth, radius

RADIANS

constant to convert to radians

VERSION

Public Instance Methods

kml_regular_polygon(longitude, latitude, radius, segments=20, rotate=0) click to toggle source

kml_regular_polygon - Regular polygon

(longitude, latitude) - center point in decimal degrees
radius                - radius in meters
segments              - number of sides, > 20 looks like a circle (optional, default: 20)
rotate                - rotate polygon by number of degrees (optional, default: 0)

Returns a string suitable for adding into a KML file.

# File lib/kml_polygon.rb, line 153
def kml_regular_polygon(longitude, latitude, radius, segments=20, rotate=0)
  points_to_kml(spoints(longitude, latitude, radius, segments, rotate))
end
kml_star(longitude, latitude, radius, inner_radius, segments=10, rotate=0) click to toggle source

kml_star - Make a “star” or “burst” pattern

(longitude, latitude) - center point in decimal degrees
radius                - radius in meters
innner_radius         - radius in meters, typically < outer_radius
segments              - number of "points" on the star (optional, default: 10)
rotate                - rotate polygon by number of degrees (optional, default: 0)

Returns a string suitable for adding into a KML file.

# File lib/kml_polygon.rb, line 168
def kml_star(longitude, latitude, radius, inner_radius, segments=10, rotate=0)
  outer_points = spoints(longitude, latitude, radius, segments, rotate)
  inner_points = spoints(longitude, latitude, inner_radius, segments, rotate + 180.0 / segments)

  # interweave the radius and inner_radius points
  # I'm sure there is a better way
  points = []
  for point in 0...outer_points.length
    points << outer_points[point]
    points << inner_points[point]
  end

  # MTB - Drop the last overlapping point leaving start and end points connecting
  # (resulting output differs from orig, but is more correct)
  points.pop

  points_to_kml(points)
end
points_to_kml(points) click to toggle source

Output points formatted as a KML string

You may want to edit this function to change “extrude” and other XML nodes.

# File lib/kml_polygon.rb, line 127
def points_to_kml(points)

  kml_string = "<Polygon>\n"
  kml_string << "  <outerBoundaryIs><LinearRing><coordinates>\n"

  points.each do |point|
    kml_string << "    " << point[0].to_s << "," << point[1].to_s << "\n"
  end

  kml_string << "  </coordinates></LinearRing></outerBoundaryIs>\n"
  kml_string << "</Polygon>\n"

  # kml_string << "  <extrude>1</extrude>\sides"
  # kml_string << "  <altitudeMode>clampToGround</altitudeMode>\sides"
end
rotate_point(vector, point, phi) click to toggle source

rotate point around unit vector by phi radians blog.modp.com/2007/09/rotating-point-around-vector.html

# File lib/kml_polygon.rb, line 109
def rotate_point(vector, point, phi)
  # remap vector for sanity
  u, v, w, x, y, z = vector[0], vector[1], vector[2], point[0], point[1], point[2]

  a = u*x + v*y + w*z
  d = Math.cos(phi)
  e = Math.sin(phi)

  [(a*u + (x - a*u)*d + (v*z - w*y) * e),
   (a*v + (y - a*v)*d + (w*x - u*z) * e),
   (a*w + (z - a*w)*d + (u*y - v*x) * e)]
end
spoints(longitude, latitude, radius, sides, rotate=0) click to toggle source

spoints – get raw list of points in longitude,latitude format

radius: radius of polygon in meters sides: number of sides rotate: rotate polygon by number of degrees

Returns a list of points comprising the object

# File lib/kml_polygon.rb, line 85
def spoints(longitude, latitude, radius, sides, rotate=0)

  rotate_radians = rotate * RADIANS

  # compute longitude degrees (in radians) at given latitude
  r = radius / (EARTH_MEAN_RADIUS * Math.cos(latitude * RADIANS))

  vector = to_cart(longitude * RADIANS, latitude * RADIANS)
  point = to_cart(longitude * RADIANS + r, latitude * RADIANS)
  points = []

  for side in 0...sides
    points << to_earth(rotate_point(vector, point, rotate_radians + (2.0 * PI/sides)*side))
  end

  # Connect to starting point exactly
  # Not sure if required, but seems to help when the polygon is not filled
  points << points[0]
end
to_cart(longitude, latitude) click to toggle source

convert longitude, latitude IN RADIANS to (x,y,z)

# File lib/kml_polygon.rb, line 68
def to_cart(longitude, latitude)
  theta = longitude
  # spherical coordinate use "co-latitude", not "latitude"
  # lattiude = [-90, 90] with 0 at equator
  # co-latitude = [0, 180] with 0 at north pole
  phi = PI / 2.0 - latitude
  [Math.cos(theta) * Math.sin(phi), Math.sin(theta) * Math.sin(phi), Math.cos(phi)]
end
to_earth(p) click to toggle source

Convert (x,y,z) on unit sphere back to (longitude, latitude)

p is vector of three elements

# File lib/kml_polygon.rb, line 56
def to_earth(p)
  p[0] == 0.0 ? longitude = PI / 2.0 :longitude = Math.atan(p[1]/p[0])
  latitude = PI / 2.0 - Math.acos(p[2])

  # select correct branch of arctan
  (p[1] <= 0.0 ? longitude = -(PI - longitude) : longitude = PI + longitude) if p[0] < 0.0
  [longitude * DEGREES, latitude * DEGREES]
end