kamelopard 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,7 @@
8
8
 
9
9
  # Pretty much everything important is in this module
10
10
  module Kamelopard
11
+ require 'bundler/setup'
11
12
  require 'singleton'
12
13
  require 'xml'
13
14
  require 'yaml'
@@ -57,6 +58,26 @@ module Kamelopard
57
58
  @@logger.call(level, mod, msg) unless @@logger.nil? or @@log_level > LogLevels[level]
58
59
  end
59
60
 
61
+ def Kamelopard.xml_to_hash(node, fields)
62
+ result = {}
63
+ fields.each do |field|
64
+ begin
65
+ if field =~ /:/
66
+ xmlfield = field
67
+ else
68
+ xmlfield = "kml:#{field}"
69
+ end
70
+ my_f = node.find("//#{xmlfield}").first
71
+ if ! my_f.nil?
72
+ result[field.to_sym] = my_f.first
73
+ end
74
+ rescue NoMethodError
75
+ log(:debug, 'xml-to-hash', "Error getting field #{field} (#{xmlfileld}) from node")
76
+ end
77
+ end
78
+ result
79
+ end
80
+
60
81
  def Kamelopard.get_next_id # :nodoc:
61
82
  @@sequence += 1
62
83
  @@sequence
@@ -318,6 +339,13 @@ module Kamelopard
318
339
  @altitude = altitude unless altitude.nil?
319
340
  end
320
341
 
342
+ # Converts an XML::Node to a Kamelopard::Point
343
+ def self.parse(p)
344
+ a = Kamelopard.xml_to_hash(p, %w[altitudeMode extrude coordinates])
345
+ (lon, lat, alt) = a[:coordinates].to_s.split(/,\s+/).collect { |a| a.to_f }
346
+ return Point.new(lon, lat, alt, :altitudeMode => a[:altitudeMode].to_s.to_sym, :extrude => a[:extrude].to_s.to_i)
347
+ end
348
+
321
349
  def longitude=(long)
322
350
  @longitude = Kamelopard.convert_coord(long)
323
351
  end
@@ -327,10 +355,13 @@ module Kamelopard
327
355
  end
328
356
 
329
357
  def to_s
330
- p @extrude
331
358
  "Point (#{@longitude}, #{@latitude}, #{@altitude}, mode = #{@altitudeMode}, #{ @extrude == 1 ? 'extruded' : 'not extruded' })"
332
359
  end
333
360
 
361
+ def extrude=(a)
362
+ @extrude = ((a and a != 0) ? 1 : 0)
363
+ end
364
+
334
365
  def to_kml(elem = nil, short = false)
335
366
  e = XML::Node.new 'Point'
336
367
  super(e)
@@ -341,7 +372,7 @@ module Kamelopard
341
372
 
342
373
  if not short then
343
374
  c = XML::Node.new 'extrude'
344
- c << ( @extrude ? 1 : 0 ).to_s
375
+ c << @extrude.to_s
345
376
  e << c
346
377
 
347
378
  Kamelopard.add_altitudeMode(@altitudeMode, e)
@@ -499,7 +530,8 @@ module Kamelopard
499
530
 
500
531
  # Abstract class corresponding to KML's AbstractView object
501
532
  class AbstractView < Object
502
- attr_accessor :timestamp, :timespan, :viewerOptions, :heading, :tilt, :roll, :range, :altitudeMode
533
+ attr_accessor :timestamp, :timespan, :viewerOptions, :heading, :tilt,
534
+ :roll, :range, :altitudeMode
503
535
  attr_reader :className, :point
504
536
 
505
537
  def initialize(className, point, options = {})
@@ -622,13 +654,14 @@ module Kamelopard
622
654
  class Camera < AbstractView
623
655
  def initialize(point = nil, options = {})
624
656
  super('Camera', point, options)
657
+ @roll = 0 if @roll.nil?
625
658
  end
626
659
 
627
660
  def range
628
661
  raise "The range element is part of LookAt objects, not Camera objects"
629
662
  end
630
663
 
631
- def range=
664
+ def range=(a)
632
665
  # The range element doesn't exist in Camera objects
633
666
  end
634
667
  end
@@ -637,6 +670,37 @@ module Kamelopard
637
670
  class LookAt < AbstractView
638
671
  def initialize(point = nil, options = {})
639
672
  super('LookAt', point, options)
673
+ @range = 0 if @range.nil?
674
+ end
675
+
676
+ def self.parse(la)
677
+ # TimePrimitive
678
+ #<!-- inherited from AbstractView element -->
679
+ #<TimePrimitive>...</TimePrimitive> <!-- gx:TimeSpan or gx:TimeStamp -->
680
+ # ViewerOptions
681
+ #<gx:ViewerOptions>
682
+ #<option> name=" " type="boolean"> <!-- name="streetview", "historicalimagery", "sunlight", or "groundnavigation" -->
683
+ #</option>
684
+ #</gx:ViewerOptions>
685
+ fields = %w[
686
+ longitude latitude altitude heading
687
+ tilt range altitudeMode
688
+ gx:TimeStamp gx:TimeSpan gx:ViewerOptions
689
+ ]
690
+ b = Kamelopard.xml_to_hash(la, fields)
691
+ a = {}
692
+ b.each do |k, v|
693
+ if k == :altitudeMode
694
+ a[k] = v.to_s.to_sym
695
+ else
696
+ a[k] = v.to_s.to_f
697
+ end
698
+ end
699
+ return LookAt.new(
700
+ Point.new( a[:longitude], a[:latitude], a[:altitude], :altitudeMode => a[:altitudeMode]),
701
+ :heading => a[:heading],
702
+ :tilt => a[:tilt],
703
+ :range => a[:range])
640
704
  end
641
705
 
642
706
  def roll
@@ -791,8 +855,8 @@ module Kamelopard
791
855
  end
792
856
 
793
857
  def description=(a)
794
- b = CGI.escapeHTML(a)
795
- if b != a then
858
+ b = CGI.escapeHTML(a) if b.is_a? String
859
+ if (! a.is_a? XML::Node) and b != a then
796
860
  @description = XML::Node.new_cdata a
797
861
  else
798
862
  @description = a
@@ -908,9 +972,10 @@ module Kamelopard
908
972
  end
909
973
 
910
974
  def styles_to_kml(elem)
975
+ # XXX Remove this
911
976
  raise "done here" if elem.class == Array
912
977
  @styles.each do |a|
913
- a.to_kml(elem) unless a.attached?
978
+ a.to_kml(elem) # unless a.attached?
914
979
  end
915
980
  end
916
981
  end
@@ -1003,7 +1068,7 @@ module Kamelopard
1003
1068
  # this function, and tours for non-LG targets, work normally.
1004
1069
  attr_accessor :master_mode
1005
1070
 
1006
- def initialize(options = {})
1071
+ def initialize(name = '', options = {})
1007
1072
  @tours = []
1008
1073
  @folders = []
1009
1074
  @vsr_actions = []
@@ -1087,18 +1152,44 @@ module Kamelopard
1087
1152
 
1088
1153
  def get_kml_document
1089
1154
  k = XML::Document.new
1155
+
1156
+ # # XXX Should this be add_namespace instead?
1157
+ # ns_arr = [
1158
+ # ['', 'http://www.opengis.net/kml/2.2'],
1159
+ # ['gx', 'http://www.google.com/kml/ext/2.2'],
1160
+ # ['kml', 'http://www.opengis.net/kml/2.2'],
1161
+ # ['atom', 'http://www.w3.org/2005/Atom'],
1162
+ # ['test', 'http://test.com']
1163
+ # ]
1164
+ # ns_arr.each do |a|
1165
+ # nm = 'xmlns'
1166
+ # nm = a[0] if a[0] != ''
1167
+ # k.context.register_namespace(nm, a[1])
1168
+ # end
1169
+
1090
1170
  # XXX fix this
1091
1171
  #k << XML::XMLDecl.default
1092
1172
  k.root = XML::Node.new('kml')
1093
1173
  r = k.root
1094
- if @uses_xal then
1095
- r.attributes['xmlns:xal'] = "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"
1096
- end
1097
- # XXX Should this be add_namespace instead?
1098
- r.attributes['xmlns'] = 'http://www.opengis.net/kml/2.2'
1099
- r.attributes['xmlns:gx'] = 'http://www.google.com/kml/ext/2.2'
1100
- r.attributes['xmlns:kml'] = 'http://www.opengis.net/kml/2.2'
1101
- r.attributes['xmlns:atom'] = 'http://www.w3.org/2005/Atom'
1174
+ # # XXX Should this be add_namespace instead?
1175
+ # r.attributes['xmlns'] = 'http://www.opengis.net/kml/2.2'
1176
+ # r.attributes['xmlns:gx'] = 'http://www.google.com/kml/ext/2.2'
1177
+ # r.attributes['xmlns:kml'] = 'http://www.opengis.net/kml/2.2'
1178
+ # r.attributes['xmlns:atom'] = 'http://www.w3.org/2005/Atom'
1179
+ ns_values = [
1180
+ [nil, 'http://www.opengis.net/kml/2.2'],
1181
+ ['gx', 'http://www.google.com/kml/ext/2.2'],
1182
+ ['kml', 'http://www.opengis.net/kml/2.2'],
1183
+ ['atom', 'http://www.w3.org/2005/Atom'],
1184
+ ]
1185
+ if @uses_xal
1186
+ ns_values << ['xal', "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"]
1187
+ end
1188
+ ns_values.each do |ns|
1189
+ n = XML::Namespace.new(r, ns[0], ns[1])
1190
+ r.namespaces.namespace = n if ns[0].nil?
1191
+ end
1192
+
1102
1193
  r << self.to_kml
1103
1194
  k
1104
1195
  end
@@ -1152,6 +1243,15 @@ module Kamelopard
1152
1243
  @document_index = a
1153
1244
  end
1154
1245
 
1246
+ def delete_current_doc
1247
+ @documents.delete_at @document_index unless @document_index == -1
1248
+ if @documents.size > 0
1249
+ @document_index = @documents.size - 1
1250
+ else
1251
+ @document_index = -1
1252
+ end
1253
+ end
1254
+
1155
1255
  def current_document
1156
1256
  # Automatically create a Document if we don't already have one
1157
1257
  if @documents.size <= 0
@@ -1196,6 +1296,7 @@ module Kamelopard
1196
1296
 
1197
1297
  def initialize(color = nil, options = {})
1198
1298
  super options
1299
+ @set_colorMode = false
1199
1300
  @color = color unless color.nil?
1200
1301
  end
1201
1302
 
@@ -1205,6 +1306,7 @@ module Kamelopard
1205
1306
 
1206
1307
  def colorMode=(a)
1207
1308
  validate_colorMode a
1309
+ @set_colorMode = true
1208
1310
  @colorMode = a
1209
1311
  end
1210
1312
 
@@ -1246,9 +1348,11 @@ module Kamelopard
1246
1348
  e = XML::Node.new 'color'
1247
1349
  e << @color
1248
1350
  k << e
1249
- e = XML::Node.new 'colorMode'
1250
- e << @colorMode
1251
- k << e
1351
+ if @set_colorMode then
1352
+ e = XML::Node.new 'colorMode'
1353
+ e << @colorMode
1354
+ k << e
1355
+ end
1252
1356
  k
1253
1357
  end
1254
1358
  end
@@ -1655,7 +1759,7 @@ module Kamelopard
1655
1759
 
1656
1760
  def initialize(view = nil, options = {})
1657
1761
  @duration = 0
1658
- @mode = :bounce
1762
+ @mode = Kamelopard::DocumentHolder.instance.current_document.flyto_mode
1659
1763
  super options
1660
1764
  self.view= view unless view.nil?
1661
1765
  end
@@ -1679,6 +1783,8 @@ module Kamelopard
1679
1783
  def to_kml(elem = nil)
1680
1784
  k = XML::Node.new 'gx:FlyTo'
1681
1785
  super k
1786
+
1787
+ #k.namespaces.namespace = XML::Namespaces.new(k, 'gx', 'http://www.google.com/kml/ext/2.2')
1682
1788
  Kamelopard.kml_array(k, [
1683
1789
  [ @duration, 'gx:duration' ],
1684
1790
  [ @mode, 'gx:flyToMode' ]
@@ -1777,6 +1883,8 @@ module Kamelopard
1777
1883
  @duration = duration
1778
1884
  end
1779
1885
 
1886
+ # Accepts an XML node representing a gx:Wait KML object, and turns it
1887
+ # into a Kamelopard::Wait object
1780
1888
  def self.parse(x)
1781
1889
  dur = nil
1782
1890
  id = x.attributes['id'] if x.attributes? 'id'
@@ -3,6 +3,7 @@
3
3
  #++
4
4
  # Describes functions that can be calculated to create flight paths
5
5
 
6
+ require 'bundler/setup'
6
7
  require 'matrix'
7
8
 
8
9
  #--
@@ -8,7 +8,8 @@
8
8
  module Kamelopard
9
9
 
10
10
  # This function creates a hash, then uses that hash to create a point in a
11
- # tour, using make_view_from() among other things.
11
+ # tour, using make_view_from() among other things. It will yield to a code
12
+ # block, if one is provided, as described below.
12
13
  # Arguments:
13
14
  # points: The number of points in the series
14
15
  # hash: Values used to create the hash, which creates the point in the
@@ -20,18 +21,13 @@ module Kamelopard
20
21
  # calculating, starting with 0, and the current value of the hash
21
22
  # created for this point. "duration" represents the time in seconds
22
23
  # spent flying from the last point to this one.
23
- # callback
24
- # This Proc object, if defined, will be called after the other hash
25
- # keys have been calculated. It gets passed the number of the point,
26
- # and the current value of the hash for this point. It can modify and
27
- # return that hash as needed.
28
24
  # callback_value
29
25
  # A placeholder the callback function can use. It can set it when
30
26
  # it's called one time, and see that value when called the next time.
31
27
  # pause
32
- # The amount of time to pause after flying to this point, or nil for no pause
28
+ # The amount of time to pause after flying to each point, or nil for no pause
33
29
  # show_placemarks
34
- # If set, a placemark object will be created at this point
30
+ # If set, a placemark object will be created at each point
35
31
  # no_flyto
36
32
  # If set, on flyto objects will be created
37
33
  # multidim
@@ -75,13 +71,13 @@ module Kamelopard
75
71
 
76
72
  callback_value = nil
77
73
  i = 0
78
- while (i <= points)
74
+ while (i < points)
79
75
  p = i.to_f / points.to_f
80
76
  hash = {}
81
77
  [ :latitude, :longitude, :altitude, :heading,
82
78
  :tilt, :altitudeMode, :extrude, :when,
83
79
  :roll, :range, :pause, :begin, :end, :show_placemarks,
84
- :no_flyto, :pause
80
+ :no_flyto
85
81
  ].each do |k|
86
82
  if options.has_key? k then
87
83
  hash[k] = val(options[k], i, p)
@@ -1,6 +1,7 @@
1
1
  # vim:ts=4:sw=4:et:smartindent:nowrap
2
2
 
3
3
  require 'rubygems'
4
+ require 'bundler/setup'
4
5
  require 'net/http'
5
6
  require 'uri'
6
7
  require 'cgi'
@@ -12,6 +12,13 @@
12
12
  def set_flyto_mode_to(mode)
13
13
  Kamelopard::DocumentHolder.instance.current_document.flyto_mode = mode
14
14
  end
15
+
16
+ # Clears out the document_holder
17
+ def clear_documents
18
+ dh = get_doc_holder
19
+ dh.delete_current_doc while dh.documents.size > 0
20
+ end
21
+
15
22
 
16
23
  # Shows or hides the popup balloon for Placemark and ScreenOverlay objects.
17
24
  # Arguments are the object; 0 or 1 to hide or show the balloon, respectively;
@@ -153,45 +160,88 @@
153
160
  l.range += dist
154
161
  Kamelopard::FlyTo.new(l, nil, dur, mode)
155
162
  end
163
+
164
+ # Translates a heading into something between 0 and 360
165
+ def convert_heading(heading)
166
+ if heading > 360 then
167
+ step = -360
168
+ else
169
+ step = 360
170
+ end
171
+ while heading < 0 or heading > 360 do
172
+ heading = heading + step
173
+ end
174
+ heading
175
+ end
156
176
 
157
- # Creates a list of FlyTo elements to orbit and look at a given point (center),
158
- # at a given range (in meters), starting and ending at given angles (in
159
- # degrees) from the center, where 0 and 360 (and -360, and 720, and -980, etc.)
160
- # are north. To orbit clockwise, make startHeading less than endHeading.
161
- # Otherwise, it will orbit counter-clockwise. To orbit multiple times, add or
162
- # subtract 360 from the endHeading. The tilt argument matches the KML LookAt
163
- # tilt argument
164
- def orbit(center, range = 100, tilt = 90, startHeading = 0, endHeading = 360)
165
- am = center.altitudeMode
166
-
167
- # We want at least 5 points (arbitrarily chosen value), plus at least 5 for
168
- # each full revolution
169
-
170
- # When I tried this all in one step, ruby told me 360 / 10 = 1805. I'm sure
171
- # there's some reason why this is a feature and not a bug, but I'd rather
172
- # not look it up right now.
173
- num = (endHeading - startHeading).abs
174
- den = ((endHeading - startHeading) / 360.0).to_i.abs * 5 + 5
175
- step = num / den
176
- step = 1 if step < 1
177
- step = step * -1 if startHeading > endHeading
178
-
179
- lastval = startHeading
180
- mode = :bounce
181
- startHeading.step(endHeading, step) do |theta|
182
- lastval = theta
183
- fly_to Kamelopard::LookAt.new(center, :heading => theta, :tilt => tilt, :range => range, :altitudeMode => am), :duration => 2, :mode => mode
184
- mode = :smooth
185
- end
186
- if lastval != endHeading then
187
- fly_to Kamelopard::LookAt.new(center, :heading => endHeading, :tilt => tilt, :range => range, :altitudeMode => am), :duration => 2, :mode => :smooth
188
- end
189
- end
177
+ # Creates a list of FlyTo elements to orbit and look at a given point (center),
178
+ # at a given range (in meters), starting and ending at given angles (in
179
+ # degrees) from the center, where 0 and 360 (and -360, and 720, and -980, etc.)
180
+ # are north. To orbit multiple times, add or subtract 360 from the
181
+ # endHeading.
182
+ # The tilt argument matches the KML LookAt tilt argument
183
+ # already_there, if true, means we've already flown to the initial point
184
+ # The options hash can contain:
185
+ # :duration The total duration of the orbit. Defaults to 0, which means it will take 2 seconds per step
186
+ # :step How much to change the heading for each flyto. Defaults to some strange value >= 5
187
+ # :already_there
188
+ # Default false. Indicates that we've already flown to the initial position
189
+ def orbit(center, range = 100, tilt = 90, startHeading = 0, endHeading = 360, options = {})
190
+ duration = options.has_key?(:duration) ? options[:duration] : 0
191
+ step = options.has_key?(:step) ? options[:step] : nil
192
+ already_there = options.has_key?(:already_there) ? options[:already_there] : false
193
+
194
+ am = center.altitudeMode
190
195
 
191
- # Adds a SoundCue object.
192
- def sound_cue(href, ds = nil)
193
- Kamelopard::SoundCue.new href, ds
194
- end
196
+ if not step.nil? then
197
+ if (endHeading - startHeading > 0 and step < 0) or (endHeading - startHeading < 0 and step > 0) then
198
+ raise "Given start = #{startHeading}, end = #{endHeading}, and step = #{step}, this will be an infinite loop"
199
+ end
200
+ end
201
+
202
+ # We want at least 5 points (arbitrarily chosen value), plus at least 5 for
203
+ # each full revolution
204
+
205
+ # When I tried this all in one step, ruby told me 360 / 10 = 1805. I'm sure
206
+ # there's some reason why this is a feature and not a bug, but I'd rather
207
+ # not look it up right now.
208
+ dur = 2
209
+ if step.nil? then
210
+ num = (endHeading - startHeading).abs
211
+ num_steps = ((endHeading - startHeading) / 360.0).to_i.abs * 5 + 5
212
+ step = num / num_steps
213
+ step = 1 if step < 1
214
+ step = step * -1 if startHeading > endHeading
215
+ if already_there
216
+ num_steps = num_steps - 1
217
+ startHeading = startHeading + step
218
+ end
219
+ if duration != 0
220
+ dur = duration.to_f / num_steps
221
+ end
222
+ else
223
+ dur = duration * 1.0 / ((endHeading - startHeading) * 1.0 / step) if duration != 0
224
+ startHeading = startHeading + step if already_there
225
+ end
226
+
227
+ lastval = startHeading
228
+ mode = :bounce
229
+ mode = :smooth if already_there
230
+ startHeading.step(endHeading, step) do |theta|
231
+ lastval = theta
232
+ heading = convert_heading theta
233
+ fly_to Kamelopard::LookAt.new(center, :heading => heading, :tilt => tilt, :range => range, :altitudeMode => am), :duration => dur, :mode => mode
234
+ mode = :smooth
235
+ end
236
+ if lastval != endHeading then
237
+ fly_to Kamelopard::LookAt.new(center, :heading => convert_heading(endHeading), :tilt => tilt, :range => range, :altitudeMode => am), :duration => dur, :mode => :smooth
238
+ end
239
+ end
240
+
241
+ # Adds a SoundCue object.
242
+ def sound_cue(href, ds = nil)
243
+ Kamelopard::SoundCue.new href, ds
244
+ end
195
245
 
196
246
  # 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
197
247
  # def orbit(center, range = 100, startHeading = 0, endHeading = 360)
@@ -505,6 +555,7 @@
505
555
 
506
556
  # Pulls the Placemarks from the KML document d and yields each in turn to the caller
507
557
  # d = an XML::Document containing KML
558
+ # XXX This currently expects Placemarks to contain LookAt or Camera objects. It should do something useful with placemarks based on Points, or other objects
508
559
  def each_placemark(d)
509
560
  d.find('//kml:Placemark').each do |p|
510
561
  all_values = {}
@@ -616,7 +667,9 @@
616
667
 
617
668
  # Generates a series of points in a path that will simulate Earth's FlyTo in
618
669
  # bounce mode, from one view to another. Note that the view objects must be
619
- # the same time: either LookAt, or Camera
670
+ # the same time: either LookAt, or Camera. Options include :no_flyto and
671
+ # :show_placemarks, and match make_function_path's meanings for those
672
+ # options
620
673
  #--
621
674
  # XXX Fix the limitation that the views must be the same type
622
675
  # XXX Make it slow down a bit toward the end of the run
@@ -672,16 +725,20 @@
672
725
  # Returns the great circle distance between two points
673
726
  def great_circle_distance(a, b)
674
727
  # Stolen from http://rosettacode.org/wiki/Haversine_formula#Ruby
675
- include Math
676
728
 
677
729
  def deg2rad(a)
678
- a * PI / 180
730
+ a * Math::PI / 180
679
731
  end
680
732
 
681
733
  radius = 6371 # rough radius of the Earth, in kilometers
682
734
  lat1, long1 = [Math::PI * a.latitude / 180.0, Math::PI * a.longitude / 180.0]
683
735
  lat2, long2 = [Math::PI * b.latitude / 180.0, Math::PI * b.longitude / 180.0]
684
- d = 2 * radius * asin(sqrt(sin((lat2-lat1)/2)**2 + cos(lat1) * cos(lat2) * sin((long2 - long1)/2)**2))
736
+ d = 2 * radius *
737
+ Math.asin( Math.sqrt(
738
+ Math.sin((lat2-lat1)/2)**2 +
739
+ Math.cos(lat1) * Math.cos(lat2) *
740
+ Math.sin((long2 - long1)/2)**2
741
+ ))
685
742
 
686
743
  return d
687
744
  end
@@ -1,3 +1,4 @@
1
+ require 'bundler/setup'
1
2
  require 'matrix'
2
3
 
3
4
  module Kamelopard
@@ -84,7 +85,7 @@ module Kamelopard
84
85
  # the camera vector.
85
86
  transformed_up = rot_z(heading) * rot_x(tilt) * Vector[0,1,0]
86
87
  if cross_product(up_vec, transformed_up).r == 0
87
- negate = not(same_quadrant(up_vec, transformed_up))
88
+ negate = ! (same_quadrant(up_vec, transformed_up))
88
89
  else
89
90
  negate = same_quadrant(vec, cross_product(up_vec, transformed_up))
90
91
  end
@@ -5,6 +5,7 @@
5
5
 
6
6
  #require 'rubygems'
7
7
  $LOAD_PATH << './lib'
8
+ require 'bundler/setup'
8
9
  require 'kamelopard'
9
10
  require 'libxml'
10
11
 
@@ -0,0 +1,99 @@
1
+ module Kamelopard
2
+ module Regionate
3
+ #require 'ai4r'
4
+ require 'bundler/setup'
5
+ require 'hierclust'
6
+
7
+ class Hierclust::Point
8
+ attr_accessor :placemark
9
+ def <=>(a)
10
+ raise "Something called this!"
11
+ end
12
+
13
+ def distance_to(b)
14
+ if self.is_a? Hierclust::Point then
15
+ pt1 = point(self.x, self.y)
16
+ else
17
+ pt1 = self.placemark.geometry
18
+ end
19
+ if b.is_a? Hierclust::Point then
20
+ pt2 = point(b.x, b.y)
21
+ else
22
+ pt2 = b.placemark.geometry
23
+ end
24
+ great_circle_distance(pt1, pt2)
25
+ end
26
+ end
27
+
28
+ # Regionate a bunch of placemarks. Add placemarks representing the centers
29
+ # of the discovered clusters to a folder if one is provided.
30
+ # Options:
31
+ # :folder => The folder to add placemarks for each region to. Defaults to
32
+ # nil, in which case no placemarks will be added
33
+ # :minlod => minLod value for each region, defaults to 12000
34
+ # :maxlod => minLod value for each region, defaults to -1 # :dist => minimum distance in degrees of lat/long between cluster centers
35
+ #def self.regionate(placemarks, count, options = {})
36
+ def self.regionate(placemarks, dist, options = {})
37
+ #include Ai4r::Data
38
+ #include Ai4r::Clusterers
39
+
40
+ options[:minlod] = 12000 unless options.has_key? :minlod and not options[:minlod].nil?
41
+ options[:maxlod] = -1 unless options.has_key? :maxlod and not options[:maxlod].nil?
42
+ hierpoints = []
43
+
44
+ placemarks.each do |g|
45
+ h = Hierclust::Point.new(g.longitude.to_f, g.latitude.to_f)
46
+ h.placemark = g
47
+ hierpoints << h
48
+ end
49
+ clusterer = Hierclust::Clusterer.new(hierpoints, dist)
50
+
51
+ centers = []
52
+ pmarks = []
53
+ regions = []
54
+
55
+ clusterer.clusters.each do |c|
56
+ Kamelopard.log(:debug, 'regionate', "Here's a cluster: #{c.points}")
57
+
58
+ r = Kamelopard::Region.new(
59
+ # Do something based on ... dist, perhaps, when radius is 0
60
+ :latlonaltbox => Kamelopard::LatLonBox.new(
61
+ # n, s, e, w
62
+ c.y + c.radius,
63
+ c.y - c.radius,
64
+ c.x + c.radius,
65
+ c.x - c.radius,
66
+ 0
67
+ ),
68
+ :lod => Kamelopard::Lod.new(options[:minlod], options[:maxlod], 0, 0)
69
+ # - :lod => Kamelopard::Lod.new(-1, options[:minlod], 0, 0)
70
+ )
71
+ regions << r
72
+
73
+ # Find placemarks in this cluster, and add region
74
+ c.points.each do |pt|
75
+ Kamelopard.log(:debug, 'regionate', "Found placemark #{pt.placemark.name} in the cluster")
76
+ pt.placemark.region = r
77
+ end
78
+
79
+ pl = placemark "#{c.size}-element cluster at #{c.y}, #{c.x}", :geometry => point(c.x, c.y, 0)
80
+ c_r = Kamelopard::Region.new(
81
+ :latlonaltbox => Kamelopard::LatLonBox.new(
82
+ # n, s, e, w
83
+ c.y + c.radius,
84
+ c.y - c.radius,
85
+ c.x + c.radius,
86
+ c.x - c.radius,
87
+ 0
88
+ ),
89
+ :lod => Kamelopard::Lod.new(-1, options[:minlod], 0, 0)
90
+ )
91
+ pl.region = c_r
92
+ options[:folder] << pl unless options[:folder].nil?
93
+ pmarks << pl
94
+ end
95
+
96
+ [ clusterer.clusters, centers, pmarks ]
97
+ end
98
+ end
99
+ end
@@ -1,4 +1,5 @@
1
1
  # vim:ts=4:sw=4:et:smartindent:nowrap
2
+ require 'bundler/setup'
2
3
  require 'matrix'
3
4
 
4
5
  # Basic support for splines
@@ -82,7 +83,7 @@ module Kamelopard
82
83
  ]
83
84
 
84
85
  p = Matrix[[1, u, u**2, u**3]] * h * cps
85
- return p
86
+ return p.row(0)
86
87
  end
87
88
  end ## End of SplineFunction class
88
89
  end ## End of Function module
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamelopard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-06-18 00:00:00.000000000 Z
13
+ date: 2013-12-12 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Various classes and functions used to ease development of KML files,
16
16
  in particular for development of Google Earth tours
@@ -23,6 +23,7 @@ extra_rdoc_files: []
23
23
  files:
24
24
  - lib/kamelopard/multicam.rb
25
25
  - lib/kamelopard/multicamify.rb
26
+ - lib/kamelopard/regionate.rb
26
27
  - lib/kamelopard/geocode.rb
27
28
  - lib/kamelopard/classes.rb
28
29
  - lib/kamelopard/helpers.rb
@@ -50,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
51
  version: '0'
51
52
  requirements: []
52
53
  rubyforge_project:
53
- rubygems_version: 1.8.25
54
+ rubygems_version: 1.8.23
54
55
  signing_key:
55
56
  specification_version: 3
56
57
  summary: Tools for building KML