nofxx-georuby 1.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/History.txt +4 -0
  2. data/LICENSE +21 -0
  3. data/README.txt +59 -0
  4. data/Rakefile +49 -0
  5. data/VERSION.yml +4 -0
  6. data/lib/geo_ruby.rb +21 -0
  7. data/lib/geo_ruby/base/envelope.rb +167 -0
  8. data/lib/geo_ruby/base/ewkb_parser.rb +216 -0
  9. data/lib/geo_ruby/base/ewkt_parser.rb +336 -0
  10. data/lib/geo_ruby/base/geometry.rb +234 -0
  11. data/lib/geo_ruby/base/geometry_collection.rb +136 -0
  12. data/lib/geo_ruby/base/geometry_factory.rb +81 -0
  13. data/lib/geo_ruby/base/georss_parser.rb +135 -0
  14. data/lib/geo_ruby/base/helper.rb +18 -0
  15. data/lib/geo_ruby/base/line_string.rb +184 -0
  16. data/lib/geo_ruby/base/linear_ring.rb +12 -0
  17. data/lib/geo_ruby/base/multi_line_string.rb +39 -0
  18. data/lib/geo_ruby/base/multi_point.rb +41 -0
  19. data/lib/geo_ruby/base/multi_polygon.rb +37 -0
  20. data/lib/geo_ruby/base/point.rb +310 -0
  21. data/lib/geo_ruby/base/polygon.rb +150 -0
  22. data/lib/geo_ruby/shp4r/dbf.rb +180 -0
  23. data/lib/geo_ruby/shp4r/shp.rb +701 -0
  24. data/spec/data/multipoint.dbf +0 -0
  25. data/spec/data/multipoint.shp +0 -0
  26. data/spec/data/multipoint.shx +0 -0
  27. data/spec/data/point.dbf +0 -0
  28. data/spec/data/point.shp +0 -0
  29. data/spec/data/point.shx +0 -0
  30. data/spec/data/polygon.dbf +0 -0
  31. data/spec/data/polygon.shp +0 -0
  32. data/spec/data/polygon.shx +0 -0
  33. data/spec/data/polyline.dbf +0 -0
  34. data/spec/data/polyline.shp +0 -0
  35. data/spec/data/polyline.shx +0 -0
  36. data/spec/geo_ruby/base/envelope_spec.rb +45 -0
  37. data/spec/geo_ruby/base/ewkb_parser_spec.rb +158 -0
  38. data/spec/geo_ruby/base/ewkt_parser_spec.rb +179 -0
  39. data/spec/geo_ruby/base/geometry_collection_spec.rb +55 -0
  40. data/spec/geo_ruby/base/geometry_factory_spec.rb +11 -0
  41. data/spec/geo_ruby/base/geometry_spec.rb +32 -0
  42. data/spec/geo_ruby/base/georss_parser_spec.rb +218 -0
  43. data/spec/geo_ruby/base/line_string_spec.rb +208 -0
  44. data/spec/geo_ruby/base/linear_ring_spec.rb +14 -0
  45. data/spec/geo_ruby/base/multi_line_string_spec.rb +35 -0
  46. data/spec/geo_ruby/base/multi_point_spec.rb +29 -0
  47. data/spec/geo_ruby/base/multi_polygon_spec.rb +35 -0
  48. data/spec/geo_ruby/base/point_spec.rb +275 -0
  49. data/spec/geo_ruby/base/polygon_spec.rb +108 -0
  50. data/spec/geo_ruby/shp4r/shp_spec.rb +238 -0
  51. data/spec/geo_ruby_spec.rb +27 -0
  52. data/spec/spec.opts +6 -0
  53. data/spec/spec_helper.rb +12 -0
  54. metadata +123 -0
@@ -0,0 +1,12 @@
1
+ require 'geo_ruby/base/line_string'
2
+
3
+ module GeoRuby
4
+ module Base
5
+ #Represents a linear ring, which is a closed line string (see LineString). Currently, no check is performed to verify if the linear ring is really closed.
6
+ class LinearRing < LineString
7
+ def initialize(srid= @@default_srid,with_z=false,with_m=false)
8
+ super(srid,with_z,with_m)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'geo_ruby/base/geometry_collection'
2
+
3
+ module GeoRuby
4
+ module Base
5
+ #Represents a group of line strings (see LineString).
6
+ class MultiLineString < GeometryCollection
7
+ def initialize(srid = @@default_srid,with_z=false,with_m=false)
8
+ super(srid)
9
+ end
10
+
11
+ def binary_geometry_type #:nodoc:
12
+ 5
13
+ end
14
+
15
+ #Text representation of a multi line string
16
+ def text_representation(allow_z=true,allow_m=true) #:nodoc:
17
+ @geometries.collect{|line_string| "(" + line_string.text_representation(allow_z,allow_m) + ")" }.join(",")
18
+ end
19
+ #WKT geometry type
20
+ def text_geometry_type #:nodoc:
21
+ "MULTILINESTRING"
22
+ end
23
+
24
+ #Creates a new multi line string from an array of line strings
25
+ def self.from_line_strings(line_strings,srid=@@default_srid,with_z=false,with_m=false)
26
+ multi_line_string = new(srid,with_z,with_m)
27
+ multi_line_string.concat(line_strings)
28
+ multi_line_string
29
+ end
30
+
31
+ #Creates a new multi line string from sequences of points : (((x,y)...(x,y)),((x,y)...(x,y)))
32
+ def self.from_coordinates(point_sequences,srid=@@default_srid,with_z=false,with_m=false)
33
+ multi_line_string = new(srid,with_z,with_m)
34
+ multi_line_string.concat(point_sequences.collect {|points| LineString.from_coordinates(points,srid,with_z,with_m) })
35
+ multi_line_string
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ require 'geo_ruby/base/geometry_collection'
2
+
3
+ module GeoRuby
4
+ module Base
5
+ #Represents a group of points (see Point).
6
+ class MultiPoint < GeometryCollection
7
+
8
+ def initialize(srid= @@default_srid,with_z=false,with_m=false)
9
+ super(srid,with_z,with_m)
10
+ end
11
+
12
+ def binary_geometry_type #:nodoc:
13
+ 4
14
+ end
15
+
16
+ #Text representation of a MultiPoint
17
+ def text_representation(allow_z=true,allow_m=true) #:nodoc:
18
+ "(" + @geometries.collect{|point| point.text_representation(allow_z,allow_m)}.join("),(") + ")"
19
+ end
20
+ #WKT geoemtry type
21
+ def text_geometry_type #:nodoc:
22
+ "MULTIPOINT"
23
+ end
24
+
25
+ #Creates a new multi point from an array of points
26
+ def self.from_points(points,srid= @@default_srid,with_z=false,with_m=false)
27
+ multi_point= new(srid,with_z,with_m)
28
+ multi_point.concat(points)
29
+ multi_point
30
+ end
31
+
32
+ #Creates a new multi point from a list of point coordinates : ((x,y)...(x,y))
33
+ def self.from_coordinates(points,srid= @@default_srid,with_z=false,with_m=false)
34
+ multi_point= new(srid,with_z,with_m)
35
+ multi_point.concat(points.collect {|point| Point.from_coordinates(point,srid,with_z,with_m)})
36
+ multi_point
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ require 'geo_ruby/base/geometry_collection'
2
+
3
+ module GeoRuby
4
+ module Base
5
+ #Represents a group of polygons (see Polygon).
6
+ class MultiPolygon < GeometryCollection
7
+ def initialize(srid = @@default_srid,with_z=false,with_m=false)
8
+ super(srid)
9
+ end
10
+
11
+ def binary_geometry_type #:nodoc:
12
+ 6
13
+ end
14
+ #Text representation of a MultiPolygon
15
+ def text_representation(allow_z=true,allow_m=true) #:nodoc:
16
+ @geometries.collect{|polygon| "(" + polygon.text_representation(allow_z,allow_m) + ")"}.join(",")
17
+ end
18
+ #WKT geometry type
19
+ def text_geometry_type #:nodoc:
20
+ "MULTIPOLYGON"
21
+ end
22
+
23
+ #Creates a multi polygon from an array of polygons
24
+ def self.from_polygons(polygons,srid=@@default_srid,with_z=false,with_m=false)
25
+ multi_polygon = new(srid,with_z,with_m)
26
+ multi_polygon.concat(polygons)
27
+ multi_polygon
28
+ end
29
+ #Creates a multi polygon from sequences of points : ((((x,y)...(x,y)),((x,y)...(x,y)),((x,y)...(x,y)))
30
+ def self.from_coordinates(point_sequence_sequences,srid= @@default_srid,with_z=false,with_m=false)
31
+ multi_polygon = new(srid,with_z,with_m)
32
+ multi_polygon.concat( point_sequence_sequences.collect {|point_sequences| Polygon.from_coordinates(point_sequences,srid,with_z,with_m) } )
33
+ multi_polygon
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,310 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "geo_ruby/base/geometry"
3
+
4
+ module GeoRuby
5
+ module Base
6
+ #Represents a point. It is in 3D if the Z coordinate is not +nil+.
7
+ class Point < Geometry
8
+ DEG2RAD = 0.0174532925199433
9
+ attr_accessor :x,:y,:z,:m
10
+ attr_reader :r, :t # radium and theta
11
+
12
+ #if you prefer calling the coordinates lat and lon (or lng, for GeoKit compatibility)
13
+ alias :lon :x
14
+ alias :lng :x
15
+ alias :lat :y
16
+ alias :rad :r
17
+ alias :tet :t
18
+ alias :tetha :t
19
+
20
+ def initialize(srid=@@default_srid,with_z=false,with_m=false)
21
+ super(srid,with_z,with_m)
22
+ @x = @y = 0.0
23
+ @z=0.0 #default value : meaningful if with_z
24
+ @m=0.0 #default value : meaningful if with_m
25
+ end
26
+ #sets all coordinates in one call. Use the +m+ accessor to set the m.
27
+ def set_x_y_z(x,y,z)
28
+ @x=x
29
+ @y=y
30
+ @z=z
31
+ self
32
+ end
33
+ alias :set_lon_lat_z :set_x_y_z
34
+
35
+ #sets all coordinates of a 2D point in one call
36
+ def set_x_y(x,y)
37
+ @x=x
38
+ @y=y
39
+ self
40
+ end
41
+ alias :set_lon_lat :set_x_y
42
+
43
+ #Return the distance between the 2D points (ie taking care only of the x and y coordinates), assuming
44
+ #the points are in projected coordinates. Euclidian distance in whatever unit the x and y ordinates are.
45
+ def euclidian_distance(point)
46
+ Math.sqrt((point.x - x)**2 + (point.y - y)**2)
47
+ end
48
+
49
+ #Returns the sperical distance in meters, with a radius of 6471000m, with the haversine law.
50
+ #Assumes x is the lon and y the lat, in degrees (Changed in version 1.1).
51
+ #The user has to make sure using this distance makes sense (ie she should be in latlon coordinates)
52
+ def spherical_distance(point,r=6370997.0)
53
+ radlat_from = lat * DEG2RAD
54
+ radlat_to = point.lat * DEG2RAD
55
+ dlat = (point.lat - lat) * DEG2RAD / 2
56
+ dlon = (point.lon - lon) * DEG2RAD / 2
57
+
58
+ a = Math.sin(dlat)**2 + Math.cos(radlat_from) * Math.cos(radlat_to) * Math.sin(dlon)**2
59
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
60
+ r * c
61
+ end
62
+
63
+ #Ellipsoidal distance in m using Vincenty's formula. Lifted entirely from Chris Veness's code at http://www.movable-type.co.uk/scripts/LatLongVincenty.html and adapted for Ruby. Assumes the x and y are the lon and lat in degrees.
64
+ #a is the semi-major axis (equatorial radius) of the ellipsoid
65
+ #b is the semi-minor axis (polar radius) of the ellipsoid
66
+ #Their values by default are set to the ones of the WGS84 ellipsoid
67
+ def ellipsoidal_distance(point, a = 6378137.0, b = 6356752.3142)
68
+ f = (a-b) / a
69
+ l = (point.lon - lon) * DEG2RAD
70
+
71
+ u1 = Math.atan((1-f) * Math.tan(lat * DEG2RAD ))
72
+ u2 = Math.atan((1-f) * Math.tan(point.lat * DEG2RAD))
73
+ sinU1 = Math.sin(u1)
74
+ cosU1 = Math.cos(u1)
75
+ sinU2 = Math.sin(u2)
76
+ cosU2 = Math.cos(u2)
77
+
78
+ lambda = l
79
+ lambdaP = 2 * Math::PI
80
+ iterLimit = 20
81
+
82
+ while (lambda-lambdaP).abs > 1e-12 && --iterLimit>0
83
+ sinLambda = Math.sin(lambda)
84
+ cosLambda = Math.cos(lambda)
85
+ sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda))
86
+
87
+ return 0 if sinSigma == 0 #coincident points
88
+
89
+ cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda
90
+ sigma = Math.atan2(sinSigma, cosSigma)
91
+ sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma
92
+ cosSqAlpha = 1 - sinAlpha*sinAlpha
93
+ cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha
94
+
95
+ cos2SigmaM = 0 if (cos2SigmaM.nan?) #equatorial line: cosSqAlpha=0
96
+
97
+ c = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
98
+ lambdaP = lambda
99
+ lambda = l + (1-c) * f * sinAlpha * (sigma + c * sinSigma * (cos2SigmaM + c * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)))
100
+ end
101
+ return NaN if iterLimit==0 #formula failed to converge
102
+
103
+ uSq = cosSqAlpha * (a*a - b*b) / (b*b)
104
+ a_bis = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
105
+ b_bis = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
106
+ deltaSigma = b_bis * sinSigma*(cos2SigmaM + b_bis/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)- b_bis/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
107
+
108
+ b*a_bis*(sigma-deltaSigma)
109
+ end
110
+
111
+ #Bounding box in 2D/3D. Returns an array of 2 points
112
+ def bounding_box
113
+ unless with_z
114
+ [Point.from_x_y(@x,@y),Point.from_x_y(@x,@y)]
115
+ else
116
+ [Point.from_x_y_z(@x,@y,@z),Point.from_x_y_z(@x,@y,@z)]
117
+ end
118
+ end
119
+
120
+ def m_range
121
+ [@m,@m]
122
+ end
123
+
124
+ #tests the equality of the position of points + m
125
+ def ==(other_point)
126
+ if other_point.class != self.class
127
+ false
128
+ else
129
+ @x == other_point.x and @y == other_point.y and @z == other_point.z and @m == other_point.m
130
+ end
131
+ end
132
+
133
+ #binary representation of a point. It lacks some headers to be a valid EWKB representation.
134
+ def binary_representation(allow_z=true,allow_m=true) #:nodoc:
135
+ bin_rep = [@x,@y].pack("EE")
136
+ bin_rep += [@z].pack("E") if @with_z and allow_z #Default value so no crash
137
+ bin_rep += [@m].pack("E") if @with_m and allow_m #idem
138
+ bin_rep
139
+ end
140
+ #WKB geometry type of a point
141
+ def binary_geometry_type#:nodoc:
142
+ 1
143
+ end
144
+
145
+ #text representation of a point
146
+ def text_representation(allow_z=true,allow_m=true) #:nodoc:
147
+ tex_rep = "#{@x} #{@y}"
148
+ tex_rep += " #{@z}" if @with_z and allow_z
149
+ tex_rep += " #{@m}" if @with_m and allow_m
150
+ tex_rep
151
+ end
152
+ #WKT geometry type of a point
153
+ def text_geometry_type #:nodoc:
154
+ "POINT"
155
+ end
156
+
157
+ #georss simple representation
158
+ def georss_simple_representation(options) #:nodoc:
159
+ georss_ns = options[:georss_ns] || "georss"
160
+ geom_attr = options[:geom_attr]
161
+ "<#{georss_ns}:point#{geom_attr}>#{y} #{x}</#{georss_ns}:point>\n"
162
+ end
163
+ #georss w3c representation
164
+ def georss_w3cgeo_representation(options) #:nodoc:
165
+ w3cgeo_ns = options[:w3cgeo_ns] || "geo"
166
+ "<#{w3cgeo_ns}:lat>#{y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{x}</#{w3cgeo_ns}:long>\n"
167
+ end
168
+ #georss gml representation
169
+ def georss_gml_representation(options) #:nodoc:
170
+ georss_ns = options[:georss_ns] || "georss"
171
+ gml_ns = options[:gml_ns] || "gml"
172
+ result = "<#{georss_ns}:where>\n<#{gml_ns}:Point>\n<#{gml_ns}:pos>"
173
+ result += "#{y} #{x}"
174
+ result += "</#{gml_ns}:pos>\n</#{gml_ns}:Point>\n</#{georss_ns}:where>\n"
175
+ end
176
+
177
+ #outputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
178
+ #<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
179
+ #it won't be used by GE anyway: clampToGround is the default)
180
+ def kml_representation(options = {}) #:nodoc:
181
+ result = "<Point#{options[:id_attr]}>\n"
182
+ result += options[:geom_data] if options[:geom_data]
183
+ result += "<coordinates>#{x},#{y}"
184
+ result += ",#{options[:fixed_z] || z ||0}" if options[:allow_z]
185
+ result += "</coordinates>\n"
186
+ result += "</Point>\n"
187
+ end
188
+
189
+ # Outputs the geometry in coordinates format:
190
+ # 47°52′48″, -20°06′00″
191
+ def as_latlong(opts = { })
192
+ val = []
193
+ [x,y].each_with_index do |l,i|
194
+ deg = l.to_i.abs
195
+ min = (60 * (l.abs - deg)).to_i
196
+ labs = (l * 1000000).abs / 1000000
197
+ sec = ((((labs - labs.to_i) * 60) - ((labs - labs.to_i) * 60).to_i) * 100000) * 60 / 100000
198
+ str = opts[:full] ? "%.i°%.2i′%05.2f″" : "%.i°%.2i′%02.0f″"
199
+ if opts[:coord]
200
+ out = str % [deg,min,sec]
201
+ if i == 0
202
+ out += l > 0 ? "N" : "S"
203
+ else
204
+ out += l > 0 ? "E" : "W"
205
+ end
206
+ val << out
207
+ else
208
+ val << str % [l.to_i, min, sec]
209
+ end
210
+ end
211
+ val.join(", ")
212
+ end
213
+
214
+ #Polar stuff
215
+ #http://www.engineeringtoolbox.com/converting-cartesian-polar-coordinates-d_1347.html
216
+ #http://rcoordinate.rubyforge.org/svn/point.rb
217
+ # outputs radium
218
+ def r; Math.sqrt(x**2 + y**2); end
219
+
220
+ #outputs theta
221
+ def theta_rad
222
+ if @x.zero?
223
+ @y < 0 ? 3 * Math::PI / 2 : Math::PI / 2
224
+ else
225
+ th = Math.atan(@y/@x)
226
+ th += 2 * Math::PI if r > 0
227
+ end
228
+ end
229
+
230
+ def theta_deg
231
+ theta_rad / DEG2RAD
232
+ end
233
+
234
+ #outputs an array containing polar distance and theta
235
+ def as_polar
236
+ [r,t]
237
+ end
238
+
239
+ #creates a point from an array of coordinates
240
+ def self.from_coordinates(coords,srid=@@default_srid,with_z=false,with_m=false)
241
+ if ! (with_z or with_m)
242
+ from_x_y(coords[0],coords[1],srid)
243
+ elsif with_z and with_m
244
+ from_x_y_z_m(coords[0],coords[1],coords[2],coords[3],srid)
245
+ elsif with_z
246
+ from_x_y_z(coords[0],coords[1],coords[2],srid)
247
+ else
248
+ from_x_y_m(coords[0],coords[1],coords[2],srid)
249
+ end
250
+ end
251
+
252
+ #creates a point from the X and Y coordinates
253
+ def self.from_x_y(x,y,srid=@@default_srid)
254
+ point= new(srid)
255
+ point.set_x_y(x,y)
256
+ end
257
+
258
+ #creates a point from the X, Y and Z coordinates
259
+ def self.from_x_y_z(x,y,z,srid=@@default_srid)
260
+ point= new(srid,true)
261
+ point.set_x_y_z(x,y,z)
262
+ end
263
+
264
+ #creates a point from the X, Y and M coordinates
265
+ def self.from_x_y_m(x,y,m,srid=@@default_srid)
266
+ point= new(srid,false,true)
267
+ point.m=m
268
+ point.set_x_y(x,y)
269
+ end
270
+
271
+ #creates a point from the X, Y, Z and M coordinates
272
+ def self.from_x_y_z_m(x,y,z,m,srid=@@default_srid)
273
+ point= new(srid,true,true)
274
+ point.m=m
275
+ point.set_x_y_z(x,y,z)
276
+ end
277
+
278
+ #creates a point using polar coordinates
279
+ #r and theta(degrees)
280
+ def self.from_r_t(r,t,srid=@@default_srid)
281
+ t *= DEG2RAD
282
+ x = r * Math.cos(t)
283
+ y = r * Math.sin(t)
284
+ point= new(srid)
285
+ point.set_x_y(x,y)
286
+ end
287
+
288
+ #creates a point using coordinates like 22`34 23.45N
289
+ def self.from_latlong(lat,lon,srid=@@default_srid)
290
+ p = [lat,lon].map do |l|
291
+ sig, deg, min, sec, cen = l.scan(/(-)?(\d{1,2})\D*(\d{2})\D*(\d{2})(\D*(\d{1,3}))?/).flatten
292
+ sig = true if l =~ /W|S/
293
+ dec = deg.to_i + (min.to_i * 60 + "#{sec}#{cen}".to_f) / 3600
294
+ sig ? dec * -1 : dec
295
+ end
296
+ point= new(srid)
297
+ point.set_x_y(p[0],p[1])
298
+ end
299
+
300
+ #aliasing the constructors in case you want to use lat/lon instead of y/x
301
+ class << self
302
+ alias :from_lon_lat :from_x_y
303
+ alias :from_lon_lat_z :from_x_y_z
304
+ alias :from_lon_lat_m :from_x_y_m
305
+ alias :from_lon_lat_z_m :from_x_y_z_m
306
+ alias :from_rad_tet :from_r_t
307
+ end
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,150 @@
1
+ require 'geo_ruby/base/geometry'
2
+
3
+ module GeoRuby
4
+ module Base
5
+ #Represents a polygon as an array of linear rings (see LinearRing). No check is performed regarding the validity of the geometries forming the polygon.
6
+ class Polygon < Geometry
7
+ #the list of rings forming the polygon
8
+ attr_reader :rings
9
+
10
+ def initialize(srid = @@default_srid,with_z=false,with_m=false)
11
+ super(srid,with_z,with_m)
12
+ @rings = []
13
+ end
14
+
15
+ #Delegate the unknown methods to the rings array
16
+ def method_missing(method_name,*args,&b)
17
+ @rings.send(method_name,*args,&b)
18
+ end
19
+
20
+ #Bounding box in 2D/3D. Returns an array of 2 points
21
+ def bounding_box
22
+ unless with_z
23
+ @rings[0].bounding_box
24
+ else
25
+ result = @rings[0].bounding_box #valid for x and y
26
+ max_z, min_z = result[1].z, result[0].z
27
+ 1.upto(size - 1) do |index|
28
+ bbox = @rings[index].bounding_box
29
+ sw = bbox[0]
30
+ ne = bbox[1]
31
+ max_z = ne.z if ne.z > max_z
32
+ min_z = sw.z if sw.z < min_z
33
+ end
34
+ result[1].z, result[0].z = max_z, min_z
35
+ result
36
+ end
37
+ end
38
+
39
+ def m_range
40
+ if with_m
41
+ max_m, min_m = -Float::MAX, Float::MAX
42
+ each do |lr|
43
+ lrmr = lr.m_range
44
+ max_m = lrmr[1] if lrmr[1] > max_m
45
+ min_m = lrmr[0] if lrmr[0] < min_m
46
+ end
47
+ [min_m,max_m]
48
+ else
49
+ [0,0]
50
+ end
51
+ end
52
+
53
+ #tests for other equality. The SRID is not taken into account.
54
+ def ==(other_polygon)
55
+ if other_polygon.class != self.class or
56
+ length != other_polygon.length
57
+ false
58
+ else
59
+ index=0
60
+ while index<length
61
+ return false if self[index] != other_polygon[index]
62
+ index+=1
63
+ end
64
+ true
65
+ end
66
+ end
67
+ #binary representation of a polygon, without the headers neccessary for a valid WKB string
68
+ def binary_representation(allow_z=true,allow_m=true)
69
+ rep = [length].pack("V")
70
+ each {|linear_ring| rep << linear_ring.binary_representation(allow_z,allow_m)}
71
+ rep
72
+ end
73
+ #WKB geometry type
74
+ def binary_geometry_type
75
+ 3
76
+ end
77
+
78
+ #Text representation of a polygon
79
+ def text_representation(allow_z=true,allow_m=true)
80
+ @rings.collect{|line_string| "(" + line_string.text_representation(allow_z,allow_m) + ")" }.join(",")
81
+ end
82
+ #WKT geometry type
83
+ def text_geometry_type
84
+ "POLYGON"
85
+ end
86
+
87
+ #georss simple representation : outputs only the outer ring
88
+ def georss_simple_representation(options)
89
+ georss_ns = options[:georss_ns] || "georss"
90
+ geom_attr = options[:geom_attr]
91
+ "<#{georss_ns}:polygon#{geom_attr}>" + self[0].georss_poslist + "</#{georss_ns}:polygon>\n"
92
+ end
93
+ #georss w3c representation : outputs the first point of the outer ring
94
+ def georss_w3cgeo_representation(options)
95
+ w3cgeo_ns = options[:w3cgeo_ns] || "geo"
96
+
97
+ "<#{w3cgeo_ns}:lat>#{self[0][0].y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{self[0][0].x}</#{w3cgeo_ns}:long>\n"
98
+ end
99
+ #georss gml representation
100
+ def georss_gml_representation(options)
101
+ georss_ns = options[:georss_ns] || "georss"
102
+ gml_ns = options[:gml_ns] || "gml"
103
+
104
+ result = "<#{georss_ns}:where>\n<#{gml_ns}:Polygon>\n<#{gml_ns}:exterior>\n<#{gml_ns}:LinearRing>\n<#{gml_ns}:posList>\n" + self[0].georss_poslist + "\n</#{gml_ns}:posList>\n</#{gml_ns}:LinearRing>\n</#{gml_ns}:exterior>\n</#{gml_ns}:Polygon>\n</#{georss_ns}:where>\n"
105
+ end
106
+
107
+ #outputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
108
+ #<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
109
+ #it won't be used by GE anyway: clampToGround is the default)
110
+ def kml_representation(options = {})
111
+ result = "<Polygon#{options[:id_attr]}>\n"
112
+ result += options[:geom_data] if options[:geom_data]
113
+ rings.each_with_index do |ring, i|
114
+ if i == 0
115
+ boundary = "outerBoundaryIs"
116
+ else
117
+ boundary = "innerBoundaryIs"
118
+ end
119
+ result += "<#{boundary}><LinearRing><coordinates>\n"
120
+ result += ring.kml_poslist(options)
121
+ result += "\n</coordinates></LinearRing></#{boundary}>\n"
122
+ end
123
+ result += "</Polygon>\n"
124
+ end
125
+
126
+ #creates a new polygon. Accepts an array of linear strings as argument
127
+ def self.from_linear_rings(linear_rings,srid = @@default_srid,with_z=false,with_m=false)
128
+ polygon = new(srid,with_z,with_m)
129
+ polygon.concat(linear_rings)
130
+ polygon
131
+ end
132
+
133
+ #creates a new polygon. Accepts a sequence of points as argument : ((x,y)....(x,y)),((x,y).....(x,y))
134
+ def self.from_coordinates(point_sequences,srid=@@default_srid,with_z=false,with_m=false)
135
+ polygon = new(srid,with_z,with_m)
136
+ polygon.concat( point_sequences.collect {|points| LinearRing.from_coordinates(points,srid,with_z,with_m) } )
137
+ polygon
138
+ end
139
+
140
+ #creates a new polygon from a list of Points (pt1....ptn),(pti....ptj)
141
+ def self.from_points(point_sequences, srid=@@default_srid,with_z=false,with_m=false)
142
+ polygon = new(srid,with_z,with_m)
143
+ polygon.concat( point_sequences.collect {|points| LinearRing.from_points(points,srid,with_z,with_m) } )
144
+ polygon
145
+
146
+ end
147
+
148
+ end
149
+ end
150
+ end