schleyfox-rgeo 0.2.5

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 (150) hide show
  1. data/History.rdoc +199 -0
  2. data/README.rdoc +172 -0
  3. data/Spatial_Programming_With_RGeo.rdoc +440 -0
  4. data/Version +1 -0
  5. data/ext/geos_c_impl/extconf.rb +84 -0
  6. data/ext/geos_c_impl/factory.c +468 -0
  7. data/ext/geos_c_impl/factory.h +224 -0
  8. data/ext/geos_c_impl/geometry.c +705 -0
  9. data/ext/geos_c_impl/geometry.h +55 -0
  10. data/ext/geos_c_impl/geometry_collection.c +482 -0
  11. data/ext/geos_c_impl/geometry_collection.h +69 -0
  12. data/ext/geos_c_impl/line_string.c +509 -0
  13. data/ext/geos_c_impl/line_string.h +64 -0
  14. data/ext/geos_c_impl/main.c +70 -0
  15. data/ext/geos_c_impl/point.c +193 -0
  16. data/ext/geos_c_impl/point.h +62 -0
  17. data/ext/geos_c_impl/polygon.c +265 -0
  18. data/ext/geos_c_impl/polygon.h +66 -0
  19. data/ext/geos_c_impl/preface.h +50 -0
  20. data/ext/proj4_c_impl/extconf.rb +88 -0
  21. data/ext/proj4_c_impl/main.c +271 -0
  22. data/lib/rgeo.rb +124 -0
  23. data/lib/rgeo/cartesian.rb +60 -0
  24. data/lib/rgeo/cartesian/analysis.rb +118 -0
  25. data/lib/rgeo/cartesian/bounding_box.rb +337 -0
  26. data/lib/rgeo/cartesian/calculations.rb +161 -0
  27. data/lib/rgeo/cartesian/factory.rb +209 -0
  28. data/lib/rgeo/cartesian/feature_classes.rb +173 -0
  29. data/lib/rgeo/cartesian/feature_methods.rb +106 -0
  30. data/lib/rgeo/cartesian/interface.rb +150 -0
  31. data/lib/rgeo/coord_sys.rb +79 -0
  32. data/lib/rgeo/coord_sys/cs/entities.rb +1524 -0
  33. data/lib/rgeo/coord_sys/cs/factories.rb +208 -0
  34. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +308 -0
  35. data/lib/rgeo/coord_sys/proj4.rb +312 -0
  36. data/lib/rgeo/coord_sys/srs_database/active_record_table.rb +194 -0
  37. data/lib/rgeo/coord_sys/srs_database/interface.rb +165 -0
  38. data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +188 -0
  39. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +108 -0
  40. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +108 -0
  41. data/lib/rgeo/error.rb +63 -0
  42. data/lib/rgeo/feature.rb +88 -0
  43. data/lib/rgeo/feature/curve.rb +156 -0
  44. data/lib/rgeo/feature/factory.rb +332 -0
  45. data/lib/rgeo/feature/factory_generator.rb +138 -0
  46. data/lib/rgeo/feature/geometry.rb +614 -0
  47. data/lib/rgeo/feature/geometry_collection.rb +129 -0
  48. data/lib/rgeo/feature/line.rb +66 -0
  49. data/lib/rgeo/feature/line_string.rb +102 -0
  50. data/lib/rgeo/feature/linear_ring.rb +66 -0
  51. data/lib/rgeo/feature/multi_curve.rb +113 -0
  52. data/lib/rgeo/feature/multi_line_string.rb +66 -0
  53. data/lib/rgeo/feature/multi_point.rb +73 -0
  54. data/lib/rgeo/feature/multi_polygon.rb +97 -0
  55. data/lib/rgeo/feature/multi_surface.rb +116 -0
  56. data/lib/rgeo/feature/point.rb +120 -0
  57. data/lib/rgeo/feature/polygon.rb +141 -0
  58. data/lib/rgeo/feature/surface.rb +122 -0
  59. data/lib/rgeo/feature/types.rb +305 -0
  60. data/lib/rgeo/geographic.rb +75 -0
  61. data/lib/rgeo/geographic/factory.rb +287 -0
  62. data/lib/rgeo/geographic/interface.rb +410 -0
  63. data/lib/rgeo/geographic/proj4_projector.rb +98 -0
  64. data/lib/rgeo/geographic/projected_feature_classes.rb +213 -0
  65. data/lib/rgeo/geographic/projected_feature_methods.rb +228 -0
  66. data/lib/rgeo/geographic/projected_window.rb +467 -0
  67. data/lib/rgeo/geographic/simple_mercator_projector.rb +157 -0
  68. data/lib/rgeo/geographic/spherical_feature_classes.rb +212 -0
  69. data/lib/rgeo/geographic/spherical_feature_methods.rb +97 -0
  70. data/lib/rgeo/geographic/spherical_math.rb +206 -0
  71. data/lib/rgeo/geos.rb +72 -0
  72. data/lib/rgeo/geos/factory.rb +301 -0
  73. data/lib/rgeo/geos/impl_additions.rb +76 -0
  74. data/lib/rgeo/geos/interface.rb +139 -0
  75. data/lib/rgeo/geos/zm_factory.rb +275 -0
  76. data/lib/rgeo/geos/zm_impl.rb +432 -0
  77. data/lib/rgeo/impl_helper.rb +53 -0
  78. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +235 -0
  79. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +85 -0
  80. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +197 -0
  81. data/lib/rgeo/impl_helper/basic_point_methods.rb +138 -0
  82. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +121 -0
  83. data/lib/rgeo/impl_helper/math.rb +50 -0
  84. data/lib/rgeo/version.rb +52 -0
  85. data/lib/rgeo/wkrep.rb +72 -0
  86. data/lib/rgeo/wkrep/wkb_generator.rb +267 -0
  87. data/lib/rgeo/wkrep/wkb_parser.rb +315 -0
  88. data/lib/rgeo/wkrep/wkt_generator.rb +275 -0
  89. data/lib/rgeo/wkrep/wkt_parser.rb +496 -0
  90. data/test/common/geometry_collection_tests.rb +238 -0
  91. data/test/common/line_string_tests.rb +324 -0
  92. data/test/common/multi_line_string_tests.rb +209 -0
  93. data/test/common/multi_point_tests.rb +201 -0
  94. data/test/common/multi_polygon_tests.rb +208 -0
  95. data/test/common/point_tests.rb +331 -0
  96. data/test/common/polygon_tests.rb +232 -0
  97. data/test/coord_sys/tc_active_record_table.rb +102 -0
  98. data/test/coord_sys/tc_ogc_cs.rb +356 -0
  99. data/test/coord_sys/tc_proj4.rb +138 -0
  100. data/test/coord_sys/tc_proj4_srs_data.rb +76 -0
  101. data/test/coord_sys/tc_sr_org.rb +70 -0
  102. data/test/coord_sys/tc_url_reader.rb +82 -0
  103. data/test/geos/tc_factory.rb +91 -0
  104. data/test/geos/tc_geometry_collection.rb +62 -0
  105. data/test/geos/tc_line_string.rb +62 -0
  106. data/test/geos/tc_misc.rb +72 -0
  107. data/test/geos/tc_multi_line_string.rb +62 -0
  108. data/test/geos/tc_multi_point.rb +62 -0
  109. data/test/geos/tc_multi_polygon.rb +63 -0
  110. data/test/geos/tc_point.rb +86 -0
  111. data/test/geos/tc_polygon.rb +86 -0
  112. data/test/geos/tc_zmfactory.rb +85 -0
  113. data/test/projected_geographic/tc_geometry_collection.rb +62 -0
  114. data/test/projected_geographic/tc_line_string.rb +62 -0
  115. data/test/projected_geographic/tc_multi_line_string.rb +62 -0
  116. data/test/projected_geographic/tc_multi_point.rb +62 -0
  117. data/test/projected_geographic/tc_multi_polygon.rb +63 -0
  118. data/test/projected_geographic/tc_point.rb +93 -0
  119. data/test/projected_geographic/tc_polygon.rb +62 -0
  120. data/test/simple_cartesian/tc_calculations.rb +145 -0
  121. data/test/simple_cartesian/tc_geometry_collection.rb +69 -0
  122. data/test/simple_cartesian/tc_line_string.rb +70 -0
  123. data/test/simple_cartesian/tc_multi_line_string.rb +67 -0
  124. data/test/simple_cartesian/tc_multi_point.rb +67 -0
  125. data/test/simple_cartesian/tc_multi_polygon.rb +70 -0
  126. data/test/simple_cartesian/tc_point.rb +91 -0
  127. data/test/simple_cartesian/tc_polygon.rb +67 -0
  128. data/test/simple_mercator/tc_geometry_collection.rb +62 -0
  129. data/test/simple_mercator/tc_line_string.rb +62 -0
  130. data/test/simple_mercator/tc_multi_line_string.rb +62 -0
  131. data/test/simple_mercator/tc_multi_point.rb +62 -0
  132. data/test/simple_mercator/tc_multi_polygon.rb +63 -0
  133. data/test/simple_mercator/tc_point.rb +93 -0
  134. data/test/simple_mercator/tc_polygon.rb +62 -0
  135. data/test/simple_mercator/tc_window.rb +219 -0
  136. data/test/spherical_geographic/tc_calculations.rb +203 -0
  137. data/test/spherical_geographic/tc_geometry_collection.rb +70 -0
  138. data/test/spherical_geographic/tc_line_string.rb +70 -0
  139. data/test/spherical_geographic/tc_multi_line_string.rb +67 -0
  140. data/test/spherical_geographic/tc_multi_point.rb +67 -0
  141. data/test/spherical_geographic/tc_multi_polygon.rb +70 -0
  142. data/test/spherical_geographic/tc_point.rb +100 -0
  143. data/test/spherical_geographic/tc_polygon.rb +67 -0
  144. data/test/tc_cartesian_analysis.rb +107 -0
  145. data/test/tc_oneoff.rb +63 -0
  146. data/test/wkrep/tc_wkb_generator.rb +249 -0
  147. data/test/wkrep/tc_wkb_parser.rb +353 -0
  148. data/test/wkrep/tc_wkt_generator.rb +362 -0
  149. data/test/wkrep/tc_wkt_parser.rb +480 -0
  150. metadata +267 -0
@@ -0,0 +1,496 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known text parser 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
+ require 'strscan'
38
+
39
+
40
+ module RGeo
41
+
42
+ module WKRep
43
+
44
+
45
+ # This class provides the functionality of parsing a geometry from
46
+ # WKT (well-known text) format. You may also customize the parser
47
+ # to recognize PostGIS EWKT extensions to the input, or Simple
48
+ # Features Specification 1.2 extensions for Z and M coordinates.
49
+ #
50
+ # To use this class, create an instance with the desired settings and
51
+ # customizations, and call the parse method.
52
+ #
53
+ # === Configuration options
54
+ #
55
+ # You must provide each parser with an RGeo::Feature::FactoryGenerator.
56
+ # It should understand the configuration options <tt>:srid</tt>,
57
+ # <tt>:has_z_coordinate</tt>, and <tt>:has_m_coordinate</tt>.
58
+ # You may also pass a specific RGeo::Feature::Factory, or nil to
59
+ # specify the default Cartesian FactoryGenerator.
60
+ #
61
+ # The following additional options are recognized. These can be passed
62
+ # to the constructor, or set on the object afterwards.
63
+ #
64
+ # [<tt>:support_ewkt</tt>]
65
+ # Activate support for PostGIS EWKT type tags, which appends an "M"
66
+ # to tags to indicate the presence of M but not Z, and also
67
+ # recognizes the SRID prefix. Default is false.
68
+ # [<tt>:support_wkt12</tt>]
69
+ # Activate support for SFS 1.2 extensions to the type codes, which
70
+ # use a "M", "Z", or "ZM" token to signal the presence of Z and M
71
+ # values in the data. SFS 1.2 types such as triangle, tin, and
72
+ # polyhedralsurface are NOT yet supported. Default is false.
73
+ # [<tt>:strict_wkt11</tt>]
74
+ # If true, parsing will proceed in SFS 1.1 strict mode, which
75
+ # disallows any values other than X or Y. This has no effect if
76
+ # support_ewkt or support_wkt12 are active. Default is false.
77
+ # [<tt>:ignore_extra_tokens</tt>]
78
+ # If true, extra tokens at the end of the data are ignored. If
79
+ # false (the default), extra tokens will trigger a parse error.
80
+ # [<tt>:default_srid</tt>]
81
+ # A SRID to pass to the factory generator if no SRID is present in
82
+ # the input. Defaults to nil (i.e. don't specify a SRID).
83
+
84
+ class WKTParser
85
+
86
+
87
+ # Create and configure a WKT parser. See the WKTParser
88
+ # documentation for the options that can be passed.
89
+
90
+ def initialize(factory_generator_=nil, opts_={})
91
+ self.factory_generator = factory_generator_
92
+ @support_ewkt = opts_[:support_ewkt] ? true : false
93
+ @support_wkt12 = opts_[:support_wkt12] ? true : false
94
+ @strict_wkt11 = @support_ewkt || @support_wkt12 ? false : opts_[:strict_wkt11] ? true : false
95
+ @ignore_extra_tokens = opts_[:ignore_extra_tokens] ? true : false
96
+ @default_srid = opts_[:default_srid]
97
+ end
98
+
99
+
100
+ # Returns the factory generator. See WKTParser for details.
101
+ def factory_generator
102
+ @factory_generator
103
+ end
104
+
105
+ # If this parser was given an exact factory, returns it; otherwise
106
+ # returns nil.
107
+ def exact_factory
108
+ @exact_factory
109
+ end
110
+
111
+ # Sets the factory_generator. See WKTParser for details.
112
+ def factory_generator=(value_)
113
+ if value_.kind_of?(Feature::Factory::Instance)
114
+ @factory_generator = Feature::FactoryGenerator.single(value_)
115
+ @exact_factory = value_
116
+ elsif value_.respond_to?(:call)
117
+ @factory_generator = value_
118
+ @exact_factory = nil
119
+ else
120
+ @factory_generator = Cartesian.method(:preferred_factory)
121
+ @exact_factory = nil
122
+ end
123
+ end
124
+
125
+ # Sets the factory_generator to the given block.
126
+ # See WKTParser for details.
127
+ def to_generate_factory(&block_)
128
+ self.factory_generator = block_
129
+ end
130
+
131
+ # Returns true if this parser supports EWKT.
132
+ # See WKTParser for details.
133
+ def support_ewkt?
134
+ @support_ewkt
135
+ end
136
+
137
+ # Sets the the support_ewkt flag. See WKTParser for details.
138
+ def support_ewkt=(value_)
139
+ @support_ewkt = value_ ? true : false
140
+ end
141
+
142
+ # Returns true if this parser supports SFS 1.2 extensions.
143
+ # See WKTParser for details.
144
+ def support_wkt12?
145
+ @support_wkt12
146
+ end
147
+
148
+ # Sets the the support_wkt12 flag. See WKTParser for details.
149
+ def support_wkt12=(value_)
150
+ @support_wkt12 = value_ ? true : false
151
+ end
152
+
153
+ # Returns true if this parser strictly adheres to WKT 1.1.
154
+ # See WKTParser for details.
155
+ def strict_wkt11?
156
+ @strict_wkt11
157
+ end
158
+
159
+ # Sets the the strict_wkt11 flag. See WKTParser for details.
160
+ def strict_wkt11=(value_)
161
+ @strict_wkt11 = value_ ? true : false
162
+ end
163
+
164
+ # Returns true if this parser ignores extra tokens.
165
+ # See WKTParser for details.
166
+ def ignore_extra_tokens?
167
+ @ignore_extra_tokens
168
+ end
169
+
170
+ # Sets the the ignore_extra_tokens flag. See WKTParser for details.
171
+ def ignore_extra_tokens=(value_)
172
+ @ignore_extra_tokens = value_ ? true : false
173
+ end
174
+
175
+
176
+ # Parse the given string, and return a geometry object.
177
+
178
+ def parse(str_)
179
+ str_ = str_.downcase
180
+ @cur_factory = @exact_factory
181
+ if @cur_factory
182
+ @cur_factory_support_z = @cur_factory.property(:has_z_coordinate) ? true : false
183
+ @cur_factory_support_m = @cur_factory.property(:has_m_coordinate) ? true : false
184
+ end
185
+ @cur_expect_z = nil
186
+ @cur_expect_m = nil
187
+ @cur_srid = @default_srid
188
+ if @support_ewkt && str_ =~ /^srid=(\d+);/i
189
+ str_ = $'
190
+ @cur_srid = $1.to_i
191
+ end
192
+ begin
193
+ _start_scanner(str_)
194
+ obj_ = _parse_type_tag(false)
195
+ if @cur_token && !@ignore_extra_tokens
196
+ raise Error::ParseError, "Extra tokens beginning with #{@cur_token.inspect}."
197
+ end
198
+ ensure
199
+ _clean_scanner
200
+ end
201
+ obj_
202
+ end
203
+
204
+
205
+ def _check_factory_support # :nodoc:
206
+ if @cur_expect_z && !@cur_factory_support_z
207
+ raise Error::ParseError, "Geometry calls for Z coordinate but factory doesn't support it."
208
+ end
209
+ if @cur_expect_m && !@cur_factory_support_m
210
+ raise Error::ParseError, "Geometry calls for M coordinate but factory doesn't support it."
211
+ end
212
+ end
213
+
214
+
215
+ def _ensure_factory # :nodoc:
216
+ unless @cur_factory
217
+ @cur_factory = @factory_generator.call(:srid => @cur_srid, :has_z_coordinate => @cur_expect_z, :has_m_coordinate => @cur_expect_m)
218
+ @cur_factory_support_z = @cur_factory.property(:has_z_coordinate) ? true : false
219
+ @cur_factory_support_m = @cur_factory.property(:has_m_coordinate) ? true : false
220
+ _check_factory_support unless @cur_expect_z.nil?
221
+ end
222
+ @cur_factory
223
+ end
224
+
225
+
226
+ def _parse_type_tag(contained_) # :nodoc:
227
+ _expect_token_type(::String)
228
+ if @support_ewkt && @cur_token =~ /^(.+)(m)$/
229
+ type_ = $1
230
+ zm_ = $2
231
+ else
232
+ type_ = @cur_token
233
+ zm_ = ''
234
+ end
235
+ _next_token
236
+ if zm_.length == 0 && @support_wkt12 && @cur_token.kind_of?(::String) && @cur_token =~ /^z?m?$/
237
+ zm_ = @cur_token
238
+ _next_token
239
+ end
240
+ if zm_.length > 0 || @strict_wkt11
241
+ creating_expectation_ = @cur_expect_z.nil?
242
+ expect_z_ = zm_[0,1] == 'z' ? true : false
243
+ if @cur_expect_z.nil?
244
+ @cur_expect_z = expect_z_
245
+ elsif expect_z_ != @cur_expect_z
246
+ raise Error::ParseError, "Surrounding collection has Z but contained geometry doesn't."
247
+ end
248
+ expect_m_ = zm_[-1,1] == 'm' ? true : false
249
+ if @cur_expect_m.nil?
250
+ @cur_expect_m = expect_m_
251
+ else expect_m_ != @cur_expect_m
252
+ raise Error::ParseError, "Surrounding collection has M but contained geometry doesn't."
253
+ end
254
+ if creating_expectation_
255
+ if @cur_factory
256
+ _check_factory_support
257
+ else
258
+ _ensure_factory
259
+ end
260
+ end
261
+ end
262
+ case type_
263
+ when 'point'
264
+ _parse_point(true)
265
+ when 'linestring'
266
+ _parse_line_string
267
+ when 'polygon'
268
+ _parse_polygon
269
+ when 'geometrycollection'
270
+ _parse_geometry_collection
271
+ when 'multipoint'
272
+ _parse_multi_point
273
+ when 'multilinestring'
274
+ _parse_multi_line_string
275
+ when 'multipolygon'
276
+ _parse_multi_polygon
277
+ else
278
+ raise Error::ParseError, "Unknown type tag: #{type_.inspect}."
279
+ end
280
+ end
281
+
282
+
283
+ def _parse_coords # :nodoc:
284
+ _expect_token_type(::Numeric)
285
+ x_ = @cur_token
286
+ _next_token
287
+ _expect_token_type(::Numeric)
288
+ y_ = @cur_token
289
+ _next_token
290
+ extra_ = []
291
+ if @cur_expect_z.nil?
292
+ while ::Numeric === @cur_token
293
+ extra_ << @cur_token
294
+ _next_token
295
+ end
296
+ num_extras_ = extra_.size
297
+ @cur_expect_z = num_extras_ > 0 && (!@cur_factory || @cur_factory_support_z) ? true : false
298
+ num_extras_ -= 1 if @cur_expect_z
299
+ @cur_expect_m = num_extras_ > 0 && (!@cur_factory || @cur_factory_support_m) ? true : false
300
+ num_extras_ -= 1 if @cur_expect_m
301
+ if num_extras_ > 0
302
+ raise Error::ParseError, "Found #{extra_.size+2} coordinates, which is too many for this factory."
303
+ end
304
+ _ensure_factory
305
+ else
306
+ val_ = 0
307
+ if @cur_expect_z
308
+ _expect_token_type(::Numeric)
309
+ val_ = @cur_token
310
+ _next_token
311
+ end
312
+ if @cur_factory_support_z
313
+ extra_ << val_
314
+ end
315
+ val_ = 0
316
+ if @cur_expect_m
317
+ _expect_token_type(::Numeric)
318
+ val_ = @cur_token
319
+ _next_token
320
+ end
321
+ if @cur_factory_support_m
322
+ extra_ << val_
323
+ end
324
+ end
325
+ @cur_factory.point(x_, y_, *extra_)
326
+ end
327
+
328
+
329
+ def _parse_point(convert_empty_=false) # :nodoc:
330
+ if convert_empty_ && @cur_token == 'empty'
331
+ point_ = _ensure_factory.multi_point([])
332
+ else
333
+ _expect_token_type(:begin)
334
+ _next_token
335
+ point_ = _parse_coords
336
+ _expect_token_type(:end)
337
+ end
338
+ _next_token
339
+ point_
340
+ end
341
+
342
+
343
+ def _parse_line_string # :nodoc:
344
+ points_ = []
345
+ if @cur_token != 'empty'
346
+ _expect_token_type(:begin)
347
+ _next_token
348
+ loop do
349
+ points_ << _parse_coords
350
+ break if @cur_token == :end
351
+ _expect_token_type(:comma)
352
+ _next_token
353
+ end
354
+ end
355
+ _next_token
356
+ _ensure_factory.line_string(points_)
357
+ end
358
+
359
+
360
+ def _parse_polygon # :nodoc:
361
+ inner_rings_ = []
362
+ if @cur_token == 'empty'
363
+ outer_ring_ = _ensure_factory.linear_ring([])
364
+ else
365
+ _expect_token_type(:begin)
366
+ _next_token
367
+ outer_ring_ = _parse_line_string
368
+ loop do
369
+ break if @cur_token == :end
370
+ _expect_token_type(:comma)
371
+ _next_token
372
+ inner_rings_ << _parse_line_string
373
+ end
374
+ end
375
+ _next_token
376
+ _ensure_factory.polygon(outer_ring_, inner_rings_)
377
+ end
378
+
379
+
380
+ def _parse_geometry_collection # :nodoc:
381
+ geometries_ = []
382
+ if @cur_token != 'empty'
383
+ _expect_token_type(:begin)
384
+ _next_token
385
+ loop do
386
+ geometries_ << _parse_type_tag(true)
387
+ break if @cur_token == :end
388
+ _expect_token_type(:comma)
389
+ _next_token
390
+ end
391
+ end
392
+ _next_token
393
+ _ensure_factory.collection(geometries_)
394
+ end
395
+
396
+
397
+ def _parse_multi_point # :nodoc:
398
+ points_ = []
399
+ if @cur_token != 'empty'
400
+ _expect_token_type(:begin)
401
+ _next_token
402
+ loop do
403
+ points_ << _parse_point
404
+ break if @cur_token == :end
405
+ _expect_token_type(:comma)
406
+ _next_token
407
+ end
408
+ end
409
+ _next_token
410
+ _ensure_factory.multi_point(points_)
411
+ end
412
+
413
+
414
+ def _parse_multi_line_string # :nodoc:
415
+ line_strings_ = []
416
+ if @cur_token != 'empty'
417
+ _expect_token_type(:begin)
418
+ _next_token
419
+ loop do
420
+ line_strings_ << _parse_line_string
421
+ break if @cur_token == :end
422
+ _expect_token_type(:comma)
423
+ _next_token
424
+ end
425
+ end
426
+ _next_token
427
+ _ensure_factory.multi_line_string(line_strings_)
428
+ end
429
+
430
+
431
+ def _parse_multi_polygon # :nodoc:
432
+ polygons_ = []
433
+ if @cur_token != 'empty'
434
+ _expect_token_type(:begin)
435
+ _next_token
436
+ loop do
437
+ polygons_ << _parse_polygon
438
+ break if @cur_token == :end
439
+ _expect_token_type(:comma)
440
+ _next_token
441
+ end
442
+ end
443
+ _next_token
444
+ _ensure_factory.multi_polygon(polygons_)
445
+ end
446
+
447
+
448
+ def _start_scanner(str_) # :nodoc:
449
+ @_scanner = ::StringScanner.new(str_)
450
+ _next_token
451
+ end
452
+
453
+
454
+ def _clean_scanner # :nodoc:
455
+ @_scanner = nil
456
+ @cur_token = nil
457
+ end
458
+
459
+
460
+ def _expect_token_type(type_) # :nodoc:
461
+ unless type_ === @cur_token
462
+ raise Error::ParseError, "#{type_.inspect} expected but #{@cur_token.inspect} found."
463
+ end
464
+ end
465
+
466
+
467
+ def _next_token # :nodoc:
468
+ if @_scanner.scan_until(/\(|\)|\[|\]|,|[^\s\(\)\[\],]+/)
469
+ token_ = @_scanner.matched
470
+ case token_
471
+ when /^[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?$/
472
+ @cur_token = token_.to_f
473
+ when /^[a-z]+$/
474
+ @cur_token = token_
475
+ when ','
476
+ @cur_token = :comma
477
+ when '(','['
478
+ @cur_token = :begin
479
+ when ']',')'
480
+ @cur_token = :end
481
+ else
482
+ raise Error::ParseError, "Bad token: #{token_.inspect}"
483
+ end
484
+ else
485
+ @cur_token = nil
486
+ end
487
+ @cur_token
488
+ end
489
+
490
+
491
+ end
492
+
493
+
494
+ end
495
+
496
+ end