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.
- checksums.yaml +15 -0
- data/.gitignore +3 -0
- data/Gemfile +17 -0
- data/Guardfile +17 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +19 -91
- data/Rakefile +1 -12
- data/geos-extensions.gemspec +1 -9
- data/lib/geos-extensions.rb +1 -9
- data/lib/geos/coordinate_sequence.rb +92 -0
- data/lib/geos/extensions/version.rb +1 -1
- data/lib/geos/geometry.rb +252 -0
- data/lib/geos/geometry_collection.rb +60 -0
- data/lib/geos/geos_helper.rb +86 -72
- data/lib/geos/google_maps.rb +1 -0
- data/lib/geos/google_maps/api_2.rb +9 -23
- data/lib/geos/google_maps/api_3.rb +10 -24
- data/lib/geos/google_maps/api_common.rb +41 -0
- data/lib/geos/line_string.rb +15 -0
- data/lib/geos/multi_line_string.rb +15 -0
- data/lib/geos/multi_point.rb +15 -0
- data/lib/geos/multi_polygon.rb +27 -0
- data/lib/geos/point.rb +120 -0
- data/lib/geos/polygon.rb +158 -0
- data/lib/geos/yaml.rb +30 -0
- data/lib/geos/yaml/psych.rb +18 -0
- data/lib/geos/yaml/syck.rb +41 -0
- data/lib/geos_extensions.rb +110 -711
- data/test/google_maps_api_2_tests.rb +54 -32
- data/test/google_maps_api_3_tests.rb +58 -36
- data/test/google_maps_polyline_encoder_tests.rb +1 -1
- data/test/helper_tests.rb +28 -0
- data/test/misc_tests.rb +130 -10
- data/test/reader_tests.rb +38 -1
- data/test/test_helper.rb +54 -146
- data/test/writer_tests.rb +329 -10
- data/test/yaml_tests.rb +203 -0
- metadata +26 -102
- data/app/models/geos/geometry_column.rb +0 -39
- data/app/models/geos/spatial_ref_sys.rb +0 -12
- data/lib/geos/active_record_extensions.rb +0 -12
- data/lib/geos/active_record_extensions/connection_adapters/postgresql_adapter.rb +0 -151
- data/lib/geos/active_record_extensions/spatial_columns.rb +0 -367
- data/lib/geos/active_record_extensions/spatial_scopes.rb +0 -493
- data/lib/geos/rails/engine.rb +0 -6
- data/lib/tasks/test.rake +0 -42
- data/test/adapter_tests.rb +0 -38
- data/test/database.yml +0 -17
- data/test/fixtures/foo3ds.yml +0 -16
- data/test/fixtures/foo_geographies.yml +0 -16
- data/test/fixtures/foos.yml +0 -16
- data/test/geography_columns_tests.rb +0 -176
- data/test/geometry_columns_tests.rb +0 -178
- data/test/spatial_scopes_geographies_tests.rb +0 -107
- 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
|
+
|
data/lib/geos/geos_helper.rb
CHANGED
@@ -10,95 +10,109 @@ module Geos::Helper
|
|
10
10
|
"'" => "\\'"
|
11
11
|
}
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
javascript
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
54
|
-
args.
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
54
|
+
options = if Hash === args.last
|
55
|
+
args.last
|
56
|
+
else
|
57
|
+
Hash.new
|
58
|
+
end
|
61
59
|
|
62
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
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
|
+
|
data/lib/geos/google_maps.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
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
|
+
|