Module: KmlPolygon

Extended by:
KmlPolygon
Includes:
Math
Included in:
KmlPolygon
Defined in:
lib/kml_polygon.rb,
lib/kml_polygon/version.rb

Overview

require ‘kml_polygon/version’

Constant Summary collapse

DEGREES =

constant to convert to degrees

180.0 / PI
RADIANS =

constant to convert to radians

PI / 180.0
EARTH_MEAN_RADIUS =

Mean Radius of Earth, radius

6378.1 * 1000.0
VERSION =
"2.0.2"

Instance Method Summary collapse

Instance Method Details

#kml_regular_polygon(longitude, latitude, radius, segments = 20, rotate = 0) ⇒ Object

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.



153
154
155
# 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) ⇒ Object

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.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# 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) ⇒ Object

Output points formatted as a KML string

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



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# 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) ⇒ Object

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



109
110
111
112
113
114
115
116
117
118
119
120
# 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) ⇒ Object

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



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 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) ⇒ Object

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



68
69
70
71
72
73
74
75
# 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) ⇒ Object

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

p is vector of three elements



56
57
58
59
60
61
62
63
# 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