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,336 @@
1
+ require 'geo_ruby/base/point'
2
+ require 'geo_ruby/base/line_string'
3
+ require 'geo_ruby/base/linear_ring'
4
+ require 'geo_ruby/base/polygon'
5
+ require 'geo_ruby/base/multi_point'
6
+ require 'geo_ruby/base/multi_line_string'
7
+ require 'geo_ruby/base/multi_polygon'
8
+ require 'geo_ruby/base/geometry_collection'
9
+
10
+ require 'strscan'
11
+
12
+ module GeoRuby
13
+ module Base
14
+
15
+ #Raised when an error in the EWKT string is detected
16
+ class EWKTFormatError < StandardError
17
+ end
18
+
19
+ #Parses EWKT strings and notifies of events (such as the beginning of the definition of geometry, the value of the SRID...) the factory passed as argument to the constructor.
20
+ #
21
+ #=Example
22
+ # factory = GeometryFactory::new
23
+ # ewkt_parser = EWKTParser::new(factory)
24
+ # ewkt_parser.parse(<EWKT String>)
25
+ # geometry = @factory.geometry
26
+ #
27
+ #You can also use directly the static method Geometry.from_ewkt
28
+ class EWKTParser
29
+
30
+ def initialize(factory)
31
+ @factory = factory
32
+ @parse_options ={
33
+ "POINT" => method(:parse_point),
34
+ "LINESTRING" => method(:parse_line_string),
35
+ "POLYGON" => method(:parse_polygon),
36
+ "MULTIPOINT" => method(:parse_multi_point),
37
+ "MULTILINESTRING" => method(:parse_multi_line_string),
38
+ "MULTIPOLYGON" => method(:parse_multi_polygon),
39
+ "GEOMETRYCOLLECTION" => method(:parse_geometry_collection)
40
+ }
41
+ end
42
+
43
+ #Parses the ewkt string passed as argument and notifies the factory of events
44
+ def parse(ewkt)
45
+ @factory.reset
46
+ @tokenizer_structure = TokenizerStructure.new(ewkt)
47
+ @with_z=false
48
+ @with_m=false
49
+ @is_3dm = false
50
+ parse_geometry(true)
51
+ @srid=nil
52
+ end
53
+
54
+ private
55
+ def parse_geometry(srid_allowed)
56
+
57
+ token = @tokenizer_structure.get_next_token
58
+ if token == 'SRID'
59
+ #SRID present
60
+ raise EWKTFormatError.new("SRID not allowed at this position") if(!srid_allowed)
61
+ if @tokenizer_structure.get_next_token != '='
62
+ raise EWKTFormatError.new("Invalid SRID expression")
63
+ else
64
+ @srid = @tokenizer_structure.get_next_token.to_i
65
+ raise EWKTFormatError.new("Invalid SRID separator") if @tokenizer_structure.get_next_token != ';'
66
+ geom_type = @tokenizer_structure.get_next_token
67
+ end
68
+
69
+ else
70
+ #to manage multi geometries : the srid is not present in sub_geometries, therefore we take the srid of the parent ; if it is the root, we take the default srid
71
+ @srid= @srid || @@default_srid
72
+ geom_type = token
73
+ end
74
+
75
+ if geom_type[-1] == ?M
76
+ @is_3dm=true
77
+ @with_m=true
78
+ geom_type.chop! #remove the M
79
+ end
80
+
81
+ if @parse_options.has_key?(geom_type)
82
+ @parse_options[geom_type].call
83
+ else
84
+ raise EWKTFormatError.new("Urecognized geometry type: #{geom_type}")
85
+ end
86
+ end
87
+
88
+ def parse_geometry_collection
89
+ if @tokenizer_structure.get_next_token !='('
90
+ raise EWKTFormatError.new('Invalid GeometryCollection')
91
+ end
92
+
93
+ @factory.begin_geometry(GeometryCollection,@srid)
94
+
95
+ token = ''
96
+ while token != ')'
97
+ parse_geometry(false)
98
+ token = @tokenizer_structure.get_next_token
99
+ if token.nil?
100
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
101
+ end
102
+ end
103
+
104
+ @factory.end_geometry(@with_z,@with_m)
105
+ end
106
+
107
+ def parse_multi_polygon
108
+ if @tokenizer_structure.get_next_token !='('
109
+ raise EWKTFormatError.new('Invalid MultiLineString')
110
+ end
111
+
112
+ @factory.begin_geometry(MultiPolygon,@srid)
113
+ token = ''
114
+ while token != ')'
115
+ parse_polygon
116
+ token = @tokenizer_structure.get_next_token
117
+ if token.nil?
118
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
119
+ end
120
+ end
121
+
122
+ @factory.end_geometry(@with_z,@with_m)
123
+ end
124
+
125
+ def parse_multi_line_string
126
+ if @tokenizer_structure.get_next_token !='('
127
+ raise EWKTFormatError.new('Invalid MultiLineString')
128
+ end
129
+
130
+ @factory.begin_geometry(MultiLineString,@srid)
131
+
132
+ token = ''
133
+ while token != ')'
134
+ parse_line_string
135
+ token = @tokenizer_structure.get_next_token
136
+ if token.nil?
137
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
138
+ end
139
+ end
140
+
141
+ @factory.end_geometry(@with_z,@with_m)
142
+ end
143
+
144
+ def parse_polygon
145
+ if @tokenizer_structure.get_next_token !='('
146
+ raise EWKTFormatError.new('Invalid Polygon')
147
+ end
148
+
149
+ @factory.begin_geometry(Polygon,@srid)
150
+
151
+ token = ''
152
+ while token != ')'
153
+ parse_linear_ring
154
+ token = @tokenizer_structure.get_next_token
155
+ if token.nil?
156
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
157
+ end
158
+ end
159
+
160
+ @factory.end_geometry(@with_z,@with_m)
161
+ end
162
+
163
+ #must support the PostGIS form and the one in the specification
164
+ def parse_multi_point
165
+ if @tokenizer_structure.get_next_token !='('
166
+ raise EWKTFormatError.new('Invalid MultiPoint')
167
+ end
168
+
169
+ token = @tokenizer_structure.check_next_token
170
+ if token == '('
171
+ #specification
172
+ @factory.begin_geometry(MultiPoint,@srid)
173
+
174
+ token = ''
175
+ while token != ')'
176
+ parse_point
177
+ token = @tokenizer_structure.get_next_token
178
+ if token.nil?
179
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
180
+ end
181
+ end
182
+
183
+ @factory.end_geometry(@with_z,@with_m)
184
+ else
185
+ #postgis
186
+ parse_point_list(MultiPoint)
187
+ end
188
+ end
189
+
190
+ def parse_linear_ring
191
+ if @tokenizer_structure.get_next_token !='('
192
+ raise EWKTFormatError.new('Invalid Linear ring')
193
+ end
194
+
195
+ parse_point_list(LinearRing)
196
+ end
197
+
198
+ def parse_line_string
199
+ if @tokenizer_structure.get_next_token !='('
200
+ raise EWKTFormatError.new('Invalid Line string')
201
+ end
202
+
203
+ parse_point_list(LineString)
204
+ end
205
+
206
+ #used to parse line_strings and linear_rings and the PostGIS form of multi_points
207
+ def parse_point_list(geometry_type)
208
+ @factory.begin_geometry(geometry_type,@srid)
209
+
210
+ token = ''
211
+ while token != ')'
212
+ @factory.begin_geometry(Point,@srid)
213
+ token = parse_coords
214
+ if token.nil?
215
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
216
+ end
217
+ @factory.end_geometry(@with_z,@with_m)
218
+ end
219
+
220
+ @factory.end_geometry(@with_z,@with_m)
221
+ end
222
+
223
+ def parse_point
224
+ if @tokenizer_structure.get_next_token !='('
225
+ raise EWKTFormatError.new('Invalid Point')
226
+ end
227
+
228
+ @factory.begin_geometry(Point,@srid)
229
+
230
+ token = parse_coords
231
+
232
+ if token != ')'
233
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
234
+ end
235
+
236
+ @factory.end_geometry(@with_z,@with_m)
237
+ end
238
+
239
+ def parse_coords
240
+ coords = Array.new
241
+ x = @tokenizer_structure.get_next_token
242
+ y = @tokenizer_structure.get_next_token
243
+
244
+ if x.nil? or y.nil?
245
+ raise EWKTFormatError.new("Bad Point format")
246
+ end
247
+
248
+ if @is_3dm
249
+ m = @tokenizer_structure.get_next_token
250
+
251
+ if m.nil? or m == ',' or m == ')'
252
+ raise EWKTFormatError.new("No M dimension found")
253
+ else
254
+ @factory.add_point_x_y_m(x.to_f,y.to_f,m.to_f)
255
+ @tokenizer_structure.get_next_token
256
+ end
257
+ else
258
+ z = @tokenizer_structure.get_next_token
259
+
260
+ if z.nil?
261
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
262
+ end
263
+
264
+ if z == ',' or z == ')'
265
+ #2D : no z no m
266
+ @factory.add_point_x_y(x.to_f,y.to_f)
267
+ z
268
+ else
269
+ m = @tokenizer_structure.get_next_token
270
+ if m.nil?
271
+ raise EWKTFormatError.new("EWKT string not correctly terminated")
272
+ end
273
+
274
+ if m == ',' or m ==')'
275
+ #3Dz : no m
276
+ @with_z = true
277
+ @factory.add_point_x_y_z(x.to_f,y.to_f,z.to_f)
278
+ m
279
+ else
280
+ #4D
281
+ @with_z = true
282
+ @with_m = true
283
+ @factory.add_point_x_y_z_m(x.to_f,y.to_f,z.to_f,m.to_f)
284
+ @tokenizer_structure.get_next_token
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ class TokenizerStructure
292
+
293
+ def initialize(ewkt)
294
+ @ewkt = ewkt
295
+ @scanner = StringScanner.new(ewkt)
296
+ @regex = /\s*([\w.-]+)s*/
297
+ end
298
+
299
+ def get_next_token
300
+ if @scanner.scan(@regex).nil?
301
+ if @scanner.eos?
302
+ nil
303
+ else
304
+ ch = @scanner.getch
305
+ while ch == ' '
306
+ ch = @scanner.getch
307
+ end
308
+ ch
309
+ end
310
+ else
311
+ @scanner[1]
312
+ end
313
+ end
314
+
315
+
316
+ def check_next_token
317
+ check = @scanner.check(@regex)
318
+ if check.nil?
319
+ if @scanner.eos?
320
+ nil
321
+ else
322
+ pos = @scanner.pos
323
+ while @ewkt[pos].chr == ' '
324
+ pos+=1
325
+ end
326
+ @ewkt[pos].chr
327
+ end
328
+ else
329
+ check
330
+ end
331
+ end
332
+
333
+ end
334
+
335
+ end
336
+ end
@@ -0,0 +1,234 @@
1
+ module GeoRuby#:nodoc:
2
+ module Base
3
+ #arbitrary default SRID
4
+ @@default_srid = 4326
5
+
6
+ def self.default_srid
7
+ @@default_srid
8
+ end
9
+ def self.srid=(srid)
10
+ @@default_srid = srid
11
+ end
12
+
13
+ #Root of all geometric data classes.
14
+ #Objects of class Geometry should not be instantiated.
15
+ class Geometry
16
+ #SRID of the geometry
17
+ attr_reader :srid #writer defined below
18
+ #Flag indicating if the z ordinate of the geometry is meaningful
19
+ attr_accessor :with_z
20
+ alias :with_z? :with_z
21
+ #Flag indicating if the m ordinate of the geometry is meaningful
22
+ attr_accessor :with_m
23
+ alias :with_m? :with_m
24
+
25
+ def initialize(srid=@@default_srid,with_z=false,with_m=false)
26
+ @srid=srid
27
+ @with_z=with_z
28
+ @with_m=with_m
29
+ end
30
+
31
+ def srid=(new_srid)
32
+ @srid = new_srid
33
+ unless self.is_a?(Point)
34
+ self.each do |geom|
35
+ geom.srid=new_srid
36
+ end
37
+ end
38
+ end
39
+
40
+ #to be implemented in subclasses
41
+ def bounding_box
42
+ end
43
+
44
+ #to be implemented in subclasses
45
+ def m_range
46
+ end
47
+
48
+ #Returns an Envelope object for the geometry
49
+ def envelope
50
+ Envelope.from_points(bounding_box,srid,with_z)
51
+ end
52
+
53
+ #Outputs the geometry as an EWKB string.
54
+ #The +allow_srid+, +allow_z+ and +allow_m+ arguments allow the output to include srid, z and m respectively if they are present in the geometry. If these arguments are set to false, srid, z and m are not included, even if they are present in the geometry. By default, the output string contains all the information in the object.
55
+ def as_ewkb(allow_srid=true,allow_z=true,allow_m=true)
56
+ ewkb="";
57
+
58
+ ewkb << 1.chr #little_endian by default
59
+
60
+ type= binary_geometry_type
61
+ if @with_z and allow_z
62
+ type = type | Z_MASK
63
+ end
64
+ if @with_m and allow_m
65
+ type = type | M_MASK
66
+ end
67
+ if allow_srid
68
+ type = type | SRID_MASK
69
+ ewkb << [type,@srid].pack("VV")
70
+ else
71
+ ewkb << [type].pack("V")
72
+ end
73
+
74
+ ewkb << binary_representation(allow_z,allow_m)
75
+ end
76
+
77
+ #Outputs the geometry as a strict WKB string.
78
+ def as_wkb
79
+ as_ewkb(false,false,false)
80
+ end
81
+
82
+ #Outputs the geometry as a HexEWKB string. It is almost the same as a WKB string, except that each byte of a WKB string is replaced by its hexadecimal 2-character representation in a HexEWKB string.
83
+ def as_hex_ewkb(allow_srid=true,allow_z=true,allow_m=true)
84
+ as_ewkb(allow_srid, allow_z, allow_m).unpack('H*').join('').upcase
85
+ end
86
+
87
+ #Outputs the geometry as a strict HexWKB string
88
+ def as_hex_wkb
89
+ as_hex_ewkb(false,false,false)
90
+ end
91
+
92
+ #Outputs the geometry as an EWKT string.
93
+ def as_ewkt(allow_srid=true,allow_z=true,allow_m=true)
94
+ if allow_srid
95
+ ewkt="SRID=#{@srid};"
96
+ else
97
+ ewkt=""
98
+ end
99
+ ewkt << text_geometry_type
100
+ ewkt << "M" if @with_m and allow_m and (!@with_z or !allow_z) #to distinguish the M from the Z when there is actually no Z...
101
+ ewkt << "(" << text_representation(allow_z,allow_m) << ")"
102
+ end
103
+
104
+ #Outputs the geometry as strict WKT string.
105
+ def as_wkt
106
+ as_ewkt(false,false,false)
107
+ end
108
+
109
+ #Outputs the geometry in georss format.
110
+ #Assumes the geometries are in latlon format, with x as lon and y as lat.
111
+ #Pass the <tt>:dialect</tt> option to swhit format. Possible values are: <tt>:simple</tt> (default), <tt>:w3cgeo</tt> and <tt>:gml</tt>.
112
+ def as_georss(options = {})
113
+ dialect= options[:dialect] || :simple
114
+ case(dialect)
115
+ when :simple
116
+ geom_attr = ""
117
+ geom_attr += " featuretypetag=\"#{options[:featuretypetag]}\"" if options[:featuretypetag]
118
+ geom_attr += " relationshiptag=\"#{options[:relationshiptag]}\"" if options[:relationshiptag]
119
+ geom_attr += " floor=\"#{options[:floor]}\"" if options[:floor]
120
+ geom_attr += " radius=\"#{options[:radius]}\"" if options[:radius]
121
+ geom_attr += " elev=\"#{options[:elev]}\"" if options[:elev]
122
+ georss_simple_representation(options.merge(:geom_attr => geom_attr))
123
+ when :w3cgeo
124
+ georss_w3cgeo_representation(options)
125
+ when :gml
126
+ georss_gml_representation(options)
127
+ end
128
+ end
129
+
130
+ #Iutputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
131
+ #<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
132
+ #it won't be used by GE anyway: clampToGround is the default)
133
+ def as_kml(options = {})
134
+ id_attr = ""
135
+ id_attr = " id=\"#{options[:id]}\"" if options[:id]
136
+
137
+ geom_data = ""
138
+ geom_data += "<extrude>#{options[:extrude]}</extrude>\n" if options[:extrude]
139
+ geom_data += "<tesselate>#{options[:tesselate]}</tesselate>\n" if options[:tesselate]
140
+ geom_data += "<altitudeMode>#{options[:altitude_mode]}</altitudeMode>\n" if options[:altitude_mode]
141
+
142
+ allow_z = (with_z || !options[:altitude].nil? )&& (!options[:altitude_mode].nil?) && options[:atitude_mode] != "clampToGround"
143
+ fixed_z = options[:altitude]
144
+
145
+ kml_representation(options.merge(:id_attr => id_attr, :geom_data => geom_data, :allow_z => allow_z, :fixed_z => fixed_z))
146
+ end
147
+
148
+ #Creates a geometry based on a EWKB string. The actual class returned depends of the content of the string passed as argument. Since WKB strings are a subset of EWKB, they are also valid.
149
+ def self.from_ewkb(ewkb)
150
+ factory = GeometryFactory::new
151
+ ewkb_parser= EWKBParser::new(factory)
152
+ ewkb_parser.parse(ewkb)
153
+ factory.geometry
154
+ end
155
+ #Creates a geometry based on a HexEWKB string
156
+ def self.from_hex_ewkb(hexewkb)
157
+ factory = GeometryFactory::new
158
+ hexewkb_parser= HexEWKBParser::new(factory)
159
+ hexewkb_parser.parse(hexewkb)
160
+ factory.geometry
161
+ end
162
+ #Creates a geometry based on a EWKT string. Since WKT strings are a subset of EWKT, they are also valid.
163
+ def self.from_ewkt(ewkt)
164
+ factory = GeometryFactory::new
165
+ ewkt_parser= EWKTParser::new(factory)
166
+ ewkt_parser.parse(ewkt)
167
+ factory.geometry
168
+ end
169
+
170
+ #sends back a geometry based on the GeoRSS string passed as argument
171
+ def self.from_georss(georss)
172
+ georss_parser= GeorssParser::new
173
+ georss_parser.parse(georss)
174
+ georss_parser.geometry
175
+ end
176
+ #sends back an array: The first element is the goemetry based on the GeoRSS string passed as argument. The second one is the GeoRSSTags (found only with the Simple format)
177
+ def self.from_georss_with_tags(georss)
178
+ georss_parser= GeorssParser::new
179
+ georss_parser.parse(georss,true)
180
+ [georss_parser.geometry, georss_parser.georss_tags]
181
+ end
182
+
183
+ #Sends back a geometry from a KML encoded geometry string.
184
+ #Limitations : Only supports points, linestrings and polygons (no collection for now).
185
+ #Addapted from Pramukta's code
186
+ def self.from_kml(kml)
187
+ return GeoRuby::Base::Geometry.from_ewkt(kml_to_wkt(kml))
188
+ end
189
+
190
+ require 'rexml/document'
191
+ def self.kml_to_wkt(kml)
192
+ doc = REXML::Document.new(kml)
193
+ wkt = ""
194
+ if ["Point", "LineString", "Polygon" ].include?(doc.root.name)
195
+ case doc.root.name
196
+ when "Point" then
197
+ coords = doc.elements["/Point/coordinates"].text.gsub(/\n/," ")
198
+ wkt = doc.root.name.upcase + "(" + split_coords(coords).join(' ') + ")"
199
+ when "LineString" then
200
+ coords = doc.elements["/LineString/coordinates"].text.gsub(/\n/," ")
201
+ coords = split_coords(coords)
202
+ wkt = doc.root.name.upcase + "(" + coords.join(",") + ")"
203
+ when "Polygon" then
204
+ # polygons have one outer ring and zero or more inner rings
205
+ bounds = []
206
+ bounds << doc.elements["/Polygon/outerBoundaryIs/LinearRing/coordinates"].text
207
+ inner_coords_elements = doc.elements.each("/Polygon/innerBoundaryIs/LinearRing/coordinates") do |inner_coords|
208
+ inner_coords = inner_coords.text
209
+ bounds << inner_coords
210
+ end
211
+
212
+ wkt = doc.root.name.upcase + "(" + bounds.map do |bound|
213
+ bound.gsub!(/\n/, " ")
214
+ bound = split_coords(bound)
215
+ if bound.first != bound.last
216
+ bound.push bound.first
217
+ end
218
+ "(" + bound.join(",") + ")"
219
+ end.join(",") + ")"
220
+ end
221
+ end
222
+ return wkt
223
+ end
224
+
225
+ private
226
+
227
+ def self.split_coords(coords)
228
+ coords.split(" ").collect { |coord|
229
+ coord.gsub(","," ")
230
+ }
231
+ end
232
+ end
233
+ end
234
+ end