ruby_kml 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/LICENSE +7 -0
  2. data/README.md +34 -0
  3. data/Rakefile +52 -0
  4. data/examples/melbourne-stations.kml +18 -0
  5. data/lib/kml/color_style.rb +34 -0
  6. data/lib/kml/container.rb +42 -0
  7. data/lib/kml/document.rb +46 -0
  8. data/lib/kml/feature.rb +185 -0
  9. data/lib/kml/folder.rb +29 -0
  10. data/lib/kml/geometry.rb +102 -0
  11. data/lib/kml/ground_overlay.rb +18 -0
  12. data/lib/kml/hot_spot.rb +31 -0
  13. data/lib/kml/icon.rb +15 -0
  14. data/lib/kml/icon_style.rb +52 -0
  15. data/lib/kml/lat_lon_box.rb +36 -0
  16. data/lib/kml/line_string.rb +40 -0
  17. data/lib/kml/line_style.rb +15 -0
  18. data/lib/kml/linear_ring.rb +67 -0
  19. data/lib/kml/link.rb +51 -0
  20. data/lib/kml/look_at.rb +28 -0
  21. data/lib/kml/model.rb +40 -0
  22. data/lib/kml/multi_geometry.rb +26 -0
  23. data/lib/kml/object.rb +37 -0
  24. data/lib/kml/overlay.rb +21 -0
  25. data/lib/kml/placemark.rb +60 -0
  26. data/lib/kml/point.rb +65 -0
  27. data/lib/kml/poly_style.rb +16 -0
  28. data/lib/kml/polygon.rb +91 -0
  29. data/lib/kml/screen_overlay.rb +61 -0
  30. data/lib/kml/snippet.rb +25 -0
  31. data/lib/kml/style.rb +23 -0
  32. data/lib/kml/style_map.rb +39 -0
  33. data/lib/kml/style_selector.rb +7 -0
  34. data/lib/kml/version.rb +9 -0
  35. data/lib/kml_file.rb +55 -0
  36. data/lib/ruby_kml.rb +8 -0
  37. data/ruby_kml.gemspec +14 -0
  38. data/test/cdata_and_snippet.kml +21 -0
  39. data/test/ground_overlays.kml +25 -0
  40. data/test/kml/point_test.rb +10 -0
  41. data/test/kml_file_test.rb +255 -0
  42. data/test/paths.kml +33 -0
  43. data/test/polygon.kml +20 -0
  44. data/test/polygon_inner.kml +25 -0
  45. data/test/polygon_style.kml +24 -0
  46. data/test/simple_placemark.kml +12 -0
  47. data/test/style_map.kml +40 -0
  48. data/test/test_helper.rb +11 -0
  49. metadata +124 -0
@@ -0,0 +1,23 @@
1
+ module KML
2
+ # A Style defines an addressable style group that can be referenced by StyleMaps and Features. Styles affect
3
+ # how Geometry is presented in the 3D viewer and how Features appear in the Places panel of the List view.
4
+ # Shared styles are collected in a Document and must have an id defined for them so that they can be referenced
5
+ # by the individual Features that use them.
6
+ class Style < StyleSelector
7
+ attr_accessor :icon_style
8
+ attr_accessor :label_style
9
+ attr_accessor :line_style
10
+ attr_accessor :poly_style
11
+ attr_accessor :balloon_style
12
+ attr_accessor :list_style
13
+
14
+ def render(xm=Builder::XmlMarkup.new(:indent => 2))
15
+ xm.Style(:id => id) {
16
+ %w(Icon Label Line Poly Balloon List).each do |name|
17
+ field = "#{name.downcase}_style".to_sym
18
+ self.__send__(field).render(xm) unless self.__send__(field).nil?
19
+ end
20
+ }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # Source file that defines a StyleMap element
2
+ #
3
+ # <StyleMap id="ID">
4
+ # <!-- extends StyleSelector -->
5
+ # <!-- elements specific to StyleMap -->
6
+ # <Pair id="ID">
7
+ # <key>normal</key> <!-- kml:styleStateEnum: normal or highlight
8
+ # <styleUrl>...</styleUrl> <!-- anyURI -->
9
+ # </Pair>
10
+ # </StyeMap>
11
+
12
+ module KML #:nodoc:
13
+ # A StyleMap maps between two different icon styles. Typically a StyleMap element is used to provide
14
+ # separate normal and highlighted styles for a placemark, so that the highlighted version appears when
15
+ # the user mouses over the icon in Google Earth.
16
+ class StyleMap < StyleSelector
17
+ # Key/styleUrl pairs
18
+ attr_accessor :pairs
19
+
20
+ # Get the pairs Hash. Each key/value pair maps a mode (normal or highlight) to the predefined style URL.
21
+ # Each pair contains a key and a value which references the style. For referenced style elements that
22
+ # are local to the KML document, a simple # referencing is used. For styles that are contained in
23
+ # external files, use a full URL along with # referencing.
24
+ def pairs
25
+ @pairs ||= {}
26
+ end
27
+
28
+ def render(xm=Builder::XmlMarkup.new(:indent => 2))
29
+ xm.StyleMap(:id => id) {
30
+ pairs.each do |key, value|
31
+ xm.Pair {
32
+ xm.key(key)
33
+ xm.styleUrl(value)
34
+ }
35
+ end
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ module KML
2
+ class StyleSelector < KML::Object
3
+ end
4
+ end
5
+
6
+ require 'kml/style'
7
+ require 'kml/style_map'
@@ -0,0 +1,9 @@
1
+ module KML#:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 7
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,55 @@
1
+ # The KMLFile class is the top level class for constructing KML files. To create a new KML file
2
+ # create a KMLFile instance and add KML objects to its objects. For example:
3
+ #
4
+ # f = KMLFile.new
5
+ # f.objects << Placemark.new(
6
+ # :name => 'Simple placemark',
7
+ # :description => 'Attached to the ground. Intelligently places itself at the height of the underlying terrain.',
8
+ # :geometry => Point.new(:coordinates=>'-122.0822035425683,37.42228990140251,0')
9
+ # )
10
+ # puts f.render
11
+ class KMLFile
12
+ # The objects in the KML file
13
+ def objects
14
+ @objects ||= []
15
+ end
16
+ alias :features :objects
17
+
18
+ # Render the KML file
19
+ def render(xm=Builder::XmlMarkup.new(:indent => 2))
20
+ xm.instruct!
21
+ xm.kml(:xmlns => 'http://earth.google.com/kml/2.1'){
22
+ objects.each { |o| o.render(xm) }
23
+ }
24
+ end
25
+
26
+ def save filename
27
+ File.open(filename, 'w') { |f| f.write render }
28
+ end
29
+
30
+ # Parse the KML file using nokogiri
31
+ # <kml xmlns="http://www.opengis.net/kml/2.2">
32
+ # <NetworkLinkControl> ... </NetworkLinkControl>
33
+ # <!-- 0 or 1 Feature elements -->
34
+ # </kml>
35
+ def self.parse(io)
36
+ kml = self.new
37
+ doc = Nokogiri::XML::Document.parse(io)
38
+ doc.root.element_children.each do |cld|
39
+ case cld.name
40
+ when 'Document'
41
+ kml.features << KML::Document.parse(cld)
42
+ when 'Folder'
43
+ when 'NetworkLink'
44
+ when 'Placemark'
45
+ when 'GroundOverlay'
46
+ when 'PhotoOverlay'
47
+ when 'ScreenOverlay'
48
+ end
49
+ end
50
+ kml
51
+ end
52
+
53
+ end
54
+
55
+ require 'kml/object'
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'builder'
3
+ require 'nokogiri'
4
+
5
+ require 'kml_file'
6
+
7
+ class InvalidKMLError < StandardError
8
+ end
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.version = "0.1.7"
3
+ s.date = "2012-05-07"
4
+ s.name = "ruby_kml"
5
+ s.summary = "Generate KML files with ruby"
6
+ s.email = ""
7
+ s.homepage = "http://github.com/schleyfox/ruby_kml"
8
+ s.description = "Generate KML files with ruby"
9
+ s.has_rdoc = false
10
+ s.authors = ["aeden, schleyfox, xaviershay, andykram, IanVaughan"]
11
+ s.files = Dir['**/**']
12
+ s.add_dependency('nokogiri')
13
+ s.add_dependency('builder')
14
+ end
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <kml xmlns="http://earth.google.com/kml/2.1">
3
+ <Document>
4
+ <name>Document with CDATA example</name>
5
+ <Snippet>Document level snippet2</Snippet>
6
+ <Placemark>
7
+ <name>CDATA example</name>
8
+ <description>
9
+ <![CDATA[<h1>CDATA Tags are useful!</h1>
10
+ <p><font color="red">Text is <i>more readable</i> and
11
+ <b>easier to write</b> when you can avoid using entity
12
+ references.</font></p>
13
+ ]]>
14
+ </description>
15
+ <Snippet>Example of a snippet2</Snippet>
16
+ <Point>
17
+ <coordinates>-122.0822035425683,37.4228,0</coordinates>
18
+ </Point>
19
+ </Placemark>
20
+ </Document>
21
+ </kml>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <kml xmlns="http://earth.google.com/kml/2.1">
3
+ <Folder>
4
+ <name>Ground Overlays</name>
5
+ <description>
6
+ <![CDATA[Examples of ground overlays]]>
7
+ </description>
8
+ <GroundOverlay>
9
+ <name>Large-scale overlay on terrain</name>
10
+ <description>
11
+ <![CDATA[Overlay shows Mount Etna erupting on July 13th, 2001.]]>
12
+ </description>
13
+ <Icon>
14
+ <href>http://code.google.com/apis/kml/documentation/etna.jpg</href>
15
+ </Icon>
16
+ <LatLonBox>
17
+ <north>37.91904192681665</north>
18
+ <south>37.46543388598137</south>
19
+ <east>15.35832653742206</east>
20
+ <west>14.60128369746704</west>
21
+ <rotation/>
22
+ </LatLonBox>
23
+ </GroundOverlay>
24
+ </Folder>
25
+ </kml>
@@ -0,0 +1,10 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class KML::PointTest < Test::Unit::TestCase
4
+ include KML
5
+
6
+ def test_allows_coordinates_to_be_specified_with_a_hash
7
+ point = Point.new(:coordinates => {:lat => 10, :lng => 20, :alt => 30})
8
+ assert_equal [20, 10, 30], point.coordinates
9
+ end
10
+ end
@@ -0,0 +1,255 @@
1
+ require './test/test_helper'
2
+
3
+ class KMLFileTest < Test::Unit::TestCase
4
+ include KML
5
+
6
+ # This also tests the stripping of coordinates
7
+ def test_placemark
8
+ kml = KMLFile.new
9
+ kml.objects << Placemark.new(
10
+ :name => 'Simple placemark',
11
+ :description => 'Attached to the ground. Intelligently places itself at the height of the underlying terrain.',
12
+ :geometry => Point.new(:coordinates=>' -122.0822035425683,37.42228990140251,0 ')
13
+ )
14
+ assert_equal File.read('test/simple_placemark.kml'), kml.render
15
+ end
16
+
17
+ def test_cdata_description
18
+ description = <<-DESC
19
+ <h1>CDATA Tags are useful!</h1>
20
+ <p><font color="red">Text is <i>more readable</i> and
21
+ <b>easier to write</b> when you can avoid using entity
22
+ references.</font></p>
23
+ DESC
24
+
25
+ kml = KMLFile.new
26
+ document = Document.new(
27
+ :name => 'Document with CDATA example',
28
+ :snippet => Snippet.new("Document level snippet")
29
+ )
30
+ document.features << Placemark.new(
31
+ :name => 'CDATA example',
32
+ :description => description,
33
+ :snippet => Snippet.new("Example of a snippet"),
34
+ :geometry => Point.new(:coordinates=>'-122.0822035425683,37.4228,0')
35
+ )
36
+ kml.objects << document
37
+ assert_equal File.read('test/cdata_and_snippet.kml'), kml.render
38
+ end
39
+
40
+ def test_ground_overlays
41
+ kml = KMLFile.new
42
+ folder = Folder.new(
43
+ :name => 'Ground Overlays',
44
+ :description => 'Examples of ground overlays'
45
+ )
46
+ folder.features << GroundOverlay.new(
47
+ :name => 'Large-scale overlay on terrain',
48
+ :description => 'Overlay shows Mount Etna erupting on July 13th, 2001.',
49
+ :icon => Icon.new(:href => 'http://code.google.com/apis/kml/documentation/etna.jpg'),
50
+ :lat_lon_box => LatLonBox.new(
51
+ :north => 37.91904192681665,
52
+ :south => 37.46543388598137,
53
+ :east => 15.35832653742206,
54
+ :west => 14.60128369746704,
55
+ :rotation => -0.1556640799496235
56
+ )
57
+ )
58
+ kml.objects << folder
59
+ assert_equal File.read('test/ground_overlays.kml'), kml.render
60
+ end
61
+
62
+ def test_paths
63
+ kml = KMLFile.new
64
+ kml.objects << Document.new(
65
+ :name => 'Paths',
66
+ :description => 'Examples of paths. Note that the tessellate tag is by default
67
+ set to 0. If you want to create tessellated lines, they must be authored
68
+ (or edited) directly in KML.',
69
+ :styles => [
70
+ Style.new(
71
+ :id => 'yellowLineGreenPoly',
72
+ :line_style => LineStyle.new(:color => '7f00ffff', :width => 4),
73
+ :poly_style => PolyStyle.new(:color => '7f00ff00')
74
+ )
75
+ ],
76
+ :features => [
77
+ Placemark.new(
78
+ :name => 'Absolute Extruded',
79
+ :description => 'Transparent green wall with yellow outlines',
80
+ :style_url => '#yellowLineGreenPoly',
81
+ :geometry => LineString.new(
82
+ :extrude => true,
83
+ :tessellate => true,
84
+ :altitude_mode => 'absolute',
85
+ :coordinates => '-112.2550785337791,36.07954952145647,2357
86
+ -112.2549277039738,36.08117083492122,2357
87
+ -112.2552505069063,36.08260761307279,2357
88
+ -112.2564540158376,36.08395660588506,2357
89
+ -112.2580238976449,36.08511401044813,2357
90
+ -112.2595218489022,36.08584355239394,2357
91
+ -112.2608216347552,36.08612634548589,2357
92
+ -112.262073428656,36.08626019085147,2357
93
+ -112.2633204928495,36.08621519860091,2357
94
+ -112.2644963846444,36.08627897945274,2357
95
+ -112.2656969554589,36.08649599090644,2357'
96
+ )
97
+ )
98
+ ]
99
+ )
100
+ assert_equal File.read('test/paths.kml'), kml.render
101
+ end
102
+
103
+ def test_polygon
104
+ kml = KMLFile.new
105
+ kml.objects << Placemark.new(
106
+ :name => 'The Pentagon',
107
+ :geometry => Polygon.new(
108
+ :extrude => true,
109
+ :altitude_mode => 'relativeToGround',
110
+ :outer_boundary_is => LinearRing.new(
111
+ :coordinates => '-77.05788457660967,38.87253259892824,100
112
+ -77.05465973756702,38.87291016281703,100
113
+ -77.05315536854791,38.87053267794386,100
114
+ -77.05552622493516,38.868757801256,100
115
+ -77.05844056290393,38.86996206506943,100
116
+ -77.05788457660967,38.87253259892824,100'
117
+ ),
118
+ :inner_boundary_is => LinearRing.new(
119
+ :coordinates => '-77.05668055019126,38.87154239798456,100
120
+ -77.05542625960818,38.87167890344077,100
121
+ -77.05485125901024,38.87076535397792,100
122
+ -77.05577677433152,38.87008686581446,100
123
+ -77.05691162017543,38.87054446963351,100
124
+ -77.05668055019126,38.87154239798456,100'
125
+ )
126
+ )
127
+ )
128
+ assert_equal File.read('test/polygon.kml'), kml.render
129
+ end
130
+
131
+ def test_polygon_with_multiple_inner_boundaries
132
+ kml = KMLFile.new
133
+ kml.objects << Placemark.new(
134
+ :name => 'The Pentagon',
135
+ :geometry => Polygon.new(
136
+ :extrude => true,
137
+ :altitude_mode => 'relativeToGround',
138
+ :outer_boundary_is => LinearRing.new(
139
+ :coordinates => '-77.05788457660967,38.87253259892824,100
140
+ -77.05465973756702,38.87291016281703,100
141
+ -77.05315536854791,38.87053267794386,100
142
+ -77.05552622493516,38.868757801256,100
143
+ -77.05844056290393,38.86996206506943,100
144
+ -77.05788457660967,38.87253259892824,100'
145
+ ),
146
+ :inner_boundary_is => [
147
+ LinearRing.new(
148
+ :coordinates => '-77.05668055019126,38.87154239798456,100
149
+ -77.05542625960818,38.87167890344077,100
150
+ -77.05485125901024,38.87076535397792,100
151
+ -77.05577677433152,38.87008686581446,100
152
+ -77.05691162017543,38.87054446963351,100
153
+ -77.05668055019126,38.87154239798456,100'
154
+ ),
155
+ LinearRing.new(
156
+ :coordinates => '-77.05668055019126,38.87154239798456,100
157
+ -77.05542625960818,38.87167890344077,100
158
+ -77.05485125901024,38.87076535397792,100
159
+ -77.05577677433152,38.87008686581446,100
160
+ -77.05691162017543,38.87054446963351,100
161
+ -77.05668055019126,38.87154239798456,100'
162
+ )
163
+ ]
164
+ )
165
+ )
166
+ assert_equal File.read('test/polygon_inner.kml'), kml.render
167
+ end
168
+
169
+ def test_geometry_styles
170
+ kml = KMLFile.new
171
+ kml.objects << Style.new(
172
+ :id => "transBluePoly",
173
+ :line_style => LineStyle.new(
174
+ :width => 1.5
175
+ ),
176
+ :poly_style => PolyStyle.new(
177
+ :color => '7dff0000'
178
+ )
179
+ )
180
+ kml.objects << Placemark.new(
181
+ :name => 'Building 41',
182
+ :style_url => '#transBluePoly',
183
+ :geometry => Polygon.new(
184
+ :extrude => true,
185
+ :altitude_mode => 'relativeToGround',
186
+ :outer_boundary_is => LinearRing.new(
187
+ :coordinates => '-122.0857412771483,37.42227033155257,17
188
+ -122.0858169768481,37.42231408832346,17
189
+ -122.085852582875,37.42230337469744,17
190
+ -122.0858799945639,37.42225686138789,17
191
+ -122.0858860101409,37.4222311076138,17
192
+ -122.0858069157288,37.42220250173855,17
193
+ -122.0858379542653,37.42214027058678,17
194
+ -122.0856732640519,37.42208690214408,17
195
+ -122.0856022926407,37.42214885429042,17
196
+ -122.0855902778436,37.422128290487,17
197
+ -122.0855841672237,37.42208171967246,17
198
+ -122.0854852065741,37.42210455874995,17
199
+ -122.0855067264352,37.42214267949824,17
200
+ -122.0854430712915,37.42212783846172,17
201
+ -122.0850990714904,37.42251282407603,17
202
+ -122.0856769818632,37.42281815323651,17
203
+ -122.0860162273783,37.42244918858722,17
204
+ -122.0857260327004,37.42229239604253,17
205
+ -122.0857412771483,37.42227033155257,17'
206
+ )
207
+ )
208
+ )
209
+
210
+ assert_equal File.read('test/polygon_style.kml'), kml.render
211
+ end
212
+
213
+ def test_style_map
214
+ kml = KMLFile.new
215
+ kml.objects << Document.new(
216
+ :name => 'Highlighted Icon',
217
+ :description => 'Place your mouse over the icon to see it display the new icon',
218
+ :styles => [
219
+ Style.new(
220
+ :id => "highlightPlacemark",
221
+ :icon_style => IconStyle.new(
222
+ :icon => Icon.new(
223
+ :href => "http://maps.google.com/mapfiles/kml/paddle/red-stars.png"
224
+ )
225
+ )
226
+ ),
227
+ Style.new(
228
+ :id => "normalPlacemark",
229
+ :icon_style => IconStyle.new(
230
+ :icon => Icon.new(
231
+ :href => "http://maps.google.com/mapfiles/kml/paddle/wht-blank.png"
232
+ )
233
+ )
234
+ ),
235
+ StyleMap.new(
236
+ :id => 'exampleStyleMap',
237
+ :pairs => {
238
+ 'normal' => '#normalPlacemark',
239
+ 'highlight' => '#highlightPlacemark'
240
+ }
241
+ )
242
+ ],
243
+ :features => [
244
+ Placemark.new(
245
+ :name => 'Roll over this icon',
246
+ :style_url => '#exampleStyleMap',
247
+ :geometry => Point.new(
248
+ :coordinates => '-122.0856545755255,37.42243077405461,0'
249
+ )
250
+ )
251
+ ]
252
+ )
253
+ assert_equal File.read('test/style_map.kml'), kml.render
254
+ end
255
+ end