kamelopard 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/kamelopard/classes.rb +141 -32
- data/lib/kamelopard/functions.rb +157 -3
- data/lib/kamelopard/pointlist.rb +9 -5
- metadata +5 -7
data/lib/kamelopard/classes.rb
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
# http://code.google.com/apis/kml/documentation/kmlreference.html for a
|
5
5
|
# description of KML
|
6
6
|
|
7
|
+
# XXX gx:balloonVisibility isn't represented here. Fix that.
|
8
|
+
|
7
9
|
module Kamelopard
|
8
10
|
require 'singleton'
|
9
11
|
require 'kamelopard/pointlist'
|
@@ -11,6 +13,7 @@ module Kamelopard
|
|
11
13
|
require 'yaml'
|
12
14
|
|
13
15
|
@@sequence = 0
|
16
|
+
@@id_prefix = ''
|
14
17
|
|
15
18
|
def Kamelopard.get_document
|
16
19
|
Document.instance
|
@@ -21,6 +24,14 @@ module Kamelopard
|
|
21
24
|
@@sequence
|
22
25
|
end
|
23
26
|
|
27
|
+
def Kamelopard.id_prefix=(a)
|
28
|
+
@@id_prefix = a
|
29
|
+
end
|
30
|
+
|
31
|
+
def Kamelopard.id_prefix
|
32
|
+
@@id_prefix
|
33
|
+
end
|
34
|
+
|
24
35
|
#--
|
25
36
|
# Intelligently adds elements to a KML object. Expects the KML object as the
|
26
37
|
# first argument, an array as the second. Each entry in the array is itself an
|
@@ -54,7 +65,7 @@ module Kamelopard
|
|
54
65
|
# Accepts XdX'X.X", XDXmX.XXs, XdXmX.XXs, or X.XXXX with either +/- or N/E/S/W
|
55
66
|
#++
|
56
67
|
def Kamelopard.convert_coord(a) # :nodoc
|
57
|
-
a = a.to_s.upcase.strip
|
68
|
+
a = a.to_s.upcase.strip.gsub(/\s+/, '')
|
58
69
|
|
59
70
|
mult = 1
|
60
71
|
if a =~ /^-/ then
|
@@ -80,6 +91,10 @@ module Kamelopard
|
|
80
91
|
# coord is in d'"
|
81
92
|
p = a.split /[D"']/
|
82
93
|
a = p[0].to_f + (p[2].to_f / 60.0 + p[1].to_f) / 60.0
|
94
|
+
elsif m = (a =~ /^(\d+)°(\d+)'(\d+\.\d+)?"$/) then
|
95
|
+
# coord is in °'"
|
96
|
+
b = a
|
97
|
+
a = $1.to_f + ($3.to_f / 60.0 + $2.to_f) / 60.0
|
83
98
|
else
|
84
99
|
raise "Couldn't determine coordinate format for #{a}"
|
85
100
|
end
|
@@ -103,13 +118,17 @@ module Kamelopard
|
|
103
118
|
end
|
104
119
|
|
105
120
|
# Base class for all Kamelopard objects. Manages object ID and a single
|
106
|
-
# comment string associated with the object
|
121
|
+
# comment string associated with the object. Object IDs are stored in the
|
122
|
+
# kml_id attribute, and are prefixed with the value last passed to
|
123
|
+
# Kamelopard.id_prefix=, if anything. Note that assigning this prefix will
|
124
|
+
# *not* change the IDs of Kamelopard objects that are already
|
125
|
+
# initialized... just ones initialized thereafter.
|
107
126
|
class Object
|
108
127
|
attr_accessor :kml_id
|
109
128
|
attr_reader :comment
|
110
129
|
|
111
130
|
def initialize(options = {})
|
112
|
-
@kml_id = "#{self.class.name.gsub('Kamelopard::', '')}_#{ Kamelopard.get_next_id }"
|
131
|
+
@kml_id = "#{Kamelopard.id_prefix}#{self.class.name.gsub('Kamelopard::', '')}_#{ Kamelopard.get_next_id }"
|
113
132
|
|
114
133
|
options.each do |k, v|
|
115
134
|
method = "#{k}=".to_sym
|
@@ -164,8 +183,8 @@ module Kamelopard
|
|
164
183
|
|
165
184
|
def initialize(longitude = nil, latitude = nil, altitude = nil, options = {})
|
166
185
|
super options
|
167
|
-
@longitude = longitude unless longitude.nil?
|
168
|
-
@latitude = latitude unless latitude.nil?
|
186
|
+
@longitude = Kamelopard.convert_coord(longitude) unless longitude.nil?
|
187
|
+
@latitude = Kamelopard.convert_coord(latitude) unless latitude.nil?
|
169
188
|
@altitude = altitude unless altitude.nil?
|
170
189
|
end
|
171
190
|
|
@@ -357,8 +376,8 @@ module Kamelopard
|
|
357
376
|
|
358
377
|
@heading = 0
|
359
378
|
@tilt = 0
|
360
|
-
@roll =
|
361
|
-
@range =
|
379
|
+
@roll = nil
|
380
|
+
@range = nil
|
362
381
|
@altitudeMode = :clampToGround
|
363
382
|
@viewerOptions = {}
|
364
383
|
|
@@ -523,6 +542,7 @@ module Kamelopard
|
|
523
542
|
# Corresponds to KML's TimeSpan object. @begin and @end must be in a format KML
|
524
543
|
# understands.
|
525
544
|
class TimeSpan < TimePrimitive
|
545
|
+
# XXX Evidence suggests this doesn't support unbounded intervals. Fix that, if it's true.
|
526
546
|
attr_accessor :begin, :end
|
527
547
|
def initialize(ts_begin = nil, ts_end = nil, options = {})
|
528
548
|
super options
|
@@ -564,14 +584,61 @@ module Kamelopard
|
|
564
584
|
end
|
565
585
|
end
|
566
586
|
|
587
|
+
# Corresponds to Data elements within ExtendedData
|
588
|
+
class Data
|
589
|
+
attr_accessor :name, :displayName, :value
|
590
|
+
def initialize(name, value, displayName = nil)
|
591
|
+
@name = name
|
592
|
+
@displayName = displayName
|
593
|
+
@value = value
|
594
|
+
end
|
595
|
+
|
596
|
+
def to_kml(elem = nil)
|
597
|
+
v = XML::Node.new 'Data'
|
598
|
+
v.attributes['name'] = @name
|
599
|
+
Kamelopard.kml_array(v, [
|
600
|
+
[@value, 'value'],
|
601
|
+
[@displayName, 'displayName']
|
602
|
+
])
|
603
|
+
elem << v unless elem.nil?
|
604
|
+
v
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
# ExtendedData SchemaData objects
|
609
|
+
class SchemaData
|
610
|
+
attr_accessor :schemaUrl, :simpleData
|
611
|
+
def initialize(schemaUrl, simpleData = {})
|
612
|
+
@schemaUrl = schemaUrl
|
613
|
+
raise "SchemaData's simpleData attribute should behave like a hash" unless simpleData.respond_to? :keys
|
614
|
+
@simpleData = simpleData
|
615
|
+
end
|
616
|
+
|
617
|
+
def <<(a)
|
618
|
+
@simpleData.merge a
|
619
|
+
end
|
620
|
+
|
621
|
+
def to_kml(elem = nil)
|
622
|
+
s = XML::Node.new 'SchemaData'
|
623
|
+
s.attributes['schemaUrl'] = @schemaUrl
|
624
|
+
@simpleData.each do |k, v|
|
625
|
+
sd = XML::Node.new 'SimpleData', v
|
626
|
+
sd.attributes['name'] = k
|
627
|
+
s << sd
|
628
|
+
end
|
629
|
+
elem << v unless elem.nil?
|
630
|
+
v
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
567
634
|
# Abstract class corresponding to KML's Feature object.
|
635
|
+
# XXX Make this support alternate namespaces
|
568
636
|
class Feature < Object
|
569
637
|
# Abstract class
|
570
638
|
attr_accessor :visibility, :open, :atom_author, :atom_link, :name,
|
571
|
-
:phoneNumber, :description, :abstractView,
|
572
|
-
:timeprimitive, :styleUrl, :styleSelector, :region, :metadata
|
573
|
-
|
574
|
-
attr_reader :addressDetails, :snippet
|
639
|
+
:phoneNumber, :description, :abstractView, :styles,
|
640
|
+
:timeprimitive, :styleUrl, :styleSelector, :region, :metadata
|
641
|
+
attr_reader :addressDetails, :snippet, :extendedData
|
575
642
|
|
576
643
|
include Snippet
|
577
644
|
|
@@ -583,6 +650,11 @@ module Kamelopard
|
|
583
650
|
@name = name unless name.nil?
|
584
651
|
end
|
585
652
|
|
653
|
+
def extendedData=(a)
|
654
|
+
raise "extendedData attribute must respond to the 'each' method" unless a.respond_to? :each
|
655
|
+
@extendedData = a
|
656
|
+
end
|
657
|
+
|
586
658
|
def styles=(a)
|
587
659
|
if a.is_a? Array then
|
588
660
|
@styles = a
|
@@ -665,11 +737,11 @@ module Kamelopard
|
|
665
737
|
[@description, 'description'],
|
666
738
|
[@styleUrl, 'styleUrl'],
|
667
739
|
[@styleSelector, lambda { |o| @styleSelector.to_kml(o) }],
|
668
|
-
[@metadata, 'Metadata' ]
|
669
|
-
[@extendedData, 'ExtendedData' ]
|
740
|
+
[@metadata, 'Metadata' ]
|
670
741
|
])
|
671
742
|
styles_to_kml(elem)
|
672
743
|
snippet_to_kml(elem) unless @snippet_text.nil?
|
744
|
+
extended_data_to_kml(elem) unless @extendedData.nil?
|
673
745
|
@abstractView.to_kml(elem) unless @abstractView.nil?
|
674
746
|
@timeprimitive.to_kml(elem) unless @timeprimitive.nil?
|
675
747
|
@region.to_kml(elem) unless @region.nil?
|
@@ -677,9 +749,18 @@ module Kamelopard
|
|
677
749
|
elem
|
678
750
|
end
|
679
751
|
|
752
|
+
def extended_data_to_kml(elem)
|
753
|
+
v = XML::Node.new 'ExtendedData'
|
754
|
+
@extendedData.each do |f|
|
755
|
+
v << f.to_kml
|
756
|
+
end
|
757
|
+
elem << v unless elem.nil?
|
758
|
+
v
|
759
|
+
end
|
760
|
+
|
680
761
|
def styles_to_kml(elem)
|
681
762
|
@styles.each do |a|
|
682
|
-
a.to_kml(elem)
|
763
|
+
a.to_kml(elem) unless a.attached?
|
683
764
|
end
|
684
765
|
end
|
685
766
|
end
|
@@ -767,27 +848,26 @@ module Kamelopard
|
|
767
848
|
# scripts can (for now) manage only one Document at a time.
|
768
849
|
class Document < Container
|
769
850
|
include Singleton
|
770
|
-
attr_accessor :flyto_mode, :
|
851
|
+
attr_accessor :flyto_mode, :folders, :tours, :uses_xal
|
771
852
|
|
772
853
|
def initialize(options = {})
|
773
|
-
@
|
774
|
-
@
|
775
|
-
@doc_styles = []
|
854
|
+
@tours = []
|
855
|
+
@folders = []
|
776
856
|
super
|
777
857
|
end
|
778
858
|
|
779
859
|
# Returns the current Tour object
|
780
860
|
def tour
|
781
|
-
Tour.new if @
|
782
|
-
@
|
861
|
+
Tour.new if @tours.length == 0
|
862
|
+
@tours.last
|
783
863
|
end
|
784
864
|
|
785
865
|
# Returns the current Folder object
|
786
866
|
def folder
|
787
|
-
if @
|
867
|
+
if @folders.size == 0 then
|
788
868
|
Folder.new
|
789
869
|
end
|
790
|
-
@
|
870
|
+
@folders.last
|
791
871
|
end
|
792
872
|
|
793
873
|
def get_kml_document
|
@@ -813,15 +893,16 @@ module Kamelopard
|
|
813
893
|
super d
|
814
894
|
|
815
895
|
# Print styles first
|
816
|
-
|
896
|
+
#! These get printed out in the call to super, in Feature.to_kml()
|
897
|
+
#@styles.map do |a| d << a.to_kml unless a.attached? end
|
817
898
|
|
818
899
|
# then folders
|
819
|
-
@
|
900
|
+
@folders.map do |a|
|
820
901
|
a.to_kml(d) unless a.has_parent?
|
821
902
|
end
|
822
903
|
|
823
904
|
# then tours
|
824
|
-
@
|
905
|
+
@tours.map do |a| a.to_kml(d) end
|
825
906
|
|
826
907
|
d
|
827
908
|
end
|
@@ -949,12 +1030,12 @@ module Kamelopard
|
|
949
1030
|
attr_accessor :href, :x, :y, :w, :h, :refreshMode, :refreshInterval, :viewRefreshMode, :viewRefreshTime, :viewBoundScale, :viewFormat, :httpQuery
|
950
1031
|
|
951
1032
|
def href=(h)
|
952
|
-
@icon_id = "Icon_#{Kamelopard.get_next_id}" if @icon_id.nil?
|
1033
|
+
@icon_id = "#{Kamelopard.id_prefix}Icon_#{Kamelopard.get_next_id}" if @icon_id.nil?
|
953
1034
|
@href = h
|
954
1035
|
end
|
955
1036
|
|
956
1037
|
def icon_to_kml(elem = nil)
|
957
|
-
@icon_id = "Icon_#{Kamelopard.get_next_id}" if @icon_id.nil?
|
1038
|
+
@icon_id = "#{Kamelopard.id_prefix}Icon_#{Kamelopard.get_next_id}" if @icon_id.nil?
|
958
1039
|
k = XML::Node.new 'Icon'
|
959
1040
|
k.attributes['id'] = @icon_id
|
960
1041
|
Kamelopard.kml_array(k, [
|
@@ -1145,7 +1226,7 @@ module Kamelopard
|
|
1145
1226
|
def initialize(options = {})
|
1146
1227
|
super
|
1147
1228
|
@attached = false
|
1148
|
-
Document.instance.
|
1229
|
+
Document.instance.styles << self
|
1149
1230
|
end
|
1150
1231
|
|
1151
1232
|
def attached?
|
@@ -1261,6 +1342,8 @@ module Kamelopard
|
|
1261
1342
|
def point
|
1262
1343
|
if @geometry.kind_of? Point then
|
1263
1344
|
@geometry
|
1345
|
+
elsif @geometry.respond_to? :point then
|
1346
|
+
@geometry.point
|
1264
1347
|
else
|
1265
1348
|
raise "This placemark uses a non-point geometry, but the operation you're trying requires a point object"
|
1266
1349
|
end
|
@@ -1291,6 +1374,8 @@ module Kamelopard
|
|
1291
1374
|
def view=(view)
|
1292
1375
|
if view.kind_of? AbstractView then
|
1293
1376
|
@view = view
|
1377
|
+
elsif view.respond_to? :abstractView then
|
1378
|
+
@view = view.abstractView
|
1294
1379
|
else
|
1295
1380
|
@view = LookAt.new view
|
1296
1381
|
end
|
@@ -1684,11 +1769,8 @@ module Kamelopard
|
|
1684
1769
|
def initialize(icon, options = {})
|
1685
1770
|
@altitude = 0
|
1686
1771
|
@altitudeMode = :clampToGround
|
1772
|
+
@href = icon
|
1687
1773
|
super options
|
1688
|
-
@latlonbox = latlonbox
|
1689
|
-
@latlonquad = latlonquad
|
1690
|
-
@altitude = altitude
|
1691
|
-
@altitudeMode = altitudeMode
|
1692
1774
|
end
|
1693
1775
|
|
1694
1776
|
def to_kml(elem = nil)
|
@@ -1932,6 +2014,7 @@ module Kamelopard
|
|
1932
2014
|
@extrude = 0
|
1933
2015
|
@altitudeMode = :clampToGround
|
1934
2016
|
@inner = []
|
2017
|
+
@outer = outer
|
1935
2018
|
super options
|
1936
2019
|
end
|
1937
2020
|
|
@@ -2039,5 +2122,31 @@ module Kamelopard
|
|
2039
2122
|
end
|
2040
2123
|
end
|
2041
2124
|
|
2125
|
+
class Track < Geometry
|
2126
|
+
attr_accessor :altitudeMode, :when, :coord, :angles, :model
|
2127
|
+
def initialize(options = {})
|
2128
|
+
@when = []
|
2129
|
+
@coord = []
|
2130
|
+
@angles = []
|
2131
|
+
super
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
def to_kml(elem = nil)
|
2135
|
+
e = XML::Node.new 'gx:Track'
|
2136
|
+
[
|
2137
|
+
[ @coord, 'gx:coord' ],
|
2138
|
+
[ @when, 'when' ],
|
2139
|
+
[ @angles, 'gx:angles' ],
|
2140
|
+
].each do |a|
|
2141
|
+
a[0].each do |g|
|
2142
|
+
w = XML::Node.new a[1], g.to_s
|
2143
|
+
e << w
|
2144
|
+
end
|
2145
|
+
end
|
2146
|
+
elem << e unless elem.nil?
|
2147
|
+
e
|
2148
|
+
end
|
2149
|
+
end
|
2150
|
+
|
2042
2151
|
end
|
2043
2152
|
# End of Kamelopard module
|
data/lib/kamelopard/functions.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# vim:ts=4:sw=4:et:smartindent:nowrap
|
2
2
|
def fly_to(p, d = 0, r = 100, m = nil)
|
3
3
|
m = Kamelopard::Document.instance.flyto_mode if m.nil?
|
4
|
-
Kamelopard::FlyTo.new
|
4
|
+
Kamelopard::FlyTo.new p, :range => r, :duration => d, :mode => m
|
5
5
|
end
|
6
6
|
|
7
7
|
def set_flyto_mode_to(a)
|
@@ -15,7 +15,7 @@ def mod_popup_for(p, v)
|
|
15
15
|
end
|
16
16
|
a = XML::Node.new 'Change'
|
17
17
|
b = XML::Node.new 'Placemark'
|
18
|
-
b.attributes['targetId'] = p.
|
18
|
+
b.attributes['targetId'] = p.kml_id
|
19
19
|
c = XML::Node.new 'visibility'
|
20
20
|
c << XML::Node.new_text(v.to_s)
|
21
21
|
b << c
|
@@ -32,7 +32,8 @@ def show_popup_for(p)
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def point(lo, la, alt=0, mode=nil, extrude = false)
|
35
|
-
|
35
|
+
m = ( mode.nil? ? :clampToGround : mode )
|
36
|
+
Kamelopard::Point.new(lo, la, alt, :altitudeMode => m, :extrude => extrude)
|
36
37
|
end
|
37
38
|
|
38
39
|
# Returns the KML that makes up the current Kamelopard::Document, as a string.
|
@@ -58,6 +59,7 @@ end
|
|
58
59
|
|
59
60
|
def name_folder(a)
|
60
61
|
Kamelopard::Document.instance.folder.name = a
|
62
|
+
return Kamelopard::Document.instance.folder
|
61
63
|
end
|
62
64
|
|
63
65
|
def zoom_out(dist = 1000, dur = 0, mode = nil)
|
@@ -127,3 +129,155 @@ end
|
|
127
129
|
# fly_to
|
128
130
|
# end
|
129
131
|
# end
|
132
|
+
|
133
|
+
def set_prefix_to(a)
|
134
|
+
Kamelopard.id_prefix = a
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_kml_to(file = 'doc.kml')
|
138
|
+
File.open(file, 'w') do |f| f.write get_kml.to_s.gsub(/balloonVis/, 'gx:balloonVis') end
|
139
|
+
end
|
140
|
+
|
141
|
+
def fade_overlay(ov, show, options = {})
|
142
|
+
color = '00ffffff'
|
143
|
+
color = 'ffffffff' if show
|
144
|
+
if ov.is_a? String then
|
145
|
+
id = ov
|
146
|
+
else
|
147
|
+
id = ov.kml_id
|
148
|
+
end
|
149
|
+
k = Kamelopard::AnimatedUpdate.new "<Change><ScreenOverlay targetId=\"#{id}\"><color>#{color}</color></ScreenOverlay></Change>", options
|
150
|
+
k
|
151
|
+
end
|
152
|
+
|
153
|
+
def mod_balloon_for(a, val)
|
154
|
+
c = a.change('gx:balloonVisibility', val).to_s
|
155
|
+
STDERR.puts c
|
156
|
+
Kamelopard::AnimatedUpdate.new c
|
157
|
+
end
|
158
|
+
|
159
|
+
def show_balloon_for(a)
|
160
|
+
Kamelopard::AnimatedUpdate.new %{<Change><Placemark targetId="#{a.kml_id}"><balloonVisibility>1</balloonVisibility></Placemark></Change>}
|
161
|
+
end
|
162
|
+
|
163
|
+
def hide_balloon_for(a)
|
164
|
+
Kamelopard::AnimatedUpdate.new %{<Change><Placemark targetId="#{a.kml_id}"><balloonVisibility>0</balloonVisibility></Placemark></Change>}
|
165
|
+
end
|
166
|
+
|
167
|
+
module TelemetryProcessor
|
168
|
+
Pi = 3.1415926535
|
169
|
+
|
170
|
+
def TelemetryProcessor.get_heading(p)
|
171
|
+
x1, y1, x2, y2 = [ p[1][0], p[1][1], p[2][0], p[2][1] ]
|
172
|
+
|
173
|
+
h = Math.atan((x2-x1) / (y2-y1)) * 180 / Pi
|
174
|
+
h = h + 180.0 if y2 < y1
|
175
|
+
h
|
176
|
+
end
|
177
|
+
|
178
|
+
def TelemetryProcessor.get_dist2(x1, y1, x2, y2)
|
179
|
+
Math.sqrt( (x2 - x1)**2 + (y2 - y1)**2).abs
|
180
|
+
end
|
181
|
+
|
182
|
+
def TelemetryProcessor.get_dist3(x1, y1, z1, x2, y2, z2)
|
183
|
+
Math.sqrt( (x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2 ).abs
|
184
|
+
end
|
185
|
+
|
186
|
+
def TelemetryProcessor.get_tilt(p)
|
187
|
+
x1, y1, z1, x2, y2, z2 = [ p[1][0], p[1][1], p[1][2], p[2][0], p[2][1], p[2][2] ]
|
188
|
+
smoothing_factor = 10.0
|
189
|
+
dist = get_dist3(x1, y1, z1, x2, y2, z2)
|
190
|
+
dist = dist + 1
|
191
|
+
# + 1 to avoid setting dist to 0, and having div-by-0 errors later
|
192
|
+
t = Math.atan((z2 - z1) / dist) * 180 / Pi / @@options[:exaggerate]
|
193
|
+
# the / 2.0 is just because it looked nicer that way
|
194
|
+
90.0 + t
|
195
|
+
end
|
196
|
+
|
197
|
+
# roll = get_roll(last_last_lon, last_last_lat, last_lon, last_lat, lon, lat)
|
198
|
+
def TelemetryProcessor.get_roll(p)
|
199
|
+
x1, y1, x2, y2, x3, y3 = [ p[0][0], p[0][1], p[1][0], p[1][1], p[2][0], p[2][1] ]
|
200
|
+
return 0 if x1.nil? or x2.nil?
|
201
|
+
|
202
|
+
# Measure roll based on angle between P1 -> P2 and P2 -> P3. To be really
|
203
|
+
# exact I ought to take into account altitude as well, but ... I don't want
|
204
|
+
# to
|
205
|
+
|
206
|
+
# Set x2, y2 as the origin
|
207
|
+
xn1 = x1 - x2
|
208
|
+
xn3 = x3 - x2
|
209
|
+
yn1 = y1 - y2
|
210
|
+
yn3 = y3 - y2
|
211
|
+
|
212
|
+
# Use dot product to get the angle between the two segments
|
213
|
+
angle = Math.acos( ((xn1 * xn3) + (yn1 * yn3)) / (get_dist2(0, 0, xn1, yn1).abs * get_dist2(0, 0, xn3, yn3).abs) ) * 180 / Pi
|
214
|
+
|
215
|
+
# angle = angle > 90 ? 90 : angle
|
216
|
+
@@options[:exaggerate] * (angle - 180)
|
217
|
+
end
|
218
|
+
|
219
|
+
def TelemetryProcessor.fix_coord(a)
|
220
|
+
a = a - 360 if a > 180
|
221
|
+
a = a + 360 if a < -180
|
222
|
+
a
|
223
|
+
end
|
224
|
+
|
225
|
+
def TelemetryProcessor.add_flyto(p)
|
226
|
+
# p is an array of three points, where p[0] is the earliest. Each point is itself an array of [longitude, latitude, altitude].
|
227
|
+
p2 = TelemetryProcessor::normalize_points p
|
228
|
+
p = p2
|
229
|
+
heading = get_heading p
|
230
|
+
tilt = get_tilt p
|
231
|
+
# roll = get_roll(last_last_lon, last_last_lat, last_lon, last_lat, lon, lat)
|
232
|
+
roll = get_roll p
|
233
|
+
#p = Kamelopard::Point.new last_lon, last_lat, last_alt, { :altitudeMode => :absolute }
|
234
|
+
point = Kamelopard::Point.new p[1][0], p[1][1], p[1][2], { :altitudeMode => :absolute }
|
235
|
+
c = Kamelopard::Camera.new point, { :heading => heading, :tilt => tilt, :roll => roll, :altitudeMode => :absolute }
|
236
|
+
f = Kamelopard::FlyTo.new c, { :duration => @@options[:pause], :mode => :smooth }
|
237
|
+
f.comment = "#{p[1][0]} #{p[1][1]} #{p[1][2]} to #{p[2][0]} #{p[2][1]} #{p[2][2]}"
|
238
|
+
end
|
239
|
+
|
240
|
+
def TelemetryProcessor.options=(a)
|
241
|
+
@@options = a
|
242
|
+
end
|
243
|
+
|
244
|
+
def TelemetryProcessor.normalize_points(p)
|
245
|
+
# The whole point here is to prevent problems when you cross the poles or the dateline
|
246
|
+
# This could have serious problems if points are really far apart, like
|
247
|
+
# hundreds of degrees. This seems unlikely.
|
248
|
+
lons = ((0..2).collect { |i| p[i][0] })
|
249
|
+
lats = ((0..2).collect { |i| p[i][1] })
|
250
|
+
|
251
|
+
lon_min, lon_max = lons.minmax
|
252
|
+
lat_min, lat_max = lats.minmax
|
253
|
+
|
254
|
+
if (lon_max - lon_min).abs > 200 then
|
255
|
+
(0..2).each do |i|
|
256
|
+
lons[i] += 360.0 if p[i][0] < 0
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
if (lat_max - lat_min).abs > 200 then
|
261
|
+
(0..2).each do |i|
|
262
|
+
lats[i] += 360.0 if p[i][1] < 0
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
return [
|
267
|
+
[ lons[0], lats[0], p[0][2] ],
|
268
|
+
[ lons[1], lats[1], p[1][2] ],
|
269
|
+
[ lons[2], lats[2], p[2][2] ],
|
270
|
+
]
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def tour_from_points(points, options = {})
|
275
|
+
options.merge!({
|
276
|
+
:pause => 1,
|
277
|
+
:exaggerate => 1
|
278
|
+
}) { |key, old, new| old }
|
279
|
+
TelemetryProcessor.options = options
|
280
|
+
(0..(points.size-3)).each do |i|
|
281
|
+
TelemetryProcessor::add_flyto points[i,3]
|
282
|
+
end
|
283
|
+
end
|
data/lib/kamelopard/pointlist.rb
CHANGED
@@ -59,14 +59,12 @@ class NDPointList
|
|
59
59
|
raise "NDPointList of size #{@dim} has no Z element"
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
def each(&blk)
|
64
64
|
@points.each(&blk)
|
65
65
|
end
|
66
66
|
|
67
|
-
def interpolate(resolution =
|
68
|
-
# XXX Figure out how to implement the "resolution" argument
|
69
|
-
STDERR.puts "resolution argument to NDPointList.interpolate is ignored" if resolution.nil?
|
67
|
+
def interpolate(resolution = [10])
|
70
68
|
# Ruby implementation of Catmull-Rom splines (http://www.cubic.org/docs/hermite.htm)
|
71
69
|
# Return NDPointList interpolating a path along all points in this list
|
72
70
|
|
@@ -79,6 +77,9 @@ class NDPointList
|
|
79
77
|
|
80
78
|
result = NDPointList.new(@dim)
|
81
79
|
|
80
|
+
idx = 0
|
81
|
+
resolution = [resolution] if ! resolution.respond_to? []
|
82
|
+
|
82
83
|
# Calculate spline between every two points
|
83
84
|
(0..(self.size-2)).each do |i|
|
84
85
|
p1 = self[i]
|
@@ -96,13 +97,16 @@ class NDPointList
|
|
96
97
|
c = Matrix[p1, p2, t1.row(0), t2.row(0)]
|
97
98
|
|
98
99
|
# Make a set of points
|
99
|
-
(0
|
100
|
+
point_count = (resolution[idx] * 1.0 / self.size).to_i
|
101
|
+
(0..point_count).each do |t|
|
100
102
|
r = t/10.0
|
101
103
|
s = Matrix[[r**3, r**2, r, 1]]
|
102
104
|
tmp = s * h
|
103
105
|
p = tmp * c
|
104
106
|
result << p.row(0).to_a
|
105
107
|
end
|
108
|
+
idx += 1
|
109
|
+
idx = 0 if idx >= resolution.size
|
106
110
|
end
|
107
111
|
result
|
108
112
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kamelopard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Tolley
|
@@ -16,8 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2012-
|
20
|
-
default_executable:
|
19
|
+
date: 2012-05-04 00:00:00 Z
|
21
20
|
dependencies: []
|
22
21
|
|
23
22
|
description: Various classes and functions used to ease development of KML files, in particular for development of Google Earth tours
|
@@ -36,7 +35,6 @@ files:
|
|
36
35
|
- lib/kamelopard/functions.rb
|
37
36
|
- lib/kamelopard/pointlist.rb
|
38
37
|
- lib/kamelopard.rb
|
39
|
-
has_rdoc: true
|
40
38
|
homepage: http://www.endpoint.com/services/liquid_galaxy
|
41
39
|
licenses: []
|
42
40
|
|
@@ -66,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
64
|
requirements: []
|
67
65
|
|
68
66
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.
|
67
|
+
rubygems_version: 1.8.21
|
70
68
|
signing_key:
|
71
69
|
specification_version: 3
|
72
70
|
summary: Tools for building KML
|