georuby 1.9.3

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 (75) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +29 -0
  3. data/History.txt +4 -0
  4. data/LICENSE +21 -0
  5. data/README.rdoc +184 -0
  6. data/Rakefile +48 -0
  7. data/VERSION +1 -0
  8. data/georuby.gemspec +128 -0
  9. data/lib/geo_ruby.rb +23 -0
  10. data/lib/geo_ruby/geojson.rb +129 -0
  11. data/lib/geo_ruby/georss.rb +133 -0
  12. data/lib/geo_ruby/gpx.rb +1 -0
  13. data/lib/geo_ruby/gpx4r/gpx.rb +118 -0
  14. data/lib/geo_ruby/shp.rb +1 -0
  15. data/lib/geo_ruby/shp4r/dbf.rb +42 -0
  16. data/lib/geo_ruby/shp4r/shp.rb +718 -0
  17. data/lib/geo_ruby/simple_features/envelope.rb +167 -0
  18. data/lib/geo_ruby/simple_features/ewkb_parser.rb +218 -0
  19. data/lib/geo_ruby/simple_features/ewkt_parser.rb +336 -0
  20. data/lib/geo_ruby/simple_features/geometry.rb +236 -0
  21. data/lib/geo_ruby/simple_features/geometry_collection.rb +144 -0
  22. data/lib/geo_ruby/simple_features/geometry_factory.rb +81 -0
  23. data/lib/geo_ruby/simple_features/helper.rb +18 -0
  24. data/lib/geo_ruby/simple_features/line_string.rb +228 -0
  25. data/lib/geo_ruby/simple_features/linear_ring.rb +34 -0
  26. data/lib/geo_ruby/simple_features/multi_line_string.rb +63 -0
  27. data/lib/geo_ruby/simple_features/multi_point.rb +58 -0
  28. data/lib/geo_ruby/simple_features/multi_polygon.rb +64 -0
  29. data/lib/geo_ruby/simple_features/point.rb +381 -0
  30. data/lib/geo_ruby/simple_features/polygon.rb +175 -0
  31. data/nofxx-georuby.gemspec +149 -0
  32. data/spec/data/geojson/feature_collection.json +34 -0
  33. data/spec/data/georss/atom.xml +21 -0
  34. data/spec/data/georss/gml.xml +40 -0
  35. data/spec/data/georss/w3c.xml +22 -0
  36. data/spec/data/gpx/fells_loop.gpx +1077 -0
  37. data/spec/data/gpx/long.gpx +1642 -0
  38. data/spec/data/gpx/long.kml +31590 -0
  39. data/spec/data/gpx/long.nmea +2220 -0
  40. data/spec/data/gpx/short.gpx +13634 -0
  41. data/spec/data/gpx/short.kml +130 -0
  42. data/spec/data/gpx/tracktreks.gpx +706 -0
  43. data/spec/data/multipoint.dbf +0 -0
  44. data/spec/data/multipoint.shp +0 -0
  45. data/spec/data/multipoint.shx +0 -0
  46. data/spec/data/point.dbf +0 -0
  47. data/spec/data/point.shp +0 -0
  48. data/spec/data/point.shx +0 -0
  49. data/spec/data/polygon.dbf +0 -0
  50. data/spec/data/polygon.shp +0 -0
  51. data/spec/data/polygon.shx +0 -0
  52. data/spec/data/polyline.dbf +0 -0
  53. data/spec/data/polyline.shp +0 -0
  54. data/spec/data/polyline.shx +0 -0
  55. data/spec/geo_ruby/geojson_spec.rb +147 -0
  56. data/spec/geo_ruby/georss.rb +218 -0
  57. data/spec/geo_ruby/georss_spec.rb +14 -0
  58. data/spec/geo_ruby/gpx4r/gpx_spec.rb +106 -0
  59. data/spec/geo_ruby/shp4r/shp_spec.rb +239 -0
  60. data/spec/geo_ruby/simple_features/envelope_spec.rb +47 -0
  61. data/spec/geo_ruby/simple_features/ewkb_parser_spec.rb +158 -0
  62. data/spec/geo_ruby/simple_features/ewkt_parser_spec.rb +179 -0
  63. data/spec/geo_ruby/simple_features/geometry_collection_spec.rb +55 -0
  64. data/spec/geo_ruby/simple_features/geometry_factory_spec.rb +11 -0
  65. data/spec/geo_ruby/simple_features/geometry_spec.rb +32 -0
  66. data/spec/geo_ruby/simple_features/line_string_spec.rb +259 -0
  67. data/spec/geo_ruby/simple_features/linear_ring_spec.rb +24 -0
  68. data/spec/geo_ruby/simple_features/multi_line_string_spec.rb +54 -0
  69. data/spec/geo_ruby/simple_features/multi_point_spec.rb +35 -0
  70. data/spec/geo_ruby/simple_features/multi_polygon_spec.rb +50 -0
  71. data/spec/geo_ruby/simple_features/point_spec.rb +356 -0
  72. data/spec/geo_ruby/simple_features/polygon_spec.rb +122 -0
  73. data/spec/geo_ruby_spec.rb +27 -0
  74. data/spec/spec_helper.rb +73 -0
  75. metadata +228 -0
@@ -0,0 +1,381 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "geo_ruby/simple_features/geometry"
3
+
4
+ module GeoRuby
5
+ module SimpleFeatures
6
+ #Represents a point. It is in 3D if the Z coordinate is not +nil+.
7
+ class Point < Geometry
8
+ DEG2RAD = 0.0174532925199433
9
+ HALFPI = 1.5707963267948966
10
+ attr_accessor :x,:y,:z,:m
11
+ attr_reader :r, :t # radium and theta
12
+
13
+ #if you prefer calling the coordinates lat and lon (or lng, for GeoKit compatibility)
14
+ alias :lon :x
15
+ alias :lng :x
16
+ alias :lat :y
17
+ alias :rad :r
18
+ alias :tet :t
19
+ alias :tetha :t
20
+
21
+ def initialize(srid=DEFAULT_SRID,with_z=false,with_m=false)
22
+ super(srid,with_z,with_m)
23
+ @x = @y = 0.0
24
+ @z=0.0 #default value : meaningful if with_z
25
+ @m=0.0 #default value : meaningful if with_m
26
+ end
27
+ #sets all coordinates in one call. Use the +m+ accessor to set the m.
28
+ def set_x_y_z(x,y,z)
29
+ @x=x
30
+ @y=y
31
+ @z=z
32
+ self
33
+ end
34
+ alias :set_lon_lat_z :set_x_y_z
35
+
36
+ #sets all coordinates of a 2D point in one call
37
+ def set_x_y(x,y)
38
+ @x=x
39
+ @y=y
40
+ self
41
+ end
42
+ alias :set_lon_lat :set_x_y
43
+
44
+ #Return the distance between the 2D points (ie taking care only of the x and y coordinates), assuming
45
+ #the points are in projected coordinates. Euclidian distance in whatever unit the x and y ordinates are.
46
+ def euclidian_distance(point)
47
+ Math.sqrt((point.x - x)**2 + (point.y - y)**2)
48
+ end
49
+
50
+ # Spherical distance in meters, using 'Haversine' formula.
51
+ # with a radius of 6471000m
52
+ # Assumes x is the lon and y the lat, in degrees (Changed in version 1.1).
53
+ # The user has to make sure using this distance makes sense (ie she should be in latlon coordinates)
54
+ def spherical_distance(point,r=6370997.0)
55
+ dlat = (point.lat - lat) * DEG2RAD / 2
56
+ dlon = (point.lon - lon) * DEG2RAD / 2
57
+
58
+ a = Math.sin(dlat)**2 + Math.cos(lat * DEG2RAD) * Math.cos(point.lat * DEG2RAD) * 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
+ # Orthogonal Distance
112
+ # Based http://www.allegro.cc/forums/thread/589720
113
+ def orthogonal_distance(line, tail = nil)
114
+ head, tail = tail ? [line, tail] : [line[0], line[-1]]
115
+ a, b = @x - head.x, @y - head.y
116
+ c, d = tail.x - head.x, tail.y - head.y
117
+
118
+ dot = a * c + b * d
119
+ len = c * c + d * d
120
+ res = dot / len
121
+
122
+ xx, yy = if res < 0
123
+ [head.x, head.y]
124
+ elsif res > 1
125
+ [tail.x, tail.y]
126
+ else
127
+ [head.x + res * c, head.y + res * d]
128
+ end
129
+ # todo benchmark if worth creating an instance
130
+ # euclidian_distance(Point.from_x_y(xx, yy))
131
+ Math.sqrt((@x - xx) ** 2 + (@y - yy) ** 2)
132
+ end
133
+
134
+ #Bearing from a point to another, in degrees.
135
+ def bearing_to(other)
136
+ return 0 if self == other
137
+ a,b = other.x - self.x, other.y - self.y
138
+ res = Math.acos(b / Math.sqrt(a*a+b*b)) / Math::PI * 180;
139
+ a < 0 ? 360 - res : res
140
+ end
141
+
142
+ #Bearing from a point to another as symbols. (:n, :s, :sw, :ne...)
143
+ def bearing_text(other)
144
+ case bearing_to(other)
145
+ when 1..22 then :n
146
+ when 23..66 then :ne
147
+ when 67..112 then :e
148
+ when 113..146 then :se
149
+ when 147..202 then :s
150
+ when 203..246 then :sw
151
+ when 247..292 then :w
152
+ when 293..336 then :nw
153
+ when 337..360 then :n
154
+ else nil
155
+ end
156
+ end
157
+
158
+ #Bounding box in 2D/3D. Returns an array of 2 points
159
+ def bounding_box
160
+ unless with_z
161
+ [Point.from_x_y(@x,@y),Point.from_x_y(@x,@y)]
162
+ else
163
+ [Point.from_x_y_z(@x,@y,@z),Point.from_x_y_z(@x,@y,@z)]
164
+ end
165
+ end
166
+
167
+ def m_range
168
+ [@m,@m]
169
+ end
170
+
171
+ #tests the equality of the position of points + m
172
+ def ==(other)
173
+ return false unless other.kind_of?(Point)
174
+ @x == other.x and @y == other.y and @z == other.z and @m == other.m
175
+ end
176
+
177
+ #binary representation of a point. It lacks some headers to be a valid EWKB representation.
178
+ def binary_representation(allow_z=true,allow_m=true) #:nodoc:
179
+ bin_rep = [@x.to_f,@y.to_f].pack("EE")
180
+ bin_rep += [@z.to_f].pack("E") if @with_z and allow_z #Default value so no crash
181
+ bin_rep += [@m.to_f].pack("E") if @with_m and allow_m #idem
182
+ bin_rep
183
+ end
184
+
185
+ #WKB geometry type of a point
186
+ def binary_geometry_type#:nodoc:
187
+ 1
188
+ end
189
+
190
+ #text representation of a point
191
+ def text_representation(allow_z=true,allow_m=true) #:nodoc:
192
+ tex_rep = "#{@x} #{@y}"
193
+ tex_rep += " #{@z}" if @with_z and allow_z
194
+ tex_rep += " #{@m}" if @with_m and allow_m
195
+ tex_rep
196
+ end
197
+
198
+ #WKT geometry type of a point
199
+ def text_geometry_type #:nodoc:
200
+ "POINT"
201
+ end
202
+
203
+ #georss simple representation
204
+ def georss_simple_representation(options) #:nodoc:
205
+ georss_ns = options[:georss_ns] || "georss"
206
+ geom_attr = options[:geom_attr]
207
+ "<#{georss_ns}:point#{geom_attr}>#{y} #{x}</#{georss_ns}:point>\n"
208
+ end
209
+
210
+ #georss w3c representation
211
+ def georss_w3cgeo_representation(options) #:nodoc:
212
+ w3cgeo_ns = options[:w3cgeo_ns] || "geo"
213
+ "<#{w3cgeo_ns}:lat>#{y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{x}</#{w3cgeo_ns}:long>\n"
214
+ end
215
+
216
+ #georss gml representation
217
+ def georss_gml_representation(options) #:nodoc:
218
+ georss_ns = options[:georss_ns] || "georss"
219
+ gml_ns = options[:gml_ns] || "gml"
220
+ result = "<#{georss_ns}:where>\n<#{gml_ns}:Point>\n<#{gml_ns}:pos>"
221
+ result += "#{y} #{x}"
222
+ result += "</#{gml_ns}:pos>\n</#{gml_ns}:Point>\n</#{georss_ns}:where>\n"
223
+ end
224
+
225
+ #outputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
226
+ #<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
227
+ #it won't be used by GE anyway: clampToGround is the default)
228
+ def kml_representation(options = {}) #:nodoc:
229
+ result = "<Point#{options[:id_attr]}>\n"
230
+ result += options[:geom_data] if options[:geom_data]
231
+ result += "<coordinates>#{x},#{y}"
232
+ result += ",#{options[:fixed_z] || z ||0}" if options[:allow_z]
233
+ result += "</coordinates>\n"
234
+ result += "</Point>\n"
235
+ end
236
+
237
+ # Outputs the geometry in coordinates format:
238
+ # 47°52′48″, -20°06′00″
239
+ def as_latlong(opts = { })
240
+ val = []
241
+ [x,y].each_with_index do |l,i|
242
+ deg = l.to_i.abs
243
+ min = (60 * (l.abs - deg)).to_i
244
+ labs = (l * 1000000).abs / 1000000
245
+ sec = ((((labs - labs.to_i) * 60) - ((labs - labs.to_i) * 60).to_i) * 100000) * 60 / 100000
246
+ str = opts[:full] ? "%.i°%.2i′%05.2f″" : "%.i°%.2i′%02.0f″"
247
+ if opts[:coord]
248
+ out = str % [deg,min,sec]
249
+ if i == 0
250
+ out += l > 0 ? "N" : "S"
251
+ else
252
+ out += l > 0 ? "E" : "W"
253
+ end
254
+ val << out
255
+ else
256
+ val << str % [l.to_i, min, sec]
257
+ end
258
+ end
259
+ val.join(", ")
260
+ end
261
+
262
+ # Polar stuff
263
+ #
264
+ # http://www.engineeringtoolbox.com/converting-cartesian-polar-coordinates-d_1347.html
265
+ # http://rcoordinate.rubyforge.org/svn/point.rb
266
+ # outputs radium
267
+ def r; Math.sqrt(@x**2 + @y**2); end
268
+
269
+ # outputs theta
270
+ def theta_rad
271
+ if @x.zero?
272
+ @y < 0 ? 3 * HALFPI : HALFPI
273
+ else
274
+ th = Math.atan(@y/@x)
275
+ th += 2 * Math::PI if r > 0
276
+ end
277
+ end
278
+
279
+ # outputs theta in degrees
280
+ def theta_deg; theta_rad / DEG2RAD; end
281
+
282
+ # outputs an array containing polar distance and theta
283
+ def as_polar; [r,t]; end
284
+
285
+ # invert signal of all coordinates
286
+ def -@
287
+ set_x_y_z(-@x, -@y, -@z)
288
+ end
289
+
290
+ # TODO Perhaps should support with_m analogous to from_coordinates?
291
+ def to_coordinates
292
+ if with_z
293
+ [x,y,z]
294
+ else
295
+ [x,y]
296
+ end
297
+ end
298
+
299
+ # simple geojson representation
300
+ # TODO add CRS / SRID support?
301
+ def to_json(options = {})
302
+ {:type => 'Point',
303
+ :coordinates => self.to_coordinates}.to_json(options)
304
+ end
305
+ alias :as_geojson :to_json
306
+
307
+ #creates a point from an array of coordinates
308
+ def self.from_coordinates(coords,srid=DEFAULT_SRID,with_z=false,with_m=false)
309
+ if ! (with_z or with_m)
310
+ from_x_y(coords[0],coords[1],srid)
311
+ elsif with_z and with_m
312
+ from_x_y_z_m(coords[0],coords[1],coords[2],coords[3],srid)
313
+ elsif with_z
314
+ from_x_y_z(coords[0],coords[1],coords[2],srid)
315
+ else
316
+ from_x_y_m(coords[0],coords[1],coords[2],srid)
317
+ end
318
+ end
319
+
320
+ #creates a point from the X and Y coordinates
321
+ def self.from_x_y(x,y,srid=DEFAULT_SRID)
322
+ point= new(srid)
323
+ point.set_x_y(x,y)
324
+ end
325
+
326
+ #creates a point from the X, Y and Z coordinates
327
+ def self.from_x_y_z(x,y,z,srid=DEFAULT_SRID)
328
+ point= new(srid,true)
329
+ point.set_x_y_z(x,y,z)
330
+ end
331
+
332
+ #creates a point from the X, Y and M coordinates
333
+ def self.from_x_y_m(x,y,m,srid=DEFAULT_SRID)
334
+ point= new(srid,false,true)
335
+ point.m=m
336
+ point.set_x_y(x,y)
337
+ end
338
+
339
+ #creates a point from the X, Y, Z and M coordinates
340
+ def self.from_x_y_z_m(x,y,z,m,srid=DEFAULT_SRID)
341
+ point= new(srid,true,true)
342
+ point.m=m
343
+ point.set_x_y_z(x,y,z)
344
+ end
345
+
346
+ #creates a point using polar coordinates
347
+ #r and theta(degrees)
348
+ def self.from_r_t(r,t,srid=DEFAULT_SRID)
349
+ t *= DEG2RAD
350
+ x = r * Math.cos(t)
351
+ y = r * Math.sin(t)
352
+ point= new(srid)
353
+ point.set_x_y(x,y)
354
+ end
355
+
356
+ #creates a point using coordinates like 22`34 23.45N
357
+ def self.from_latlong(lat,lon,srid=DEFAULT_SRID)
358
+ p = [lat,lon].map do |l|
359
+ sig, deg, min, sec, cen = l.scan(/(-)?(\d{1,2})\D*(\d{2})\D*(\d{2})(\D*(\d{1,3}))?/).flatten
360
+ sig = true if l =~ /W|S/
361
+ dec = deg.to_i + (min.to_i * 60 + "#{sec}#{cen}".to_f) / 3600
362
+ sig ? dec * -1 : dec
363
+ end
364
+ point= new(srid)
365
+ point.set_x_y(p[0],p[1])
366
+ end
367
+
368
+ #aliasing the constructors in case you want to use lat/lon instead of y/x
369
+ class << self
370
+ alias :xy :from_x_y
371
+ alias :xyz :from_x_y_z
372
+ alias :from_lon_lat_z :from_x_y_z
373
+ alias :from_lon_lat :from_x_y
374
+ alias :from_lon_lat_z :from_x_y_z
375
+ alias :from_lon_lat_m :from_x_y_m
376
+ alias :from_lon_lat_z_m :from_x_y_z_m
377
+ alias :from_rad_tet :from_r_t
378
+ end
379
+ end
380
+ end
381
+ end
@@ -0,0 +1,175 @@
1
+ require 'geo_ruby/simple_features/geometry'
2
+
3
+ module GeoRuby
4
+
5
+ module SimpleFeatures
6
+
7
+ # Represents a polygon as an array of linear rings (see LinearRing).
8
+ # No check is performed regarding the validity of the geometries forming the polygon.
9
+ class Polygon < Geometry
10
+ #the list of rings forming the polygon
11
+ attr_reader :rings
12
+
13
+ def initialize(srid = DEFAULT_SRID,with_z=false,with_m=false)
14
+ super(srid,with_z,with_m)
15
+ @rings = []
16
+ end
17
+
18
+ #Delegate the unknown methods to the rings array
19
+ def method_missing(method_name,*args,&b)
20
+ @rings.send(method_name,*args,&b)
21
+ end
22
+
23
+ #Bounding box in 2D/3D. Returns an array of 2 points
24
+ def bounding_box
25
+ unless with_z
26
+ @rings[0].bounding_box
27
+ else
28
+ result = @rings[0].bounding_box #valid for x and y
29
+ max_z, min_z = result[1].z, result[0].z
30
+ 1.upto(size - 1) do |index|
31
+ bbox = @rings[index].bounding_box
32
+ sw = bbox[0]
33
+ ne = bbox[1]
34
+ max_z = ne.z if ne.z > max_z
35
+ min_z = sw.z if sw.z < min_z
36
+ end
37
+ result[1].z, result[0].z = max_z, min_z
38
+ result
39
+ end
40
+ end
41
+
42
+ def m_range
43
+ if with_m
44
+ max_m, min_m = -Float::MAX, Float::MAX
45
+ each do |lr|
46
+ lrmr = lr.m_range
47
+ max_m = lrmr[1] if lrmr[1] > max_m
48
+ min_m = lrmr[0] if lrmr[0] < min_m
49
+ end
50
+ [min_m,max_m]
51
+ else
52
+ [0,0]
53
+ end
54
+ end
55
+
56
+ #tests for other equality. The SRID is not taken into account.
57
+ def ==(other_polygon)
58
+ if other_polygon.class != self.class or
59
+ length != other_polygon.length
60
+ false
61
+ else
62
+ index=0
63
+ while index<length
64
+ return false if self[index] != other_polygon[index]
65
+ index+=1
66
+ end
67
+ true
68
+ end
69
+ end
70
+
71
+ #binary representation of a polygon, without the headers neccessary for a valid WKB string
72
+ def binary_representation(allow_z=true,allow_m=true)
73
+ rep = [length].pack("V")
74
+ each {|linear_ring| rep << linear_ring.binary_representation(allow_z,allow_m)}
75
+ rep
76
+ end
77
+
78
+ #WKB geometry type
79
+ def binary_geometry_type
80
+ 3
81
+ end
82
+
83
+ #Text representation of a polygon
84
+ def text_representation(allow_z=true,allow_m=true)
85
+ @rings.collect{|line_string| "(" + line_string.text_representation(allow_z,allow_m) + ")" }.join(",")
86
+ end
87
+
88
+ #WKT geometry type
89
+ def text_geometry_type
90
+ "POLYGON"
91
+ end
92
+
93
+ # Contains a point?
94
+ def contains_point?(point)
95
+ !@rings.select { |lr| lr.contains_point? point }.empty?
96
+ end
97
+
98
+ #georss simple representation : outputs only the outer ring
99
+ def georss_simple_representation(options)
100
+ georss_ns = options[:georss_ns] || "georss"
101
+ geom_attr = options[:geom_attr]
102
+ "<#{georss_ns}:polygon#{geom_attr}>" + self[0].georss_poslist + "</#{georss_ns}:polygon>\n"
103
+ end
104
+
105
+ #georss w3c representation : outputs the first point of the outer ring
106
+ def georss_w3cgeo_representation(options)
107
+ w3cgeo_ns = options[:w3cgeo_ns] || "geo"
108
+
109
+ "<#{w3cgeo_ns}:lat>#{self[0][0].y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{self[0][0].x}</#{w3cgeo_ns}:long>\n"
110
+ end
111
+ #georss gml representation
112
+ def georss_gml_representation(options)
113
+ georss_ns = options[:georss_ns] || "georss"
114
+ gml_ns = options[:gml_ns] || "gml"
115
+
116
+ 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"
117
+ end
118
+
119
+ #outputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
120
+ #<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
121
+ #it won't be used by GE anyway: clampToGround is the default)
122
+ def kml_representation(options = {})
123
+ result = "<Polygon#{options[:id_attr]}>\n"
124
+ result += options[:geom_data] if options[:geom_data]
125
+ rings.each_with_index do |ring, i|
126
+ if i == 0
127
+ boundary = "outerBoundaryIs"
128
+ else
129
+ boundary = "innerBoundaryIs"
130
+ end
131
+ result += "<#{boundary}><LinearRing><coordinates>\n"
132
+ result += ring.kml_poslist(options)
133
+ result += "\n</coordinates></LinearRing></#{boundary}>\n"
134
+ end
135
+ result += "</Polygon>\n"
136
+ end
137
+
138
+ def to_coordinates
139
+ rings.map{|lr| lr.to_coordinates}
140
+ end
141
+
142
+ # simple geojson representation
143
+ # TODO add CRS / SRID support?
144
+ def to_json(options = {})
145
+ {:type => 'Polygon',
146
+ :coordinates => self.to_coordinates}.to_json(options)
147
+ end
148
+ alias :as_geojson :to_json
149
+
150
+ #creates a new polygon. Accepts an array of linear strings as argument
151
+ def self.from_linear_rings(linear_rings,srid = DEFAULT_SRID,with_z=false,with_m=false)
152
+ polygon = new(srid,with_z,with_m)
153
+ polygon.concat(linear_rings)
154
+ polygon
155
+ end
156
+
157
+ #creates a new polygon. Accepts a sequence of points as argument : ((x,y)....(x,y)),((x,y).....(x,y))
158
+ def self.from_coordinates(point_sequences,srid=DEFAULT_SRID,with_z=false,with_m=false)
159
+ polygon = new(srid,with_z,with_m)
160
+ polygon.concat( point_sequences.map {|points| LinearRing.from_coordinates(points,srid,with_z,with_m) } )
161
+ polygon
162
+ end
163
+
164
+ #creates a new polygon from a list of Points (pt1....ptn),(pti....ptj)
165
+ def self.from_points(point_sequences, srid=DEFAULT_SRID,with_z=false,with_m=false)
166
+ polygon = new(srid,with_z,with_m)
167
+ polygon.concat( point_sequences.map {|points| LinearRing.from_points(points,srid,with_z,with_m) } )
168
+ polygon
169
+ end
170
+
171
+ end
172
+
173
+ end
174
+
175
+ end