ruby_kml 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +7 -0
- data/README.md +34 -0
- data/Rakefile +52 -0
- data/examples/melbourne-stations.kml +18 -0
- data/lib/kml/color_style.rb +34 -0
- data/lib/kml/container.rb +42 -0
- data/lib/kml/document.rb +46 -0
- data/lib/kml/feature.rb +185 -0
- data/lib/kml/folder.rb +29 -0
- data/lib/kml/geometry.rb +102 -0
- data/lib/kml/ground_overlay.rb +18 -0
- data/lib/kml/hot_spot.rb +31 -0
- data/lib/kml/icon.rb +15 -0
- data/lib/kml/icon_style.rb +52 -0
- data/lib/kml/lat_lon_box.rb +36 -0
- data/lib/kml/line_string.rb +40 -0
- data/lib/kml/line_style.rb +15 -0
- data/lib/kml/linear_ring.rb +67 -0
- data/lib/kml/link.rb +51 -0
- data/lib/kml/look_at.rb +28 -0
- data/lib/kml/model.rb +40 -0
- data/lib/kml/multi_geometry.rb +26 -0
- data/lib/kml/object.rb +37 -0
- data/lib/kml/overlay.rb +21 -0
- data/lib/kml/placemark.rb +60 -0
- data/lib/kml/point.rb +65 -0
- data/lib/kml/poly_style.rb +16 -0
- data/lib/kml/polygon.rb +91 -0
- data/lib/kml/screen_overlay.rb +61 -0
- data/lib/kml/snippet.rb +25 -0
- data/lib/kml/style.rb +23 -0
- data/lib/kml/style_map.rb +39 -0
- data/lib/kml/style_selector.rb +7 -0
- data/lib/kml/version.rb +9 -0
- data/lib/kml_file.rb +55 -0
- data/lib/ruby_kml.rb +8 -0
- data/ruby_kml.gemspec +14 -0
- data/test/cdata_and_snippet.kml +21 -0
- data/test/ground_overlays.kml +25 -0
- data/test/kml/point_test.rb +10 -0
- data/test/kml_file_test.rb +255 -0
- data/test/paths.kml +33 -0
- data/test/polygon.kml +20 -0
- data/test/polygon_inner.kml +25 -0
- data/test/polygon_style.kml +24 -0
- data/test/simple_placemark.kml +12 -0
- data/test/style_map.kml +40 -0
- data/test/test_helper.rb +11 -0
- metadata +124 -0
data/lib/kml/model.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module KML
|
2
|
+
class Model < Geometry
|
3
|
+
|
4
|
+
def initialize(params={})
|
5
|
+
@lng, @lat, @alt = *params[:location]
|
6
|
+
@heading, @tilt, @roll = *params[:orientation] || [0, 0, 0]
|
7
|
+
@x, @y, @z = *params[:scale] || [1, 1, 1]
|
8
|
+
@link = params[:link]
|
9
|
+
@id = params[:id] if params[:id]
|
10
|
+
end
|
11
|
+
|
12
|
+
def id
|
13
|
+
@id || "model_#{@link.href}_#{@x}_#{@y}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(xm = Builder::XmlMarkup.new(:indent => 2))
|
17
|
+
xm.Model :id => id do
|
18
|
+
xm.altitudeMode(altitude_mode) if altitude_mode_set?
|
19
|
+
xm.Location do
|
20
|
+
xm.longitude @lng
|
21
|
+
xm.latitude @lat
|
22
|
+
xm.altitude @alt
|
23
|
+
end
|
24
|
+
xm.Orientation do
|
25
|
+
xm.heading @heading
|
26
|
+
xm.tilt @tilt
|
27
|
+
xm.roll @roll
|
28
|
+
end
|
29
|
+
xm.Scale do
|
30
|
+
xm.x @x
|
31
|
+
xm.y @y
|
32
|
+
xm.z @z
|
33
|
+
end
|
34
|
+
@link.render(xm)
|
35
|
+
# ResourceMap needs to be implemented still
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module KML
|
2
|
+
class MultiGeometry < Geometry
|
3
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
4
|
+
xm.MultiGeometry { features.each { |f| f.render(xm) } }
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.parse(node)
|
8
|
+
self.new.parse(node)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(node)
|
12
|
+
super(node) do |cld|
|
13
|
+
case cld.name
|
14
|
+
when 'Polygon'
|
15
|
+
self.features << KML::Polygon.parse(cld)
|
16
|
+
else
|
17
|
+
puts "MultiGeometry"
|
18
|
+
p cld
|
19
|
+
puts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/kml/object.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module KML #:nodoc:
|
2
|
+
# Base class for all KML objects
|
3
|
+
class Object
|
4
|
+
# The KML object ID
|
5
|
+
attr_accessor :id
|
6
|
+
|
7
|
+
# Initialize the object, optionally passing a Hash of attributes to set.
|
8
|
+
def initialize(attributes=nil)
|
9
|
+
if attributes
|
10
|
+
case attributes
|
11
|
+
when Hash
|
12
|
+
attributes.each do |name, value|
|
13
|
+
self.__send__("#{name}=".to_sym, value)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Attributes must be specified as a Hash"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse(node)
|
22
|
+
self.id = node['id']
|
23
|
+
node.element_children.each do |cld|
|
24
|
+
yield cld
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'kml/feature'
|
32
|
+
require 'kml/geometry'
|
33
|
+
require 'kml/color_style'
|
34
|
+
require 'kml/style_selector'
|
35
|
+
require 'kml/link'
|
36
|
+
require 'kml/icon'
|
37
|
+
require 'kml/lat_lon_box'
|
data/lib/kml/overlay.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module KML
|
2
|
+
# Overlay is the base type for image overlays drawn on the planet surface or on the screen. +icon+ specifies the
|
3
|
+
# image to use and can be configured to reload images based on a timer or by camera changes. This element also
|
4
|
+
# includes specifications for stacking order of multiple overlays and for adding color and transparency values to
|
5
|
+
# the base image.
|
6
|
+
class Overlay < Feature
|
7
|
+
attr_accessor :color
|
8
|
+
attr_accessor :draw_order
|
9
|
+
attr_accessor :icon
|
10
|
+
|
11
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
12
|
+
super
|
13
|
+
xm.color(color) unless color.nil?
|
14
|
+
xm.drawOrder(drawOrder) unless draw_order.nil?
|
15
|
+
icon.render(xm) unless icon.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'kml/ground_overlay'
|
21
|
+
require 'kml/screen_overlay'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Basic XML Structure:
|
2
|
+
#
|
3
|
+
# <Placemark id="ID">
|
4
|
+
# <!-- inherited from Feature element -->
|
5
|
+
# <name>...</name> <!-- string -->
|
6
|
+
# <visibility>1</visibility> <!-- boolean -->
|
7
|
+
# <open>1</open> <!-- boolean -->
|
8
|
+
# <address>...</address> <!-- string -->
|
9
|
+
# <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">...
|
10
|
+
# </AddressDetails> <!-- string -->
|
11
|
+
# <phoneNumber>...</phoneNumber> <!-- string -->
|
12
|
+
# <Snippet maxLines="2">...</Snippet> <!-- string -->
|
13
|
+
# <description>...</description> <!-- string -->
|
14
|
+
# <LookAt>...</LookAt>
|
15
|
+
# <TimePrimitive>...</TimePrimitive>
|
16
|
+
# <styleUrl>...</styleUrl> <!-- anyURI -->
|
17
|
+
# <StyleSelector>...</StyleSelector>
|
18
|
+
# <Region>...</Region>
|
19
|
+
# <Metadata>...</Metadata>
|
20
|
+
#
|
21
|
+
# <!-- specific to Placemark element -->
|
22
|
+
# <Geometry>...</Geometry>
|
23
|
+
# </Placemark>
|
24
|
+
|
25
|
+
module KML
|
26
|
+
class Placemark < KML::Container
|
27
|
+
attr_accessor :geometry
|
28
|
+
|
29
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
30
|
+
xm.Placemark {
|
31
|
+
super
|
32
|
+
features.each { |f| f.render(xm) }
|
33
|
+
plain_children.each { |c| xm << c }
|
34
|
+
geometry.render(xm) unless geometry.nil?
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse(node)
|
39
|
+
self.new.parse(node)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse(node)
|
43
|
+
super(node) do |cld|
|
44
|
+
case cld.name
|
45
|
+
when 'MultiGeometry'
|
46
|
+
self.geometry = KML::MultiGeometry.parse(cld)
|
47
|
+
when 'Polygon'
|
48
|
+
self.geometry = KML::Polygon.parse(cld)
|
49
|
+
when 'Point'
|
50
|
+
self.geometry = KML::Point.parse(cld)
|
51
|
+
else
|
52
|
+
puts "Placemark"
|
53
|
+
p cld
|
54
|
+
puts
|
55
|
+
end
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/kml/point.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# <Point id="ID">
|
2
|
+
# <!-- specific to Point -->
|
3
|
+
# <extrude>0</extrude> <!-- boolean -->
|
4
|
+
# <tessellate>0</tessellate> <!-- boolean -->
|
5
|
+
# <altitudeMode>clampToGround</altitudeMode>
|
6
|
+
# <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute -->
|
7
|
+
# <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
|
8
|
+
# </Point>
|
9
|
+
module KML
|
10
|
+
class Point < Geometry
|
11
|
+
|
12
|
+
# A single tuple consisting of floating point values for longitude, latitude, and altitude (in that order).
|
13
|
+
# Longitude and latitude values are in degrees, where:
|
14
|
+
#
|
15
|
+
# * longitude >= -180 and <= 180
|
16
|
+
# * latitude >= -90 and <= 90
|
17
|
+
# * altitude values (optional) are in meters above sea level
|
18
|
+
def coordinates
|
19
|
+
@coordinates
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the coordinates
|
23
|
+
def coordinates=(c)
|
24
|
+
case c
|
25
|
+
when String
|
26
|
+
@coordinates = c.strip.split(',')
|
27
|
+
unless @coordinates.length == 2 || @coordinates.length == 3
|
28
|
+
raise "Coordinates string may only have 2 parts (indicating lat and long) or 3 parts (lat, long and altitude)"
|
29
|
+
end
|
30
|
+
when Array
|
31
|
+
@coordinates = c
|
32
|
+
when Hash
|
33
|
+
@coordinates = [:lng, :lat, :alt].collect {|attr| c[attr]}.compact
|
34
|
+
else
|
35
|
+
raise ArgumentError, "Coordinates must be either a String, Hash or an Array"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
40
|
+
xm.Point {
|
41
|
+
super
|
42
|
+
xm.coordinates(coordinates.join(","))
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse(node)
|
47
|
+
self.new.parse(node)
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse(node)
|
51
|
+
super(node) do |cld|
|
52
|
+
case cld.name
|
53
|
+
when 'coordinates'
|
54
|
+
self.coordinates = cld.content
|
55
|
+
else
|
56
|
+
puts "Point"
|
57
|
+
p cld
|
58
|
+
puts
|
59
|
+
end
|
60
|
+
end
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module KML
|
2
|
+
# Specifies the drawing style for all polygons, including polygon extrusions (which look like the walls of buildings)
|
3
|
+
# and line extrusions (which look like solid fences).
|
4
|
+
class PolyStyle < ColorStyle
|
5
|
+
attr_accessor :fill
|
6
|
+
attr_accessor :outline
|
7
|
+
|
8
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
9
|
+
xm.PolyStyle {
|
10
|
+
super
|
11
|
+
xm.fill(fill) unless fill.nil?
|
12
|
+
xm.outline(outline) unless outline.nil?
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/kml/polygon.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Source file which generates a Polygon element.
|
2
|
+
#
|
3
|
+
# <Polygon id="ID">
|
4
|
+
# <!-- specific to Polygon -->
|
5
|
+
# <extrude>0</extrude> <!-- boolean -->
|
6
|
+
# <tessellate>0</tessellate> <!-- boolean -->
|
7
|
+
# <altitudeMode>clampToGround</altitudeMode>
|
8
|
+
# <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute -->
|
9
|
+
# <outerBoundaryIs>
|
10
|
+
# <LinearRing>
|
11
|
+
# <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
|
12
|
+
# </LinearRing>
|
13
|
+
# </outerBoundaryIs>
|
14
|
+
# <innerBoundaryIs>
|
15
|
+
# <LinearRing>
|
16
|
+
# <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
|
17
|
+
# </LinearRing>
|
18
|
+
# </innerBoundaryIs>
|
19
|
+
# <innerBoundaryIs>
|
20
|
+
# ...
|
21
|
+
# </innerBoundaryIs>
|
22
|
+
# </Polygon>
|
23
|
+
|
24
|
+
module KML #:nodoc:
|
25
|
+
# A Polygon is defined by an outer boundary and 0 or more inner boundaries. The boundaries, in turn, are defined
|
26
|
+
# by LinearRings. When a Polygon is extruded, its boundaries are connected to the ground to form additional polygons,
|
27
|
+
# which gives the appearance of a building. When a Polygon is extruded, each point is extruded individually.
|
28
|
+
# Extruded Polygons use PolyStyle for their color, color mode, and fill.
|
29
|
+
class Polygon < Geometry
|
30
|
+
attr_accessor :outer_boundary_is
|
31
|
+
|
32
|
+
def inner_boundary_is
|
33
|
+
@inner_boundary_is ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
# allow old semantics for adding inner boundaries
|
37
|
+
def inner_boundary_is=(ib)
|
38
|
+
if ib.kind_of?(Array)
|
39
|
+
@inner_boundary_is = ib
|
40
|
+
else
|
41
|
+
self.inner_boundary_is << ib
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
46
|
+
xm.Polygon {
|
47
|
+
super
|
48
|
+
xm.outerBoundaryIs {
|
49
|
+
outer_boundary_is.render(xm)
|
50
|
+
}
|
51
|
+
inner_boundary_is.each do |ib|
|
52
|
+
xm.innerBoundaryIs {
|
53
|
+
ib.render(xm)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.parse(node)
|
60
|
+
self.new.parse(node)
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse(node)
|
64
|
+
super(node) do |cld|
|
65
|
+
case cld.name
|
66
|
+
when 'outerBoundaryIs'
|
67
|
+
self.outer_boundary_is = self.parse_boundary(cld)
|
68
|
+
when 'innerBoundaryIs'
|
69
|
+
self.inner_boundary_is << self.parse_boundary(cld)
|
70
|
+
else
|
71
|
+
puts "Polygon"
|
72
|
+
p cld
|
73
|
+
puts
|
74
|
+
end
|
75
|
+
end
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# should only contain a LinearRing
|
80
|
+
def parse_boundary(node)
|
81
|
+
ring = nil
|
82
|
+
node.element_children.each do |cld|
|
83
|
+
case cld.name
|
84
|
+
when 'LinearRing'
|
85
|
+
ring = KML::LinearRing.parse(cld)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
ring
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# This file implements the ScreenOverlay type
|
2
|
+
# <ScreenOverlay id="ID">
|
3
|
+
# <!-- inherited from Feature element -->
|
4
|
+
# <!-- inherited from Overlay element -->
|
5
|
+
#
|
6
|
+
# <!-- specific to ScreenOverlay -->
|
7
|
+
# <overlayXY x="double" y="double" xunits="fraction" yunits="fraction"/>
|
8
|
+
# <!-- vec2 -->
|
9
|
+
# <!-- xunits and yunits can be one of: fraction, pixels, or insetPixels -->
|
10
|
+
# <screenXY x="double" y="double" xunits="fraction" yunits="fraction"/>
|
11
|
+
# <!-- vec2 -->
|
12
|
+
# <rotationXY x="double" y="double" xunits="fraction" yunits"fraction"/>
|
13
|
+
# <!-- vec2 -->
|
14
|
+
# <size x="double" y="double" xunits="fraction" yunits="fraction"/>
|
15
|
+
# <!-- vec2 -->
|
16
|
+
# <rotation>0</rotation> <!-- float -->
|
17
|
+
# </ScreenOverlay>
|
18
|
+
|
19
|
+
module KML
|
20
|
+
|
21
|
+
class ScreenOverlay < Overlay
|
22
|
+
|
23
|
+
# A hash of options in the form of {:x, :y, :xunits, :yunits} that
|
24
|
+
# specify the point on the overlay image that is mapped to the screen
|
25
|
+
def overlay_xy
|
26
|
+
@overlay_xy
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set the overlay_xy coordinates
|
30
|
+
#
|
31
|
+
# xunits and yunits are :pixels or :fraction
|
32
|
+
def overlay_xy= coords
|
33
|
+
@overlay_xy = {:xunits => :fraction, :yunits => :fraction}.merge(coords)
|
34
|
+
end
|
35
|
+
|
36
|
+
# A hash of options in the form of {:x, :y, :xunits, :yunits} that
|
37
|
+
# specify the position on the screen where the image is mapped
|
38
|
+
def screen_xy
|
39
|
+
@screen_xy
|
40
|
+
end
|
41
|
+
|
42
|
+
#Set the screen_xy coordinates
|
43
|
+
#
|
44
|
+
# xunits and yunits are :pixels or :fraction
|
45
|
+
def screen_xy= coords
|
46
|
+
@screen_xy = {:xunits => :fraction, :yunits => :fraction}.merge(coords)
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_accessor :size, :rotation
|
50
|
+
|
51
|
+
def render(xm=Builder::XmlMarkup.new(:indent => 2))
|
52
|
+
xm.ScreenOverlay {
|
53
|
+
super
|
54
|
+
xm.overlayXY(@overlay_xy) if @overlay_xy
|
55
|
+
xm.screenXY(@screen_xy) if @screen_xy
|
56
|
+
xm.size{ @size } if @size
|
57
|
+
xm.rotation{ @rotation } if @rotation
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/kml/snippet.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module KML #:nodoc:
|
2
|
+
# A short description of a feature. In Google Earth, this description is displayed in the Places panel
|
3
|
+
# under the name of the feature. If a Snippet is not supplied, the first two lines of the description are
|
4
|
+
# used. In Google Earth, if a Placemark contains both a description and a Snippet, the Snippet appears
|
5
|
+
# beneath the Placemark in the Places panel, and the description appears in the Placemark's description
|
6
|
+
# balloon. This object does not support HTML markup.
|
7
|
+
class Snippet
|
8
|
+
# The maximum number of lines to display.
|
9
|
+
attr_accessor :max_lines
|
10
|
+
|
11
|
+
# The text that is displayed (HTML not supported)
|
12
|
+
attr_accessor :text
|
13
|
+
|
14
|
+
# Initialize the snippet with the given text and optionally with the given max_lines value.
|
15
|
+
def initialize(text, max_lines=nil)
|
16
|
+
@text = text
|
17
|
+
@max_lines = max_lines
|
18
|
+
end
|
19
|
+
|
20
|
+
# The maximum number of lines to display. Default is 2
|
21
|
+
def max_lines
|
22
|
+
@max_lines ||= 2
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|