geos-extensions 0.2.2 → 0.3.0

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.
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
+