kamelopard 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,12 +11,13 @@ module Kamelopard
11
11
  require 'xml'
12
12
  require 'yaml'
13
13
  require 'erb'
14
+ require 'cgi'
14
15
 
15
16
  @@sequence = 0
16
17
  @@id_prefix = ''
17
18
 
18
19
  def Kamelopard.get_document
19
- Document.instance
20
+ DocumentHolder.instance.current_document
20
21
  end
21
22
 
22
23
  def Kamelopard.get_next_id # :nodoc
@@ -127,6 +128,11 @@ module Kamelopard
127
128
  attr_accessor :kml_id
128
129
  attr_reader :comment
129
130
 
131
+ # This constructor looks for values in the options hash that match
132
+ # class attributes, and sets those attributes to the values in the
133
+ # hash. So a class with an attribute called :when can be set via the
134
+ # constructor by including ":when => some-value" in the options
135
+ # argument to the constructor.
130
136
  def initialize(options = {})
131
137
  @kml_id = "#{Kamelopard.id_prefix}#{self.class.name.gsub('Kamelopard::', '')}_#{ Kamelopard.get_next_id }"
132
138
 
@@ -140,15 +146,15 @@ module Kamelopard
140
146
  end
141
147
  end
142
148
 
143
- def comment=(a)
144
- if a.respond_to? :gsub then
145
- @comment = a.gsub(/</, '&lt;')
146
- else
147
- @comment = a
148
- end
149
+ # Adds an XML comment to this node. Handles HTML escaping the comment
150
+ # if needed
151
+ def comment=(cmnt)
152
+ require 'cgi'
153
+ @comment = CGI.escapeHTML(cmnt)
149
154
  end
150
155
 
151
- # Returns KML string for this object. Objects should override this method
156
+ # Returns XML::Node containing this object's KML. Objects should
157
+ # override this method
152
158
  def to_kml(elem)
153
159
  elem.attributes['id'] = @kml_id.to_s
154
160
  if not @comment.nil? and @comment != '' then
@@ -260,9 +266,9 @@ module Kamelopard
260
266
  # Note that this will not accept a one-dimensional array of numbers to add
261
267
  # a single point. Instead, create a Point with those numbers, and pass
262
268
  # it to add_element
263
- #-
269
+ #--
264
270
  # XXX The above stipulation is a weakness that needs fixing
265
- #+
271
+ #++
266
272
  def add_element(a)
267
273
  if a.kind_of? Enumerable then
268
274
  # We've got some sort of array or list. It could be a list of
@@ -517,7 +523,9 @@ module Kamelopard
517
523
  class TimePrimitive < Object
518
524
  end
519
525
 
520
- # Corresponds to KML's TimeStamp object. The @when attribute must be in a format KML understands.
526
+ # Corresponds to KML's TimeStamp object. The @when attribute must be in a
527
+ # format KML understands. Refer to the KML documentation to see which
528
+ # formats are available.
521
529
  class TimeStamp < TimePrimitive
522
530
  attr_accessor :when
523
531
  def initialize(ts_when = nil, options = {})
@@ -605,7 +613,7 @@ module Kamelopard
605
613
  end
606
614
  end
607
615
 
608
- # ExtendedData SchemaData objects
616
+ # Corresponds to KML's ExtendedData SchemaData objects
609
617
  class SchemaData
610
618
  attr_accessor :schemaUrl, :simpleData
611
619
  def initialize(schemaUrl, simpleData = {})
@@ -632,13 +640,14 @@ module Kamelopard
632
640
  end
633
641
 
634
642
  # Abstract class corresponding to KML's Feature object.
643
+ #--
635
644
  # XXX Make this support alternate namespaces
645
+ #++
636
646
  class Feature < Object
637
- # Abstract class
638
647
  attr_accessor :visibility, :open, :atom_author, :atom_link, :name,
639
- :phoneNumber, :description, :abstractView, :styles,
640
- :timeprimitive, :styleUrl, :styleSelector, :region, :metadata
641
- attr_reader :addressDetails, :snippet, :extendedData
648
+ :phoneNumber, :abstractView, :styles, :timeprimitive, :styleUrl,
649
+ :styleSelector, :region, :metadata
650
+ attr_reader :addressDetails, :snippet, :extendedData, :description
642
651
 
643
652
  include Snippet
644
653
 
@@ -650,6 +659,15 @@ module Kamelopard
650
659
  @name = name unless name.nil?
651
660
  end
652
661
 
662
+ def description=(a)
663
+ b = CGI.escapeHTML(a)
664
+ if b != a then
665
+ @description = XML::Node.new_cdata a
666
+ else
667
+ @description = a
668
+ end
669
+ end
670
+
653
671
  def extendedData=(a)
654
672
  raise "extendedData attribute must respond to the 'each' method" unless a.respond_to? :each
655
673
  @extendedData = a
@@ -695,9 +713,9 @@ module Kamelopard
695
713
 
696
714
  def addressDetails=(a)
697
715
  if a.nil? or a == '' then
698
- Document.instance.uses_xal = false
716
+ DocumentHolder.instance.current_document.uses_xal = false
699
717
  else
700
- Document.instance.uses_xal = true
718
+ DocumentHolder.instance.current_document.uses_xal = true
701
719
  end
702
720
  @addressDetails = a
703
721
  end
@@ -794,7 +812,7 @@ module Kamelopard
794
812
  @styles = []
795
813
  @folders = []
796
814
  super
797
- Document.instance.folders << self
815
+ DocumentHolder.instance.current_document.folders << self
798
816
  end
799
817
 
800
818
  def styles=(a)
@@ -844,18 +862,29 @@ module Kamelopard
844
862
  k
845
863
  end
846
864
 
847
- # Represents KML's Document class. This is a Singleton object; Kamelopard
848
- # scripts can (for now) manage only one Document at a time.
865
+ # Represents KML's Document class.
849
866
  class Document < Container
850
- include Singleton
851
- attr_accessor :flyto_mode, :folders, :tours, :uses_xal
867
+ attr_accessor :flyto_mode, :folders, :tours, :uses_xal, :vsr_actions
852
868
 
853
869
  def initialize(options = {})
854
870
  @tours = []
855
871
  @folders = []
872
+ @vsr_actions = []
873
+ DocumentHolder.instance << self
856
874
  super
857
875
  end
858
876
 
877
+ # Returns viewsyncrelay actions as a hash
878
+ def get_actions
879
+ {
880
+ 'actions' => @vsr_actions.collect { |a| a.to_hash }
881
+ }
882
+ end
883
+
884
+ def get_actions_yaml
885
+ get_actions.to_yaml
886
+ end
887
+
859
888
  # Returns the current Tour object
860
889
  def tour
861
890
  Tour.new if @tours.length == 0
@@ -955,6 +984,61 @@ module Kamelopard
955
984
  end
956
985
  end
957
986
 
987
+ # Holds a set of Document objects, so we can work with multiple KML files
988
+ # at once and keep track of them. It's important for Kamelopard's usability
989
+ # to have the concept of a "current" document, so we don't have to specify
990
+ # the document we're talking about each time we do something interesting.
991
+ # This class supports that idea.
992
+ class DocumentHolder
993
+ include Singleton
994
+ attr_accessor :document_index, :initialized
995
+ attr_reader :documents
996
+
997
+ def initialize(doc = nil)
998
+ @documents = []
999
+ @document_index = -1
1000
+ if ! doc.nil?
1001
+ self.documents << doc
1002
+ end
1003
+ end
1004
+
1005
+ def document_index
1006
+ return @document_index
1007
+ end
1008
+
1009
+ def document_index=(a)
1010
+ @document_index = a
1011
+ end
1012
+
1013
+ def current_document
1014
+ # Automatically create a Document if we don't already have one
1015
+ if @documents.size <= 0
1016
+ Document.new
1017
+ @document_index = 0
1018
+ end
1019
+ return @documents[@document_index]
1020
+ end
1021
+
1022
+ def <<(a)
1023
+ raise "Cannot add a non-Document object to a DocumentHolder" unless a.kind_of? Document
1024
+ @documents << a
1025
+ @document_index += 1
1026
+ end
1027
+
1028
+ def [](a)
1029
+ return @documents[a]
1030
+ end
1031
+
1032
+ def []=(i, v)
1033
+ raise "Cannot include a non-Document object in a DocumentHolder" unless v.kind_of? Document
1034
+ @documents[i] = v
1035
+ end
1036
+
1037
+ def size
1038
+ return @documents.size
1039
+ end
1040
+ end
1041
+
958
1042
  # Corresponds to KML's ColorStyle object. Color is stored as an 8-character hex
959
1043
  # string, with two characters each of alpha, blue, green, and red values, in
960
1044
  # that order, matching the ordering the KML spec demands.
@@ -1273,7 +1357,7 @@ module Kamelopard
1273
1357
  def initialize(options = {})
1274
1358
  super
1275
1359
  @attached = false
1276
- Document.instance.styles << self
1360
+ DocumentHolder.instance.current_document.styles << self
1277
1361
  end
1278
1362
 
1279
1363
  def attached?
@@ -1411,7 +1495,7 @@ module Kamelopard
1411
1495
  attr_accessor :standalone
1412
1496
 
1413
1497
  def initialize(options = {})
1414
- Document.instance.tour << self unless options[:standalone]
1498
+ DocumentHolder.instance.current_document.tour << self unless options[:standalone]
1415
1499
  super
1416
1500
  end
1417
1501
  end
@@ -1585,12 +1669,13 @@ module Kamelopard
1585
1669
  class Tour < Object
1586
1670
  attr_accessor :name, :description, :last_abs_view, :playlist, :icon
1587
1671
 
1588
- def initialize(name = nil, description = nil)
1672
+ def initialize(name = nil, description = nil, no_wait = false)
1589
1673
  super()
1590
1674
  @name = name
1591
1675
  @description = description
1592
1676
  @playlist = []
1593
- Document.instance.tours << self
1677
+ DocumentHolder.instance.current_document.tours << self
1678
+ Wait.new(0.1, :comment => "This wait is automatic, and helps prevent animation glitches") unless no_wait
1594
1679
  end
1595
1680
 
1596
1681
  # Add another element to this Tour
@@ -1622,7 +1707,7 @@ module Kamelopard
1622
1707
 
1623
1708
  def initialize(options = {})
1624
1709
  super nil, options
1625
- Document.instance.folder << self
1710
+ DocumentHolder.instance.current_document.folder << self
1626
1711
  end
1627
1712
 
1628
1713
  def to_kml(elem)
@@ -1799,7 +1884,7 @@ module Kamelopard
1799
1884
  Kamelopard.add_altitudeMode(@altitudeMode, k)
1800
1885
  end
1801
1886
  m = XML::Node.new 'rotation'
1802
- m = @rotation.to_s
1887
+ m << @rotation.to_s
1803
1888
  k << m
1804
1889
  elem << k unless elem.nil?
1805
1890
  k
@@ -2113,6 +2198,7 @@ module Kamelopard
2113
2198
  end
2114
2199
  end
2115
2200
 
2201
+ # Abstract class corresponding to KML's MultiGeometry object
2116
2202
  class MultiGeometry < Geometry
2117
2203
  attr_accessor :geometries
2118
2204
 
@@ -2136,6 +2222,7 @@ module Kamelopard
2136
2222
  end
2137
2223
  end
2138
2224
 
2225
+ # Analogue of KML's NetworkLink class
2139
2226
  class NetworkLink < Feature
2140
2227
  attr_accessor :refreshVisibility, :flyToView, :link
2141
2228
 
@@ -2185,6 +2272,7 @@ module Kamelopard
2185
2272
  end
2186
2273
  end
2187
2274
 
2275
+ # Corresponds to Google Earth's gx:Track extension to KML
2188
2276
  class Track < Geometry
2189
2277
  attr_accessor :altitudeMode, :when, :coord, :angles, :model
2190
2278
  def initialize(options = {})
@@ -2210,5 +2298,43 @@ module Kamelopard
2210
2298
  e
2211
2299
  end
2212
2300
  end
2301
+
2302
+ # Viewsyncrelay action
2303
+ class VSRAction < Object
2304
+ attr_accessor :name, :tour_name, :verbose, :fail_count, :input,
2305
+ :action, :exit_action, :repeat, :constraints, :reset_constraints,
2306
+ :initially_disabled
2307
+ # XXX Consider adding some constraints, so that things like @name and @action don't go nil
2308
+ # XXX Also ensure constraints and reset_constraints are hashes,
2309
+ # containing reasonable values, and reasonable keys ('latitude' vs.
2310
+ # :latitude, for instance)
2311
+
2312
+ def initialize(name, options = {})
2313
+ @name = name
2314
+ @constraints = {}
2315
+ @repeat = 'DEFAULT'
2316
+ @input = 'ALL'
2317
+ super(options)
2318
+
2319
+ DocumentHolder.instance.current_document.vsr_actions << self
2320
+ end
2321
+
2322
+ def to_hash
2323
+ a = {}
2324
+ a['name'] = @name unless @name.nil?
2325
+ a['id'] = @id unless @id.nil?
2326
+ a['input'] = @input unless @input.nil?
2327
+ a['tour_name'] = @tour_name unless @tour_name.nil?
2328
+ a['verbose'] = @verbose unless @verbose.nil?
2329
+ a['fail_count'] = @fail_count unless @fail_count.nil?
2330
+ a['action'] = @action unless @action.nil?
2331
+ a['exit_action'] = @exit_action unless @exit_action.nil?
2332
+ a['repeat'] = @repeat unless @repeat.nil?
2333
+ a['initially_disabled'] = @initially_disabled unless @initially_disabled.nil?
2334
+ a['constraints'] = @constraints unless @constraints.nil?
2335
+ a['reset_constraints'] = @reset_constraints unless @reset_constraints.nil?
2336
+ a
2337
+ end
2338
+ end
2213
2339
  end
2214
2340
  # End of Kamelopard module