kamelopard 0.0.3
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 +1706 -0
- data/lib/kamelopard/functions.rb +119 -0
- data/lib/kamelopard/geocode.rb +62 -0
- data/lib/kamelopard/pointlist.rb +127 -0
- data/lib/kamelopard.rb +3 -0
- metadata +50 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# vim:ts=4:sw=4:et:smartindent:nowrap
|
|
2
|
+
def fly_to(p, d = 0, r = 100, m = nil)
|
|
3
|
+
m = Document.instance.flyto_mode if m.nil?
|
|
4
|
+
FlyTo.new(p, r, d, m)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def set_flyto_mode_to(a)
|
|
8
|
+
Document.instance.flyto_mode = a
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def mod_popup_for(p, v)
|
|
12
|
+
a = AnimatedUpdate.new
|
|
13
|
+
if ! p.is_a? Placemark then
|
|
14
|
+
raise "Can't show popups for things that aren't placemarks"
|
|
15
|
+
end
|
|
16
|
+
a << "<Change><Placemark targetId=\"#{p.id}\"><visibility>#{v}</visibility></Placemark></Change>"
|
|
17
|
+
a
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def hide_popup_for(p)
|
|
21
|
+
mod_popup_for(p, 0)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def show_popup_for(p)
|
|
25
|
+
mod_popup_for(p, 1)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def point(lo, la, alt=0, mode=nil, extrude = false)
|
|
29
|
+
KMLPoint.new(lo, la, alt, mode.nil? ? :clampToGround : mode, extrude)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the KML that makes up the current Document, as a string.
|
|
33
|
+
def get_kml
|
|
34
|
+
Document.instance.to_kml
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pause(p)
|
|
38
|
+
Wait.new p
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def name_tour(a)
|
|
42
|
+
Document.instance.tour.name = a
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def new_folder(name)
|
|
46
|
+
Folder.new(name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def name_folder(a)
|
|
50
|
+
Document.instance.folder.name = a
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def zoom_out(dist = 1000, dur = 0, mode = nil)
|
|
54
|
+
l = Document.instance.tour.last_abs_view
|
|
55
|
+
raise "No current position to zoom out from\n" if l.nil?
|
|
56
|
+
l.range += dist
|
|
57
|
+
FlyTo.new(l, nil, dur, mode)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Creates a list of FlyTo elements to orbit and look at a given point (center),
|
|
61
|
+
# at a given range (in meters), starting and ending at given angles (in
|
|
62
|
+
# degrees) from the center, where 0 and 360 (and -360, and 720, and -980, etc.)
|
|
63
|
+
# are north. To orbit clockwise, make startHeading less than endHeading.
|
|
64
|
+
# Otherwise, it will orbit counter-clockwise. To orbit multiple times, add or
|
|
65
|
+
# subtract 360 from the endHeading. The tilt argument matches the KML LookAt
|
|
66
|
+
# tilt argument
|
|
67
|
+
def orbit(center, range = 100, tilt = 0, startHeading = 0, endHeading = 360)
|
|
68
|
+
fly_to LookAt.new(center, startHeading, tilt, range), 2, nil
|
|
69
|
+
|
|
70
|
+
# We want at least 5 points (arbitrarily chosen value), plus at least 5 for
|
|
71
|
+
# each full revolution
|
|
72
|
+
|
|
73
|
+
# When I tried this all in one step, ruby told me 360 / 10 = 1805. I'm sure
|
|
74
|
+
# there's some reason why this is a feature and not a bug, but I'd rather
|
|
75
|
+
# not look it up right now.
|
|
76
|
+
num = (endHeading - startHeading).abs
|
|
77
|
+
den = ((endHeading - startHeading) / 360.0).to_i.abs * 5 + 5
|
|
78
|
+
step = num / den
|
|
79
|
+
step = 1 if step < 1
|
|
80
|
+
step = step * -1 if startHeading > endHeading
|
|
81
|
+
|
|
82
|
+
lastval = startHeading
|
|
83
|
+
startHeading.step(endHeading, step) do |theta|
|
|
84
|
+
lastval = theta
|
|
85
|
+
fly_to LookAt.new(center, theta, tilt, range), 2, nil, 'smooth'
|
|
86
|
+
end
|
|
87
|
+
if lastval != endHeading then
|
|
88
|
+
fly_to LookAt.new(center, endHeading, tilt, range), 2, nil, 'smooth'
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def sound_cue(href, ds = nil)
|
|
93
|
+
SoundCue.new href, ds
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# XXX This implementation of orbit is trying to do things the hard way, but the code might be useful for other situations where the hard way is the only possible one
|
|
97
|
+
# def orbit(center, range = 100, startHeading = 0, endHeading = 360)
|
|
98
|
+
# p = ThreeDPointList.new()
|
|
99
|
+
#
|
|
100
|
+
# # Figure out how far we're going, and d
|
|
101
|
+
# dist = endHeading - startHeading
|
|
102
|
+
#
|
|
103
|
+
# # We want at least 5 points (arbitrarily chosen value), plus at least 5 for each full revolution
|
|
104
|
+
# step = (endHeading - startHeading) / ((endHeading - startHeading) / 360.0).to_i * 5 + 5
|
|
105
|
+
# startHeading.step(endHeading, step) do |theta|
|
|
106
|
+
# p << KMLPoint.new(
|
|
107
|
+
# center.longitude + Math.cos(theta),
|
|
108
|
+
# center.latitude + Math.sin(theta),
|
|
109
|
+
# center.altitude, center.altitudeMode)
|
|
110
|
+
# end
|
|
111
|
+
# p << KMLPoint.new(
|
|
112
|
+
# center.longitude + Math.cos(endHeading),
|
|
113
|
+
# center.latitude + Math.sin(endHeading),
|
|
114
|
+
# center.altitude, center.altitudeMode)
|
|
115
|
+
#
|
|
116
|
+
# p.interpolate.each do |a|
|
|
117
|
+
# fly_to
|
|
118
|
+
# end
|
|
119
|
+
# end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# vim:ts=4:sw=4:et:smartindent:nowrap
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'cgi'
|
|
6
|
+
require 'json'
|
|
7
|
+
|
|
8
|
+
# Geocoder base class
|
|
9
|
+
class Geocoder
|
|
10
|
+
def initialize
|
|
11
|
+
raise "Unimplemented -- some other class should extend Geocoder and replace this initialize method"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def lookup(address)
|
|
15
|
+
raise "Unimplemented -- some other class should extend Geocoder and replace this lookup method"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Uses Yahoo's PlaceFinder geocoding service: http://developer.yahoo.com/geo/placefinder/guide/requests.html
|
|
20
|
+
# Google's would seem most obvious, but since it requires you to display
|
|
21
|
+
# results on a map, ... I didn't want to have to evaluate other possible
|
|
22
|
+
# restrictions. The argument to the constructor is a PlaceFinder API key, but
|
|
23
|
+
# testing suggests it's actually unnecessary
|
|
24
|
+
class YahooGeocoder < Geocoder
|
|
25
|
+
def initialize(key)
|
|
26
|
+
@api_key = key
|
|
27
|
+
@proto = 'http'
|
|
28
|
+
@host = 'where.yahooapis.com'
|
|
29
|
+
@path = '/geocode'
|
|
30
|
+
@params = { 'appid' => @api_key, 'flags' => 'J' }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns an object built from the JSON result of the lookup, or an exception
|
|
34
|
+
def lookup(address)
|
|
35
|
+
# The argument can be a string, in which case PlaceFinder does the parsing
|
|
36
|
+
# The argument can also be a hash, with several possible keys. See the PlaceFinder documentation for details
|
|
37
|
+
# http://developer.yahoo.com/geo/placefinder/guide/requests.html
|
|
38
|
+
http = Net::HTTP.new(@host)
|
|
39
|
+
if address.kind_of? Hash then
|
|
40
|
+
p = @params.merge address
|
|
41
|
+
else
|
|
42
|
+
p = @params.merge( { 'q' => address } )
|
|
43
|
+
end
|
|
44
|
+
q = p.map { |k,v| "#{ CGI.escape(k) }=#{ CGI.escape(v) }" }.join('&')
|
|
45
|
+
u = URI::HTTP.build([nil, @host, nil, @path, q, nil])
|
|
46
|
+
|
|
47
|
+
resp = Net::HTTP.get u
|
|
48
|
+
parse_response resp
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def parse_response(resp)
|
|
52
|
+
d = JSON.parse(resp)
|
|
53
|
+
raise d['ErrorMessage'] if d['Error'].to_i != 0
|
|
54
|
+
d
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# EXAMPLE
|
|
59
|
+
# require 'rubygems'
|
|
60
|
+
# require 'kamelopard'
|
|
61
|
+
# g = YahooGeocoder.new('some-api-key')
|
|
62
|
+
# puts g.lookup({ 'city' => 'Springfield', 'count' => '100' })
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# vim:ts=4:sw=4:et:smartindent:nowrap
|
|
2
|
+
require 'matrix'
|
|
3
|
+
#require 'kamelopard_classes'
|
|
4
|
+
|
|
5
|
+
class NDPointList
|
|
6
|
+
# Contains a list of N-dimensional numeric arrays
|
|
7
|
+
|
|
8
|
+
attr_reader :dim
|
|
9
|
+
|
|
10
|
+
def initialize(num)
|
|
11
|
+
raise "Can't have an NDPointList with #{num} dimensions -- must be 1 or more" if num < 1
|
|
12
|
+
@dim = num
|
|
13
|
+
@points = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def size
|
|
17
|
+
return @points.size
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def <<(a)
|
|
21
|
+
# Append points to our list
|
|
22
|
+
if a.kind_of? KMLPoint then
|
|
23
|
+
if self.dim == 3 then
|
|
24
|
+
@points << [a.longitude, a.latitude, a.altitude]
|
|
25
|
+
else
|
|
26
|
+
@points << [a.longitude, a.latitude]
|
|
27
|
+
end
|
|
28
|
+
elsif a.respond_to? 'dim' and @dim != a.dim then
|
|
29
|
+
raise "Argument's dimension #{a.dim} must agree with our dimension #{@dim} to append to an NDPointList"
|
|
30
|
+
else
|
|
31
|
+
@points << a
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def last
|
|
36
|
+
@points.last
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def [](i)
|
|
40
|
+
@points[i]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def x
|
|
44
|
+
@points.collect do |a| a[0] end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def y
|
|
48
|
+
if @dim >= 2 then
|
|
49
|
+
@points.collect do |a| a[1] end
|
|
50
|
+
else
|
|
51
|
+
raise "NDPointList of size #{@dim} has no Y element"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def z
|
|
56
|
+
if @dim >= 2 then
|
|
57
|
+
@points.collect do |a| a[2] end
|
|
58
|
+
else
|
|
59
|
+
raise "NDPointList of size #{@dim} has no Z element"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def each(&blk)
|
|
64
|
+
@points.each(&blk)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def interpolate(resolution = nil)
|
|
68
|
+
# XXX Figure out how to implement the "resolution" argument
|
|
69
|
+
STDERR.puts "resolution argument to NDPointList.interpolate is ignored" if resolution.nil?
|
|
70
|
+
# Ruby implementation of Catmull-Rom splines (http://www.cubic.org/docs/hermite.htm)
|
|
71
|
+
# Return NDPointList interpolating a path along all points in this list
|
|
72
|
+
|
|
73
|
+
h = Matrix[
|
|
74
|
+
[ 2, -2, 1, 1 ],
|
|
75
|
+
[-3, 3, -2, -1 ],
|
|
76
|
+
[ 0, 0, 1, 0 ],
|
|
77
|
+
[ 1, 0, 0, 0 ],
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
result = NDPointList.new(@dim)
|
|
81
|
+
|
|
82
|
+
# Calculate spline between every two points
|
|
83
|
+
(0..(self.size-2)).each do |i|
|
|
84
|
+
p1 = self[i]
|
|
85
|
+
p2 = self[i+1]
|
|
86
|
+
|
|
87
|
+
# Get surrounding points for calculating tangents
|
|
88
|
+
if i <= 0 then pt1 = p1 else pt1 = self[i-1] end
|
|
89
|
+
if i == self.size - 2 then pt2 = p2 else pt2 = self[i+2] end
|
|
90
|
+
|
|
91
|
+
# Build tangent points into matrices to calculate tangents.
|
|
92
|
+
t1 = 0.5 * ( Matrix[p2] - Matrix[pt1] )
|
|
93
|
+
t2 = 0.5 * ( Matrix[pt2] - Matrix[p1] )
|
|
94
|
+
|
|
95
|
+
# Build matrix of Hermite parameters
|
|
96
|
+
c = Matrix[p1, p2, t1.row(0), t2.row(0)]
|
|
97
|
+
|
|
98
|
+
# Make a set of points
|
|
99
|
+
(0..10).each do |t|
|
|
100
|
+
r = t/10.0
|
|
101
|
+
s = Matrix[[r**3, r**2, r, 1]]
|
|
102
|
+
tmp = s * h
|
|
103
|
+
p = tmp * c
|
|
104
|
+
result << p.row(0).to_a
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
result
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class OneDPointList < NDPointList
|
|
112
|
+
def initialize
|
|
113
|
+
super 1
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class TwoDPointList < NDPointList
|
|
118
|
+
def initialize
|
|
119
|
+
super 2
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class ThreeDPointList < NDPointList
|
|
124
|
+
def initialize
|
|
125
|
+
super 3
|
|
126
|
+
end
|
|
127
|
+
end
|
data/lib/kamelopard.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kamelopard
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.3
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Joshua Tolley
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2011-08-24 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: Various classes and functions used to ease development of KML files,
|
|
15
|
+
in particular for development of Google Earth tours
|
|
16
|
+
email: josh@endpoint.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- lib/kamelopard/geocode.rb
|
|
22
|
+
- lib/kamelopard/classes.rb
|
|
23
|
+
- lib/kamelopard/functions.rb
|
|
24
|
+
- lib/kamelopard/pointlist.rb
|
|
25
|
+
- lib/kamelopard.rb
|
|
26
|
+
homepage: http://www.endpoint.com/services/liquid_galaxy
|
|
27
|
+
licenses: []
|
|
28
|
+
post_install_message:
|
|
29
|
+
rdoc_options: []
|
|
30
|
+
require_paths:
|
|
31
|
+
- lib
|
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
requirements: []
|
|
45
|
+
rubyforge_project:
|
|
46
|
+
rubygems_version: 1.8.9
|
|
47
|
+
signing_key:
|
|
48
|
+
specification_version: 3
|
|
49
|
+
summary: Tools for building KML
|
|
50
|
+
test_files: []
|