rgeo-geojson 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ === 0.2.0 / 2010-12-07
2
+
3
+ * Initial public alpha release. Spun rgeo-geojson off from the core rgeo gem.
4
+
5
+ For earlier history, see the History file for the rgeo gem.
@@ -0,0 +1,104 @@
1
+ == RGeo::GeoJSON
2
+
3
+ RGeo::GeoJSON is an optional module for {RGeo}[http://github.com/dazuma/rgeo]
4
+ that provides GeoJSON encoding and decoding services.
5
+
6
+ === Summary
7
+
8
+ \RGeo is a key component for writing location-aware applications in the
9
+ Ruby programming language. At its core is an implementation of the
10
+ industry standard OGC Simple Features Specification, which provides data
11
+ representations of geometric objects such as points, lines, and polygons,
12
+ along with a set of geometric analysis operations. See the README for the
13
+ "rgeo" gem for more information.
14
+
15
+ RGeo::GeoJSON is an optional \RGeo add-on module that provides GeoJSON
16
+ encoding and decoding services. GeoJSON is an emerging standard format
17
+ used by many web services that need to communicate geospatial data. See
18
+ http://www.geojson.com for more information.
19
+
20
+ Example:
21
+
22
+ require 'rgeo/geo_json'
23
+
24
+ str1 = '{"type":"Point","coordinates":[1,2]}'
25
+ geom = RGeo::GeoJSON.decode(str1, :json_parser => :json)
26
+ geom.as_text # => "POINT(1.0 2.0)"
27
+
28
+ str2 = '{"type":"Feature","geometry":{"type":"Point","coordinates":[2.5,4.0]},"properties":{"color":"red"}}'
29
+ feature = RGeo::GeoJSON.decode(str2, :json_parser => :json)
30
+ feature['color'] # => 'red'
31
+ feature.geometry.as_text # => "POINT(2.5 4.0)"
32
+
33
+ hash = RGeo::GeoJSON.encode(feature)
34
+ hash.to_json == str2 # => true
35
+
36
+ === Installation
37
+
38
+ RGeo::GeoJSON has the following requirements:
39
+
40
+ * Ruby 1.8.7 or later. Ruby 1.9.2 or later preferred.
41
+ * \RGeo 0.2.0 or later.
42
+ * If you are using Ruby 1.8, you should install the "json" gem to support
43
+ parsing JSON strings. Ruby 1.9 has JSON support in its standard library
44
+ and does not require the gem.
45
+
46
+ Install RGeo::GeoJSON as a gem:
47
+
48
+ gem install rgeo
49
+ gem install rgeo-geojson
50
+
51
+ See the README for the "rgeo" gem, a required dependency, for further
52
+ installation information.
53
+
54
+ === To-do list
55
+
56
+ * Add support for the "bbox" and "crs" elements.
57
+
58
+ === Development and support
59
+
60
+ Documentation is available at http://virtuoso.rubyforge.org/rgeo-geojson/README_rdoc.html
61
+
62
+ Source code is hosted on Github at http://github.com/dazuma/rgeo-geojson
63
+
64
+ Contributions are welcome. Fork the project on Github.
65
+
66
+ Report bugs on Github issues at http://github.org/dazuma/rgeo-geojson/issues
67
+
68
+ Contact the author at dazuma at gmail dot com.
69
+
70
+ === Acknowledgments
71
+
72
+ \RGeo is written by Daniel Azuma (http://www.daniel-azuma.com).
73
+
74
+ Development of \RGeo is sponsored by GeoPage, Inc. (http://www.geopage.com).
75
+
76
+ === License
77
+
78
+ Copyright 2010 Daniel Azuma
79
+
80
+ All rights reserved.
81
+
82
+ Redistribution and use in source and binary forms, with or without
83
+ modification, are permitted provided that the following conditions are met:
84
+
85
+ * Redistributions of source code must retain the above copyright notice,
86
+ this list of conditions and the following disclaimer.
87
+ * Redistributions in binary form must reproduce the above copyright notice,
88
+ this list of conditions and the following disclaimer in the documentation
89
+ and/or other materials provided with the distribution.
90
+ * Neither the name of the copyright holder, nor the names of any other
91
+ contributors to this software, may be used to endorse or promote products
92
+ derived from this software without specific prior written permission.
93
+
94
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
95
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
96
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
97
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
98
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
99
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
100
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
101
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
102
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
103
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
104
+ POSSIBILITY OF SUCH DAMAGE.
data/Version ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,62 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # GeoJSON implementation for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ # Dependencies
38
+ require 'rgeo'
39
+
40
+
41
+ # RGeo is a spatial data library for Ruby, provided by the "rgeo" gem.
42
+ #
43
+ # The optional RGeo::GeoJSON module provides a set of tools for GeoJSON
44
+ # encoding and decoding.
45
+
46
+ module RGeo
47
+
48
+
49
+ # This is a namespace for a set of tools that provide GeoJSON encoding.
50
+ # See http://geojson.org/ for more information about this specification.
51
+
52
+ module GeoJSON
53
+ end
54
+
55
+
56
+ end
57
+
58
+
59
+ # Implementation files
60
+ require 'rgeo/geo_json/entities'
61
+ require 'rgeo/geo_json/coder'
62
+ require 'rgeo/geo_json/interface'
@@ -0,0 +1,401 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # GeoJSON encoder object
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module GeoJSON
40
+
41
+
42
+ # This object encapsulates encoding and decoding settings (principally
43
+ # the RGeo::Feature::Factory and the RGeo::GeoJSON::EntityFactory to
44
+ # be used) so that you can encode and decode without specifying those
45
+ # settings every time.
46
+
47
+ class Coder
48
+
49
+
50
+ @@json_available = nil
51
+ @@yajl_available = nil
52
+ @@activesupport_available = nil
53
+
54
+
55
+ # Create a new coder settings object. The geo factory is passed as
56
+ # a required argument.
57
+ #
58
+ # Options include:
59
+ #
60
+ # <tt>:geo_factory</tt>::
61
+ # Specifies the geo factory to use to create geometry objects.
62
+ # Defaults to the preferred cartesian factory.
63
+ # <tt>:entity_factory</tt>::
64
+ # Specifies an entity factory, which lets you override the types
65
+ # of GeoJSON entities that are created. It defaults to the default
66
+ # RGeo::GeoJSON::EntityFactory, which generates objects of type
67
+ # RGeo::GeoJSON::Feature or RGeo::GeoJSON::FeatureCollection.
68
+ # See RGeo::GeoJSON::EntityFactory for more information.
69
+ # <tt>:json_parser</tt>::
70
+ # Specifies a JSON parser to use when decoding a String or IO
71
+ # object. The value may be a Proc object taking the string as the
72
+ # sole argument and returning the JSON hash, or it may be one of
73
+ # the special values <tt>:json</tt>, <tt>:yajl</tt>, or
74
+ # <tt>:active_support</tt>. Setting one of those special values
75
+ # will require the corresponding library to be available. Note
76
+ # that the <tt>:json</tt> library is present in the standard
77
+ # library in Ruby 1.9, but requires the "json" gem in Ruby 1.8.
78
+ # If a parser is not specified, then the decode method will not
79
+ # accept a String or IO object; it will require a Hash.
80
+
81
+ def initialize(opts_={})
82
+ @geo_factory = opts_[:geo_factory] || ::RGeo::Cartesian.preferred_factory
83
+ @entity_factory = opts_[:entity_factory] || EntityFactory.instance
84
+ @json_parser = opts_[:json_parser]
85
+ case @json_parser
86
+ when :json
87
+ if @@json_available.nil?
88
+ begin
89
+ require 'json'
90
+ @@json_available = true
91
+ rescue ::LoadError
92
+ @@json_available = false
93
+ end
94
+ end
95
+ if @@json_available
96
+ @json_parser = ::Proc.new{ |str_| ::JSON.parse(str_) }
97
+ else
98
+ raise Error::RGeoError, "JSON library is not available. You may need to install the 'json' gem."
99
+ end
100
+ when :yajl
101
+ if @@yajl_available.nil?
102
+ begin
103
+ require 'yajl'
104
+ @@yajl_available = true
105
+ rescue ::LoadError
106
+ @@yajl_available = false
107
+ end
108
+ end
109
+ if @@yajl_available
110
+ @json_parser = ::Proc.new{ |str_| ::Yajl::Parser.new.parse(str_) }
111
+ else
112
+ raise Error::RGeoError, "Yajl library is not available. You may need to install the 'yajl' gem."
113
+ end
114
+ when :active_support
115
+ if @@activesupport_available.nil?
116
+ begin
117
+ require 'active_support/json'
118
+ @@activesupport_available = true
119
+ rescue ::LoadError
120
+ @@activesupport_available = false
121
+ end
122
+ end
123
+ if @@activesupport_available
124
+ @json_parser = ::Proc.new{ |str_| ::ActiveSupport::JSON.decode(str_) }
125
+ else
126
+ raise Error::RGeoError, "ActiveSupport::JSON library is not available. You may need to install the 'activesupport' gem."
127
+ end
128
+ when ::Proc, nil
129
+ # Leave as is
130
+ else
131
+ raise ::ArgumentError, "Unrecognzied json_parser: #{@json_parser.inspect}"
132
+ end
133
+ @num_coordinates = 2
134
+ @num_coordinates += 1 if @geo_factory.property(:has_z_coordinate)
135
+ @num_coordinates += 1 if @geo_factory.property(:has_m_coordinate)
136
+ end
137
+
138
+
139
+ # Encode the given object as GeoJSON. The object may be one of the
140
+ # geometry objects specified in RGeo::Feature, or an appropriate
141
+ # GeoJSON wrapper entity supported by this coder's entity factory.
142
+ #
143
+ # This method returns a JSON object (i.e. a hash). In order to
144
+ # generate a string suitable for transmitting to a service, you
145
+ # will need to JSON-encode it. This is usually accomplished by
146
+ # calling <tt>to_json</tt> on the hash object, if you have the
147
+ # appropriate JSON library installed.
148
+
149
+ def encode(object_)
150
+ if @entity_factory.is_feature_collection?(object_)
151
+ {
152
+ 'type' => 'FeatureCollection',
153
+ 'features' => @entity_factory.map_feature_collection(object_){ |f_| _encode_feature(f_) },
154
+ }
155
+ elsif @entity_factory.is_feature?(object_)
156
+ _encode_feature(object_)
157
+ else
158
+ _encode_geometry(object_)
159
+ end
160
+ end
161
+
162
+
163
+ # Decode an object from GeoJSON. The input may be a JSON hash, a
164
+ # String, or an IO object from which to read the JSON string.
165
+ # If an error occurs, nil is returned.
166
+
167
+ def decode(input_)
168
+ if input_.kind_of?(::IO)
169
+ input_ = input_.read rescue nil
170
+ end
171
+ if input_.kind_of?(::String)
172
+ input_ = @json_parser.call(input_) rescue nil
173
+ end
174
+ unless input_.kind_of?(::Hash)
175
+ return nil
176
+ end
177
+ case input_['type']
178
+ when 'FeatureCollection'
179
+ features_ = input_['features']
180
+ features_ = [] unless features_.kind_of?(::Array)
181
+ decoded_features_ = []
182
+ features_.each do |f_|
183
+ if f_['type'] == 'Feature'
184
+ decoded_features_ << _decode_feature(f_)
185
+ end
186
+ end
187
+ @entity_factory.feature_collection(decoded_features_)
188
+ when 'Feature'
189
+ _decode_feature(input_)
190
+ else
191
+ _decode_geometry(input_)
192
+ end
193
+ end
194
+
195
+
196
+ # Returns the RGeo::Feature::Factory used to generate geometry objects.
197
+
198
+ def geo_factory
199
+ @geo_factory
200
+ end
201
+
202
+
203
+ # Returns the RGeo::GeoJSON::EntityFactory used to generate GeoJSON
204
+ # wrapper entities.
205
+
206
+ def entity_factory
207
+ @entity_factory
208
+ end
209
+
210
+
211
+ def _encode_feature(object_) # :nodoc:
212
+ json_ = {
213
+ 'type' => 'Feature',
214
+ 'geometry' => _encode_geometry(@entity_factory.get_feature_geometry(object_)),
215
+ 'properties' => @entity_factory.get_feature_properties(object_).dup,
216
+ }
217
+ id_ = @entity_factory.get_feature_id(object_)
218
+ json_['id'] = id_ if id_
219
+ json_
220
+ end
221
+
222
+
223
+ def _encode_geometry(object_, point_encoder_=nil) # :nodoc:
224
+ unless point_encoder_
225
+ if object_.factory.property(:has_z_coordinate)
226
+ if object_.factory.property(:has_m_coordinate)
227
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y, p_.z, p_.m] }
228
+ else
229
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y, p_.z] }
230
+ end
231
+ else
232
+ if object_.factory.property(:has_m_coordinate)
233
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y, p_.m] }
234
+ else
235
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y] }
236
+ end
237
+ end
238
+ end
239
+ case object_
240
+ when ::RGeo::Feature::Point
241
+ {
242
+ 'type' => 'Point',
243
+ 'coordinates' => point_encoder_.call(object_),
244
+ }
245
+ when ::RGeo::Feature::LineString
246
+ {
247
+ 'type' => 'LineString',
248
+ 'coordinates' => object_.points.map(&point_encoder_),
249
+ }
250
+ when ::RGeo::Feature::Polygon
251
+ {
252
+ 'type' => 'Polygon',
253
+ 'coordinates' => [object_.exterior_ring.points.map(&point_encoder_)] + object_.interior_rings.map{ |r_| r_.points.map(&point_encoder_) }
254
+ }
255
+ when ::RGeo::Feature::MultiPoint
256
+ {
257
+ 'type' => 'MultiPoint',
258
+ 'coordinates' => object_.map(&point_encoder_),
259
+ }
260
+ when ::RGeo::Feature::MultiLineString
261
+ {
262
+ 'type' => 'MultiLineString',
263
+ 'coordinates' => object_.map{ |ls_| ls_.points.map(&point_encoder_) },
264
+ }
265
+ when ::RGeo::Feature::MultiPolygon
266
+ {
267
+ 'type' => 'MultiPolygon',
268
+ 'coordinates' => object_.map{ |poly_| [poly_.exterior_ring.points.map(&point_encoder_)] + poly_.interior_rings.map{ |r_| r_.points.map(&point_encoder_) } },
269
+ }
270
+ when ::RGeo::Feature::GeometryCollection
271
+ {
272
+ 'type' => 'GeometryCollection',
273
+ 'geometries' => object_.map{ |geom_| _encode_geometry(geom_, point_encoder_) },
274
+ }
275
+ else
276
+ nil
277
+ end
278
+ end
279
+
280
+
281
+ def _decode_feature(input_) # :nodoc:
282
+ geometry_ = input_['geometry']
283
+ if geometry_
284
+ geometry_ = _decode_geometry(geometry_)
285
+ return nil unless geometry_
286
+ end
287
+ @entity_factory.feature(geometry_, input_['id'], input_['properties'])
288
+ end
289
+
290
+
291
+ def _decode_geometry(input_) # :nodoc:
292
+ case input_['type']
293
+ when 'GeometryCollection'
294
+ _decode_geometry_collection(input_)
295
+ when 'Point'
296
+ _decode_point_coords(input_['coordinates'])
297
+ when 'LineString'
298
+ _decode_line_string_coords(input_['coordinates'])
299
+ when 'Polygon'
300
+ _decode_polygon_coords(input_['coordinates'])
301
+ when 'MultiPoint'
302
+ _decode_multi_point_coords(input_['coordinates'])
303
+ when 'MultiLineString'
304
+ _decode_multi_line_string_coords(input_['coordinates'])
305
+ when 'MultiPolygon'
306
+ _decode_multi_polygon_coords(input_['coordinates'])
307
+ else
308
+ nil
309
+ end
310
+ end
311
+
312
+
313
+ def _decode_geometry_collection(input_) # :nodoc:
314
+ geometries_ = input_['geometries']
315
+ geometries_ = [] unless geometries_.kind_of?(::Array)
316
+ decoded_geometries_ = []
317
+ geometries_.each do |g_|
318
+ g_ = _decode_geometry(g_)
319
+ decoded_geometries_ << g_ if g_
320
+ end
321
+ @geo_factory.collection(decoded_geometries_)
322
+ end
323
+
324
+
325
+ def _decode_point_coords(point_coords_) # :nodoc:
326
+ return nil unless point_coords_.kind_of?(::Array)
327
+ @geo_factory.point(*(point_coords_[0...@num_coordinates].map{ |c_| c_.to_f })) rescue nil
328
+ end
329
+
330
+
331
+ def _decode_line_string_coords(line_coords_) # :nodoc:
332
+ return nil unless line_coords_.kind_of?(::Array)
333
+ points_ = []
334
+ line_coords_.each do |point_coords_|
335
+ point_ = _decode_point_coords(point_coords_)
336
+ points_ << point_ if point_
337
+ end
338
+ @geo_factory.line_string(points_)
339
+ end
340
+
341
+
342
+ def _decode_polygon_coords(poly_coords_) # :nodoc:
343
+ return nil unless poly_coords_.kind_of?(::Array)
344
+ rings_ = []
345
+ poly_coords_.each do |ring_coords_|
346
+ return nil unless ring_coords_.kind_of?(::Array)
347
+ points_ = []
348
+ ring_coords_.each do |point_coords_|
349
+ point_ = _decode_point_coords(point_coords_)
350
+ points_ << point_ if point_
351
+ end
352
+ ring_ = @geo_factory.linear_ring(points_)
353
+ rings_ << ring_ if ring_
354
+ end
355
+ if rings_.size == 0
356
+ nil
357
+ else
358
+ @geo_factory.polygon(rings_[0], rings_[1..-1])
359
+ end
360
+ end
361
+
362
+
363
+ def _decode_multi_point_coords(multi_point_coords_) # :nodoc:
364
+ return nil unless multi_point_coords_.kind_of?(::Array)
365
+ points_ = []
366
+ multi_point_coords_.each do |point_coords_|
367
+ point_ = _decode_point_coords(point_coords_)
368
+ points_ << point_ if point_
369
+ end
370
+ @geo_factory.multi_point(points_)
371
+ end
372
+
373
+
374
+ def _decode_multi_line_string_coords(multi_line_coords_) # :nodoc:
375
+ return nil unless multi_line_coords_.kind_of?(::Array)
376
+ lines_ = []
377
+ multi_line_coords_.each do |line_coords_|
378
+ line_ = _decode_line_string_coords(line_coords_)
379
+ lines_ << line_ if line_
380
+ end
381
+ @geo_factory.multi_line_string(lines_)
382
+ end
383
+
384
+
385
+ def _decode_multi_polygon_coords(multi_polygon_coords_) # :nodoc:
386
+ return nil unless multi_polygon_coords_.kind_of?(::Array)
387
+ polygons_ = []
388
+ multi_polygon_coords_.each do |poly_coords_|
389
+ poly_ = _decode_polygon_coords(poly_coords_)
390
+ polygons_ << poly_ if poly_
391
+ end
392
+ @geo_factory.multi_polygon(polygons_)
393
+ end
394
+
395
+
396
+ end
397
+
398
+
399
+ end
400
+
401
+ end