geos-extensions 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +17 -0
  4. data/Guardfile +17 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.rdoc +19 -91
  7. data/Rakefile +1 -12
  8. data/geos-extensions.gemspec +1 -9
  9. data/lib/geos-extensions.rb +1 -9
  10. data/lib/geos/coordinate_sequence.rb +92 -0
  11. data/lib/geos/extensions/version.rb +1 -1
  12. data/lib/geos/geometry.rb +252 -0
  13. data/lib/geos/geometry_collection.rb +60 -0
  14. data/lib/geos/geos_helper.rb +86 -72
  15. data/lib/geos/google_maps.rb +1 -0
  16. data/lib/geos/google_maps/api_2.rb +9 -23
  17. data/lib/geos/google_maps/api_3.rb +10 -24
  18. data/lib/geos/google_maps/api_common.rb +41 -0
  19. data/lib/geos/line_string.rb +15 -0
  20. data/lib/geos/multi_line_string.rb +15 -0
  21. data/lib/geos/multi_point.rb +15 -0
  22. data/lib/geos/multi_polygon.rb +27 -0
  23. data/lib/geos/point.rb +120 -0
  24. data/lib/geos/polygon.rb +158 -0
  25. data/lib/geos/yaml.rb +30 -0
  26. data/lib/geos/yaml/psych.rb +18 -0
  27. data/lib/geos/yaml/syck.rb +41 -0
  28. data/lib/geos_extensions.rb +110 -711
  29. data/test/google_maps_api_2_tests.rb +54 -32
  30. data/test/google_maps_api_3_tests.rb +58 -36
  31. data/test/google_maps_polyline_encoder_tests.rb +1 -1
  32. data/test/helper_tests.rb +28 -0
  33. data/test/misc_tests.rb +130 -10
  34. data/test/reader_tests.rb +38 -1
  35. data/test/test_helper.rb +54 -146
  36. data/test/writer_tests.rb +329 -10
  37. data/test/yaml_tests.rb +203 -0
  38. metadata +26 -102
  39. data/app/models/geos/geometry_column.rb +0 -39
  40. data/app/models/geos/spatial_ref_sys.rb +0 -12
  41. data/lib/geos/active_record_extensions.rb +0 -12
  42. data/lib/geos/active_record_extensions/connection_adapters/postgresql_adapter.rb +0 -151
  43. data/lib/geos/active_record_extensions/spatial_columns.rb +0 -367
  44. data/lib/geos/active_record_extensions/spatial_scopes.rb +0 -493
  45. data/lib/geos/rails/engine.rb +0 -6
  46. data/lib/tasks/test.rake +0 -42
  47. data/test/adapter_tests.rb +0 -38
  48. data/test/database.yml +0 -17
  49. data/test/fixtures/foo3ds.yml +0 -16
  50. data/test/fixtures/foo_geographies.yml +0 -16
  51. data/test/fixtures/foos.yml +0 -16
  52. data/test/geography_columns_tests.rb +0 -176
  53. data/test/geometry_columns_tests.rb +0 -178
  54. data/test/spatial_scopes_geographies_tests.rb +0 -107
  55. data/test/spatial_scopes_tests.rb +0 -337
@@ -0,0 +1,60 @@
1
+
2
+ module Geos
3
+ class GeometryCollection
4
+ if !GeometryCollection.included_modules.include?(Enumerable)
5
+ include Enumerable
6
+
7
+ # Iterates the collection through the given block.
8
+ def each
9
+ self.num_geometries.times do |n|
10
+ yield self.get_geometry_n(n)
11
+ end
12
+ nil
13
+ end
14
+
15
+ # Returns the nth geometry from the collection.
16
+ def [](*args)
17
+ self.to_a[*args]
18
+ end
19
+ alias :slice :[]
20
+ end
21
+
22
+ # Returns the last geometry from the collection.
23
+ def last
24
+ self.get_geometry_n(self.num_geometries - 1) if self.num_geometries > 0
25
+ end
26
+
27
+ # Returns a Hash suitable for converting to JSON.
28
+ def as_json(options = {})
29
+ self.collect do |p|
30
+ p.to_jsonable options
31
+ end
32
+ end
33
+ alias :to_jsonable :as_json
34
+
35
+ # Build some XmlMarkup for KML.
36
+ def to_kml(*args)
37
+ self.collect do |p|
38
+ p.to_kml(*args)
39
+ end
40
+ end
41
+
42
+ # Build some XmlMarkup for GeoRSS. Since GeoRSS is pretty trimed down,
43
+ # we just take the entire collection and use the exterior_ring as
44
+ # a Polygon. Not to bright, mind you, but until GeoRSS stops with the
45
+ # suck, what are we to do. You should include the appropriate georss
46
+ # and gml XML namespaces in your document.
47
+ def to_georss(*args)
48
+ self.exterior_ring.to_georss(*args)
49
+ end
50
+
51
+ def as_geojson(options = {})
52
+ {
53
+ :type => 'GeometryCollection',
54
+ :geometries => self.to_a.collect { |g| g.to_geojsonable(options) }
55
+ }
56
+ end
57
+ alias :to_geojsonable :as_geojson
58
+ end
59
+ end
60
+
@@ -10,95 +10,109 @@ module Geos::Helper
10
10
  "'" => "\\'"
11
11
  }
12
12
 
13
- # Escape carrier returns and single and double quotes for JavaScript
14
- # segments. Borrowed from ActiveSupport.
15
- def self.escape_javascript(javascript) #:nodoc:
16
- if javascript
17
- javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
18
- else
19
- ''
20
- end
21
- end
22
-
23
- def self.escape_json(hash, ignore_keys = [])
24
- json = hash.inject([]) do |memo, (k, v)|
25
- memo.tap {
26
- k = k.to_s
27
- memo << if ignore_keys.include?(k)
28
- "#{k.to_json}: #{v}"
29
- else
30
- "#{k.to_json}: #{v.to_json}"
31
- end
32
- }
13
+ class << self
14
+ # Escape carrier returns and single and double quotes for JavaScript
15
+ # segments. Borrowed from ActiveSupport.
16
+ def escape_javascript(javascript) #:nodoc:
17
+ if javascript
18
+ javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
19
+ else
20
+ ''
21
+ end
33
22
  end
34
23
 
35
- "{#{json.join(', ')}}"
36
- end
24
+ def escape_json(hash, ignore_keys = [])
25
+ json = hash.inject([]) do |memo, (k, v)|
26
+ memo.tap {
27
+ k = k.to_s
28
+ memo << if ignore_keys.include?(k)
29
+ "#{k.to_json}: #{v}"
30
+ else
31
+ "#{k.to_json}: #{v.to_json}"
32
+ end
33
+ }
34
+ end
37
35
 
38
- def self.camelize(lower_case_and_underscored_word, first_letter_in_uppercase = false)
39
- if first_letter_in_uppercase
40
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
41
- else
42
- lower_case_and_underscored_word.to_s[0..0].downcase + camelize(lower_case_and_underscored_word, true)[1..-1]
36
+ "{#{json.join(', ')}}"
43
37
  end
44
- end
45
38
 
46
- def self.xml_options(*args) #:nodoc:
47
- xml = if Builder::XmlMarkup === args.first
48
- args.first
49
- else
50
- Builder::XmlMarkup.new(:indent => 4)
39
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = false)
40
+ if first_letter_in_uppercase
41
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
42
+ else
43
+ lower_case_and_underscored_word.to_s[0..0].downcase + camelize(lower_case_and_underscored_word, true)[1..-1]
44
+ end
51
45
  end
52
46
 
53
- options = if Hash === args.last
54
- args.last
55
- else
56
- Hash.new
57
- end
47
+ def xml_options(*args) #:nodoc:
48
+ xml = if Builder::XmlMarkup === args.first
49
+ args.first
50
+ else
51
+ Builder::XmlMarkup.new(:indent => 4)
52
+ end
58
53
 
59
- [ xml, options ]
60
- end
54
+ options = if Hash === args.last
55
+ args.last
56
+ else
57
+ Hash.new
58
+ end
61
59
 
62
- # Return a new Hash with all keys converted to camelized Strings.
63
- def self.camelize_keys(hash, first_letter_in_uppercase = false)
64
- hash.inject({}) do |options, (key, value)|
65
- options[camelize(key, first_letter_in_uppercase)] = value
66
- options
60
+ [ xml, options ]
67
61
  end
68
- end
69
62
 
70
- # Destructively convert all keys to camelized Strings.
71
- def camelize_keys!(hash, first_letter_in_uppercase = false)
72
- hash.tap {
73
- hash.keys.each do |key|
74
- unless key.class.to_s == 'String'
75
- hash[camelize(key, first_letter_in_uppercase)] = hash[key]
76
- hash.delete(key)
77
- end
63
+ # Return a new Hash with all keys converted to camelized Strings.
64
+ def camelize_keys(hash, first_letter_in_uppercase = false)
65
+ hash.inject({}) do |options, (key, value)|
66
+ options[camelize(key, first_letter_in_uppercase)] = value
67
+ options
78
68
  end
79
- }
80
- end
69
+ end
70
+
71
+ # Destructively convert all keys to camelized Strings.
72
+ def camelize_keys!(hash, first_letter_in_uppercase = false)
73
+ hash.tap {
74
+ hash.keys.each do |key|
75
+ unless key.class.to_s == 'String'
76
+ hash[camelize(key, first_letter_in_uppercase)] = hash[key]
77
+ hash.delete(key)
78
+ end
79
+ end
80
+ }
81
+ end
81
82
 
82
- # Deeply camelize a Hash.
83
- def deep_camelize_keys(hash, first_letter_in_uppercase = false)
84
- camelize_keys(hash, first_letter_in_upppcase).inject({}) do |memo, (k, v)|
85
- memo.tap do
86
- if v.is_a? Hash
87
- memo[k] = deep_camelize_keys(v, first_letter_in_uppercase)
88
- else
89
- memo[k] = v
83
+ # Deeply camelize a Hash.
84
+ def deep_camelize_keys(hash, first_letter_in_uppercase = false)
85
+ camelize_keys(hash, first_letter_in_upppcase).inject({}) do |memo, (k, v)|
86
+ memo.tap do
87
+ if v.is_a? Hash
88
+ memo[k] = deep_camelize_keys(v, first_letter_in_uppercase)
89
+ else
90
+ memo[k] = v
91
+ end
90
92
  end
91
93
  end
92
94
  end
93
- end
94
95
 
95
- # Destructively deeply camelize a Hash.
96
- def deep_camelize_keys!(hash, first_letter_in_uppercase = false)
97
- hash.replace(deep_camelize_keys(hash, first_letter_in_uppercase))
98
- end
96
+ # Destructively deeply camelize a Hash.
97
+ def deep_camelize_keys!(hash, first_letter_in_uppercase = false)
98
+ hash.replace(deep_camelize_keys(hash, first_letter_in_uppercase))
99
+ end
100
+
101
+ def number_with_precision(number, precision = 6)
102
+ rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
103
+ "%01.#{precision}f" % rounded_number
104
+ end
99
105
 
100
- def self.number_with_precision(number, precision = 6)
101
- rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
102
- "%01.#{precision}f" % rounded_number
106
+ # Makes sure an object is wrapped in an Array.
107
+ def array_wrap(object)
108
+ if object.nil?
109
+ []
110
+ elsif object.respond_to?(:to_ary)
111
+ object.to_ary || [ object ]
112
+ else
113
+ [ object ]
114
+ end
115
+ end
103
116
  end
104
117
  end
118
+
@@ -22,6 +22,7 @@ module Geos
22
22
 
23
23
  autoload :PolylineEncoder, File.join(GEOS_EXTENSIONS_BASE, *%w{ geos google_maps polyline_encoder })
24
24
  autoload :ApiIncluder, File.join(GEOS_EXTENSIONS_BASE, *%w{ geos google_maps api_includer })
25
+ autoload :ApiCommon, File.join(GEOS_EXTENSIONS_BASE, *%w{ geos google_maps api_common })
25
26
  autoload :Api2, File.join(GEOS_EXTENSIONS_BASE, *%w{ geos google_maps api_2 })
26
27
  autoload :Api3, File.join(GEOS_EXTENSIONS_BASE, *%w{ geos google_maps api_3 })
27
28
  end
@@ -1,29 +1,7 @@
1
1
 
2
2
  module Geos::GoogleMaps::Api2
3
3
  module Geometry
4
- # Spit out Google's JSON geocoder Point format. The extra 0 is added
5
- # on as Google's format seems to like including the Z coordinate.
6
- def to_g_json_point_api2
7
- {
8
- :coordinates => (self.centroid.to_a << 0)
9
- }
10
- end
11
-
12
- # Spit out Google's JSON geocoder ExtendedData LatLonBox format.
13
- def to_g_lat_lon_box_api2
14
- {
15
- :north => self.north,
16
- :east => self.east,
17
- :south => self.south,
18
- :west => self.west
19
- }
20
- end
21
-
22
- # Spit out Google's toUrlValue format.
23
- def to_g_url_value_api2(precision = 6)
24
- c = self.centroid
25
- "#{Geos::Helper.number_with_precision(c.lat, precision)},#{Geos::Helper.number_with_precision(c.lng, precision)}"
26
- end
4
+ include Geos::GoogleMaps::ApiCommon::Geometry
27
5
 
28
6
  # Returns a new GLatLngBounds object with the proper GLatLngs in place
29
7
  # for determining the geometry bounds.
@@ -167,6 +145,8 @@ module Geos::GoogleMaps::Api2
167
145
  end
168
146
 
169
147
  module Polygon
148
+ include Geos::GoogleMaps::ApiCommon::UrlValueBounds
149
+
170
150
  # Returns a GPolyline of the exterior ring of the Polygon. This does
171
151
  # not take into consideration any interior rings the Polygon may
172
152
  # have.
@@ -182,7 +162,13 @@ module Geos::GoogleMaps::Api2
182
162
  end
183
163
  end
184
164
 
165
+ module LineString
166
+ include Geos::GoogleMaps::ApiCommon::UrlValueBounds
167
+ end
168
+
185
169
  module GeometryCollection
170
+ include Geos::GoogleMaps::ApiCommon::UrlValueBounds
171
+
186
172
  # Returns a Ruby Array of GPolylines for each geometry in the
187
173
  # collection.
188
174
  def to_g_polyline_api2(polyline_options = {}, options = {})
@@ -26,29 +26,7 @@ module Geos::GoogleMaps
26
26
  end
27
27
 
28
28
  module Api3::Geometry
29
- # Spit out Google's JSON geocoder Point format. The extra 0 is added
30
- # on as Google's format seems to like including the Z coordinate.
31
- def to_g_json_point_api3
32
- {
33
- :coordinates => (self.centroid.to_a << 0)
34
- }
35
- end
36
-
37
- # Spit out Google's JSON geocoder ExtendedData LatLonBox format.
38
- def to_g_lat_lon_box_api3
39
- {
40
- :north => self.north,
41
- :east => self.east,
42
- :south => self.south,
43
- :west => self.west
44
- }
45
- end
46
-
47
- # Spit out Google's toUrlValue format.
48
- def to_g_url_value_api3(precision = 6)
49
- c = self.centroid
50
- "#{Geos::Helper.number_with_precision(c.lat, precision)},#{Geos::Helper.number_with_precision(c.lng, precision)}"
51
- end
29
+ include Geos::GoogleMaps::ApiCommon::Geometry
52
30
 
53
31
  # Returns a new LatLngBounds object with the proper LatLngs in place
54
32
  # for determining the geometry bounds.
@@ -58,7 +36,7 @@ module Geos::GoogleMaps
58
36
 
59
37
  # Returns a bounds parameter for the Google Maps API 3 geocoder service.
60
38
  def to_g_geocoder_bounds_api3(precision = 6)
61
- "#{self.lower_left.to_g_url_value_api3(precision)}|#{self.upper_right.to_g_url_value_api3(precision)}"
39
+ "#{self.lower_left.to_g_url_value(precision)}|#{self.upper_right.to_g_url_value(precision)}"
62
40
  end
63
41
 
64
42
  # Returns a String in Google Maps' LatLngBounds#toString() format.
@@ -172,6 +150,8 @@ module Geos::GoogleMaps
172
150
  end
173
151
 
174
152
  module Api3::Polygon
153
+ include Geos::GoogleMaps::ApiCommon::UrlValueBounds
154
+
175
155
  # Returns a Polyline of the exterior ring of the Polygon. This does
176
156
  # not take into consideration any interior rings the Polygon may
177
157
  # have.
@@ -187,7 +167,13 @@ module Geos::GoogleMaps
187
167
  end
188
168
  end
189
169
 
170
+ module Api3::LineString
171
+ include Geos::GoogleMaps::ApiCommon::UrlValueBounds
172
+ end
173
+
190
174
  module Api3::GeometryCollection
175
+ include Geos::GoogleMaps::ApiCommon::UrlValueBounds
176
+
191
177
  # Returns a Ruby Array of Polylines for each geometry in the
192
178
  # collection.
193
179
  def to_g_polyline_api3(polyline_options = {}, options = {})
@@ -0,0 +1,41 @@
1
+
2
+ module Geos
3
+ module GoogleMaps
4
+ module ApiCommon
5
+ module Geometry
6
+ # Spit out Google's JSON geocoder Point format. The extra 0 is added
7
+ # on as Google's format seems to like including the Z coordinate.
8
+ def to_g_json_point
9
+ {
10
+ :coordinates => (self.centroid.to_a << 0)
11
+ }
12
+ end
13
+
14
+ # Spit out Google's JSON geocoder ExtendedData LatLonBox format.
15
+ def to_g_lat_lon_box
16
+ {
17
+ :north => self.north,
18
+ :east => self.east,
19
+ :south => self.south,
20
+ :west => self.west
21
+ }
22
+ end
23
+
24
+ # Spit out Google's toUrlValue format.
25
+ def to_g_url_value(precision = 6)
26
+ c = self.centroid
27
+ "#{Geos::Helper.number_with_precision(c.lat, precision)},#{Geos::Helper.number_with_precision(c.lng, precision)}"
28
+ end
29
+ end
30
+
31
+ module UrlValueBounds
32
+ # Spit out Google's toUrlValue format.
33
+ def to_g_url_value(precision = 6)
34
+ e = self.envelope
35
+ "#{e.southwest.to_g_url_value(precision)},#{e.northeast.to_g_url_value(precision)}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,15 @@
1
+
2
+ module Geos
3
+ class LineString
4
+ def as_json(options = {})
5
+ self.coord_seq.as_json(options)
6
+ end
7
+ alias :to_jsonable :as_json
8
+
9
+ def as_geojson(options = {})
10
+ self.coord_seq.to_geojsonable(options)
11
+ end
12
+ alias :to_geojsonable :as_geojson
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+
2
+ module Geos
3
+ class MultiLineString < GeometryCollection
4
+ def to_geojsonable(options = {})
5
+ {
6
+ :type => 'MultiLineString',
7
+ :coordinates => self.to_a.collect { |linestring|
8
+ linestring.coord_seq.to_a
9
+ }
10
+ }
11
+ end
12
+ alias :as_geojson :to_geojsonable
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+
2
+ module Geos
3
+ class MultiPoint < GeometryCollection
4
+ def to_geojsonable(options = {})
5
+ {
6
+ :type => 'MultiPoint',
7
+ :coordinates => self.to_a.collect { |point|
8
+ point.to_a
9
+ }
10
+ }
11
+ end
12
+ alias :as_geojson :to_geojsonable
13
+ end
14
+ end
15
+