kamelopard 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/lib/kamelopard/classes.rb +69 -7
- data/lib/kamelopard/geocode.rb +57 -16
- data/lib/kamelopard/helpers.rb +834 -720
- data/lib/kamelopard/multicamify.rb +2 -0
- data/lib/kamelopard/spline.rb +99 -1
- data/spec/test.rb +2999 -0
- metadata +17 -15
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZTY5Y2M5MWMyYWE4MGUyNTgxM2VlNzljMzYzMTJlYjkzZmU0NWYxZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
M2Y2MmIwNDk4YzIwNGZkMjdjMDVhOTlmYzZlMGMyNzM4N2QxMTJhMg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OWNkNGFjMGU0NjY3ZTU2MzUxYTg3NWUzOGNiZmI0ZDhmYTY5MTM4NTM2ODhj
|
10
|
+
ZmJmNDRhZmYzNzA1OTdjOTMzYTYxZGY4Y2RlMmIzY2ZiMTRiNWVkNmQ4ODU1
|
11
|
+
NmYyMTUzODU4NzA0Zjc5NWNhMmU4ZGM5NWJiNmExODRkN2YwZGU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NGY5ZTE0NGQyNzA1OTg4NjRlZWIwNzgwMDRlYzNkZDc5ZTIyZWQ5YmE4NjUz
|
14
|
+
MWY4NjEwMWMzMGUyMzBlNDFhMzc3Yjc1ZTc5MWE0NjIwYjZmZjM0OTJhNTg5
|
15
|
+
ZjJmZGQxNDkzNTU3MmYzMDliZTg0ZGEwNmFhNmI5ZDIyMzcwYTE=
|
data/lib/kamelopard/classes.rb
CHANGED
@@ -2,11 +2,53 @@
|
|
2
2
|
#--
|
3
3
|
# vim:ts=4:sw=4:et:smartindent:nowrap
|
4
4
|
#++
|
5
|
-
# Classes to manage various KML objects. See
|
6
|
-
# http://code.google.com/apis/kml/documentation/kmlreference.html for a
|
7
|
-
# description of KML
|
8
5
|
|
9
|
-
#
|
6
|
+
# == Description
|
7
|
+
# Kamelopard is a gem to create and manipulate Keyhole Markup Language, or KML.
|
8
|
+
# See http://code.google.com/apis/kml/documentation/kmlreference.html for a
|
9
|
+
# reference on the subject. Within the Kamelopard module are several classes
|
10
|
+
# which represent KML objects. To use Kamelopard to create a KML document from
|
11
|
+
# scratch, you'll eventually want to create instances of these classes. You can
|
12
|
+
# manipulate these instances' attributes and connections to other classes, and
|
13
|
+
# in the end, write the result to a KML file. Here's a simple example which
|
14
|
+
# creates one placemark in a file called doc.kml:
|
15
|
+
#
|
16
|
+
# require 'rubygems'
|
17
|
+
# require 'kamelopard'
|
18
|
+
#
|
19
|
+
# f = Kamelopard::Folder.new('Some Folder Name')
|
20
|
+
# f << Kamelopard::Placemark('My placemark',
|
21
|
+
# :geometry => Kamelopard::Point(1, 2, 3))
|
22
|
+
# write_kml_to 'doc.kml'
|
23
|
+
#
|
24
|
+
# Kamelopard operates as a sort of state machine, with a concept of the
|
25
|
+
# "current" document. Newly created objects are appended to the current
|
26
|
+
# document. See the DocumentHolder class to learn about switching around
|
27
|
+
# between different documents. Note also that whereas tour elements such as
|
28
|
+
# FlyTo and Wait are always kept in the order in which they were created, other
|
29
|
+
# elements like Styles and Folders might show up in different orders. Since in
|
30
|
+
# the final output this doesn't matter (or shouldn't, anyway), this isn't
|
31
|
+
# considered a bug, and in fact it makes it easier sometimes, because you don't
|
32
|
+
# have to figure out the structure of your document in the proper order at run
|
33
|
+
# time.
|
34
|
+
#
|
35
|
+
# Kamelopard comes with a set of helper functions which it puts in the default
|
36
|
+
# namespace. Experienced users will probably choose use these functions to
|
37
|
+
# create Kamelopard objects rather than instantiating these classes directly.
|
38
|
+
#
|
39
|
+
# Objects' attributes are often undocumented, when they correspond directly to
|
40
|
+
# a similarly named attribute in the KML object they represent. Refer to the
|
41
|
+
# KML reference linked above for more details about these undocumented
|
42
|
+
# attributes. Most objects' constructors accept a hash of options you can use
|
43
|
+
# to set these attributes, such as the :geometry option used in the sample code
|
44
|
+
# above. Kamelopard will complain if you try to set an option it doesn't
|
45
|
+
# recognize.
|
46
|
+
#
|
47
|
+
# Most objects also include a to_kml() method, which returns an XML::Node
|
48
|
+
# containing the KML this object represents, including any other objects nested
|
49
|
+
# inside. The write_kml_to helper function used above, and other helper
|
50
|
+
# functions involved in KML output, generally wrap this method so it rarely
|
51
|
+
# gets called directly.
|
10
52
|
module Kamelopard
|
11
53
|
require 'bundler/setup'
|
12
54
|
require 'singleton'
|
@@ -58,7 +100,7 @@ module Kamelopard
|
|
58
100
|
@@logger.call(level, mod, msg) unless @@logger.nil? or @@log_level > LogLevels[level]
|
59
101
|
end
|
60
102
|
|
61
|
-
def Kamelopard.xml_to_hash(node, fields)
|
103
|
+
def Kamelopard.xml_to_hash(node, fields) # :nodoc:
|
62
104
|
result = {}
|
63
105
|
fields.each do |field|
|
64
106
|
begin
|
@@ -1061,7 +1103,7 @@ module Kamelopard
|
|
1061
1103
|
|
1062
1104
|
# Represents KML's Document class.
|
1063
1105
|
class Document < Container
|
1064
|
-
attr_accessor :flyto_mode, :folders, :tours, :uses_xal, :vsr_actions
|
1106
|
+
attr_accessor :flyto_mode, :folders, :tours, :uses_xal, :vsr_actions, :filename
|
1065
1107
|
|
1066
1108
|
# Is this KML destined for a master LG node, or a slave? True if this
|
1067
1109
|
# is a master node. This defaults to true, so tours that don't need
|
@@ -1073,11 +1115,20 @@ module Kamelopard
|
|
1073
1115
|
@folders = []
|
1074
1116
|
@vsr_actions = []
|
1075
1117
|
@master_mode = false
|
1118
|
+
@filename = 'doc.kml'
|
1076
1119
|
Kamelopard.log(:info, 'Document', "Adding myself to the document holder")
|
1077
1120
|
DocumentHolder.instance << self
|
1078
1121
|
super
|
1079
1122
|
end
|
1080
1123
|
|
1124
|
+
def make_current
|
1125
|
+
Kamelopard::DocumentHolder.instance.set_current self
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
def activate
|
1129
|
+
make_current
|
1130
|
+
end
|
1131
|
+
|
1081
1132
|
# Returns viewsyncrelay actions as a hash
|
1082
1133
|
def get_actions
|
1083
1134
|
{
|
@@ -1202,6 +1253,12 @@ module Kamelopard
|
|
1202
1253
|
#! These get printed out in the call to super, in Feature.to_kml()
|
1203
1254
|
#@styles.map do |a| d << a.to_kml unless a.attached? end
|
1204
1255
|
|
1256
|
+
# then misc
|
1257
|
+
@features.each do |f|
|
1258
|
+
next if f.is_a? Folder or f.is_a? Style or f.is_a? Tour
|
1259
|
+
f.to_kml d
|
1260
|
+
end
|
1261
|
+
|
1205
1262
|
# then folders
|
1206
1263
|
@folders.map do |a|
|
1207
1264
|
a.to_kml(d) unless a.has_parent?
|
@@ -1685,7 +1742,10 @@ module Kamelopard
|
|
1685
1742
|
end
|
1686
1743
|
|
1687
1744
|
# Corresponds to KML's Placemark objects. The geometry attribute requires a
|
1688
|
-
# descendant of Geometry
|
1745
|
+
# descendant of Geometry. *Note:* whereas most objects implicitly add
|
1746
|
+
# themselves to the end of the current Document, Placemarks do not. They
|
1747
|
+
# must be explicitly added to the Document or other Container they live in,
|
1748
|
+
# typically with the Container's << operator.
|
1689
1749
|
class Placemark < Feature
|
1690
1750
|
attr_accessor :name, :geometry, :balloonVisibility
|
1691
1751
|
|
@@ -2368,6 +2428,8 @@ module Kamelopard
|
|
2368
2428
|
}.each do |k, v|
|
2369
2429
|
d = XML::Node.new k.to_s
|
2370
2430
|
d << v.to_s
|
2431
|
+
# Don't escape links, because Earth doesn't like that.
|
2432
|
+
d.first.output_escaping = false
|
2371
2433
|
x << d
|
2372
2434
|
end
|
2373
2435
|
Kamelopard.kml_array(x, [
|
data/lib/kamelopard/geocode.rb
CHANGED
@@ -7,22 +7,53 @@ require 'uri'
|
|
7
7
|
require 'cgi'
|
8
8
|
require 'json'
|
9
9
|
|
10
|
-
# Geocoder base class
|
10
|
+
# Geocoder base class. You should use a subclass. It is your responsibility to
|
11
|
+
# ensure you comply with all API licensing and usage requirements. Kamelopard
|
12
|
+
# won't do it for you.
|
11
13
|
class Geocoder
|
12
14
|
attr_accessor :host, :path, :params
|
13
15
|
def initialize
|
14
16
|
@params = {}
|
15
17
|
end
|
16
18
|
|
19
|
+
def processParam(value, key = 'location')
|
20
|
+
return ["#{key}=#{value}"] if key == 'key'
|
21
|
+
if value.kind_of? Hash then
|
22
|
+
p = value.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v)}" }
|
23
|
+
else
|
24
|
+
p = ["#{CGI.escape(key)}=#{CGI.escape(value)}"]
|
25
|
+
end
|
26
|
+
return p
|
27
|
+
end
|
28
|
+
|
17
29
|
def parse_response(r)
|
18
30
|
raise "Unimplemented -- use a child of the Geocoder class"
|
19
31
|
end
|
20
32
|
end
|
21
33
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
34
|
+
class GoogleGeocoder < Geocoder
|
35
|
+
attr_reader :api_key, :response_format
|
36
|
+
|
37
|
+
def initialize(key, response_format = 'json')
|
38
|
+
super()
|
39
|
+
@proto = 'http'
|
40
|
+
@host = 'maps.googleapis.com'
|
41
|
+
@path = '/maps/api/geocode'
|
42
|
+
@api_key = key
|
43
|
+
@response_format = response_format
|
44
|
+
end
|
45
|
+
|
46
|
+
def getURL(params)
|
47
|
+
http = Net::HTTP.new(@host)
|
48
|
+
u = URI::HTTP.build([nil, @host, nil, "#{@path}/#{@response_format}", params, nil])
|
49
|
+
|
50
|
+
JSON.parse(Net::HTTP.get u)
|
51
|
+
end
|
52
|
+
|
53
|
+
def lookup(address)
|
54
|
+
getURL(processParam(address, 'address').join('&'))
|
55
|
+
end
|
56
|
+
end
|
26
57
|
|
27
58
|
class MapquestGeocoder < Geocoder
|
28
59
|
attr_reader :api_key, :response_format
|
@@ -31,28 +62,38 @@ class MapquestGeocoder < Geocoder
|
|
31
62
|
super()
|
32
63
|
@proto = 'http'
|
33
64
|
@host = 'www.mapquestapi.com'
|
34
|
-
@path =
|
65
|
+
@path = {
|
66
|
+
:address => '/geocoding/v1/address',
|
67
|
+
:batch => '/geocoding/v1/batch'
|
68
|
+
}
|
35
69
|
@api_key = key
|
36
70
|
@response_format = response_format
|
37
71
|
@params['key'] = @api_key
|
38
72
|
end
|
39
73
|
|
74
|
+
def getURL(args, type)
|
75
|
+
raise "Type (#{type}) must be one of #{ @path.keys.map { |a| ":#{a}" }.join(', ')}" unless @path.has_key? type
|
76
|
+
|
77
|
+
params = @params.map { |k, v| processParam(v, k) }.concat(args).join('&')
|
78
|
+
|
79
|
+
http = Net::HTTP.new(@host)
|
80
|
+
u = URI::HTTP.build([nil, @host, nil, @path[type], params, nil])
|
81
|
+
|
82
|
+
resp = Net::HTTP.get u
|
83
|
+
parse_response resp
|
84
|
+
end
|
85
|
+
|
40
86
|
# Returns an object built from the JSON result of the lookup, or an exception
|
41
87
|
def lookup(address)
|
42
88
|
# The argument can be a string, in which case PlaceFinder does the parsing
|
43
89
|
# The argument can also be a hash, with several possible keys. See the PlaceFinder documentation for details
|
44
90
|
# http://developer.yahoo.com/geo/placefinder/guide/requests.html
|
45
|
-
|
46
|
-
|
47
|
-
p = @params.merge address
|
48
|
-
else
|
49
|
-
p = @params.merge( { 'location' => address } )
|
50
|
-
end
|
51
|
-
q = p.map { |k,v| "#{ k == 'key' ? k : CGI.escape(k) }=#{ k == 'key' ? v : CGI.escape(v) }" }.join('&')
|
52
|
-
u = URI::HTTP.build([nil, @host, nil, @path, q, nil])
|
91
|
+
return getURL(processParam(address), :address)
|
92
|
+
end
|
53
93
|
|
54
|
-
|
55
|
-
|
94
|
+
def lookupBatch(addresses)
|
95
|
+
# The argument should be an array of the same sorts of values that can be fed to lookup()
|
96
|
+
return getURL(addresses.map { |k| processParam(k) }.flatten(1), :batch)
|
56
97
|
end
|
57
98
|
|
58
99
|
def parse_response(r)
|