kamelopard 0.0.5 → 0.0.6
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/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
|