rgeo 0.1.14 → 0.1.15

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 (37) hide show
  1. data/History.rdoc +6 -0
  2. data/Version +1 -1
  3. data/lib/rgeo.rb +80 -30
  4. data/lib/rgeo/all.rb +48 -0
  5. data/lib/rgeo/cartesian.rb +37 -12
  6. data/lib/rgeo/cartesian/calculations.rb +5 -0
  7. data/lib/rgeo/cartesian/{simple_factory.rb → factory.rb} +14 -17
  8. data/lib/rgeo/cartesian/{simple_feature_classes.rb → feature_classes.rb} +55 -55
  9. data/lib/rgeo/cartesian/{simple_feature_methods.rb → feature_methods.rb} +7 -3
  10. data/lib/rgeo/cartesian/interface.rb +7 -3
  11. data/lib/rgeo/errors.rb +4 -0
  12. data/lib/rgeo/features.rb +25 -20
  13. data/lib/rgeo/geo_json.rb +10 -8
  14. data/lib/rgeo/geography.rb +10 -16
  15. data/lib/rgeo/geography/all.rb +40 -0
  16. data/lib/rgeo/geography/factory.rb +2 -2
  17. data/lib/rgeo/geography/{factories.rb → interface.rb} +4 -2
  18. data/lib/rgeo/geography/simple_mercator.rb +69 -0
  19. data/lib/rgeo/geography/simple_mercator/feature_classes.rb +62 -62
  20. data/lib/rgeo/geography/simple_mercator/projector.rb +1 -1
  21. data/lib/rgeo/geography/simple_spherical.rb +68 -0
  22. data/lib/rgeo/geography/simple_spherical/calculations.rb +2 -2
  23. data/lib/rgeo/geography/simple_spherical/feature_classes.rb +44 -44
  24. data/lib/rgeo/geos.rb +12 -9
  25. data/lib/rgeo/impl_helpers.rb +14 -9
  26. data/lib/rgeo/impl_helpers/basic_geometry_collection_methods.rb +10 -0
  27. data/lib/rgeo/impl_helpers/basic_point_methods.rb +3 -0
  28. data/lib/rgeo/{geography/helper.rb → impl_helpers/math.rb} +3 -3
  29. data/lib/rgeo/wkrep.rb +32 -12
  30. data/lib/rgeo/wkrep/wkb_generator.rb +95 -9
  31. data/lib/rgeo/wkrep/wkb_parser.rb +117 -9
  32. data/lib/rgeo/wkrep/wkt_generator.rb +106 -18
  33. data/lib/rgeo/wkrep/wkt_parser.rb +203 -59
  34. data/tests/simple_cartesian/tc_calculations.rb +8 -8
  35. data/tests/wkrep/tc_wkt_generator.rb +362 -0
  36. data/tests/wkrep/tc_wkt_parser.rb +490 -0
  37. metadata +16 -8
@@ -39,24 +39,132 @@ module RGeo
39
39
  module WKRep
40
40
 
41
41
 
42
+ # This class provides the functionality of parsing a geometry from
43
+ # WKB (well-known binary) format. You may also customize the parser
44
+ # to recognize PostGIS EWKB extensions to the input, or Simple
45
+ # Features Specification 1.2 extensions for Z and M coordinates.
46
+ #
47
+ # To use this class, create an instance with the desired settings and
48
+ # customizations, and call the parse method.
49
+ #
50
+ # === Configuration options
51
+ #
52
+ # The following options are recognized. These can be passed to the
53
+ # constructor, or set on the object afterwards.
54
+ #
55
+ # <tt>:default_factory</tt>::
56
+ # The default factory for generated geometries, used when no SRID
57
+ # is explicitly specified in the input. If none is provided, the
58
+ # default cartesian factory will be used.
59
+ # <tt>:factory_from_srid</tt>::
60
+ # A Proc that takes an SRID as the sole argument, and returns a
61
+ # factory for generated geometries when that SRID is specified in
62
+ # the input. If no such Proc is provided, the default_factory is
63
+ # used, regardless of the input SRID.
64
+ # <tt>:support_ewkb</tt>::
65
+ # Activate support for PostGIS EWKB type codes, which use high
66
+ # order bits in the type code to signal the presence of Z, M, and
67
+ # SRID values in the data. Default is false.
68
+ # <tt>:support_wkb12</tt>::
69
+ # Activate support for SFS 1.2 extensions to the type codes, which
70
+ # use values greater than 1000 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>:ignore_extra_bytes</tt>::
74
+ # If true, extra bytes at the end of the data are ignored. If
75
+ # false (the default), extra bytes will trigger a parse error.
76
+
42
77
  class WKBParser
43
78
 
44
79
 
45
- def initialize(factory_, opts_={}, &block_)
46
- @factory = factory_
47
- @factory_factory = block_
48
- @support_ewkb = opts_[:support_ewkb]
49
- @support_wkb12 = opts_[:support_wkb12]
50
- @ignore_extra_bytes = opts_[:ignore_extra_bytes]
80
+ # Create and configure a WKB parser. See the WKBParser
81
+ # documentation for the options that can be passed.
82
+
83
+ def initialize(opts_={})
84
+ @default_factory = opts_[:default_factory] || Cartesian.preferred_factory
85
+ @factory_from_srid = opts_[:factory_from_srid]
86
+ @support_ewkb = opts_[:support_ewkb] ? true : false
87
+ @support_wkb12 = opts_[:support_wkb12] ? true : false
88
+ @ignore_extra_bytes = opts_[:ignore_extra_bytes] ? true : false
89
+ end
90
+
91
+
92
+ # Returns the default factory. See WKBParser for details.
93
+ def default_factory
94
+ @default_factory
95
+ end
96
+
97
+ # Sets the default factory. See WKBParser for details.
98
+ def default_factory=(value_)
99
+ @default_factory = value_ || Cartesian.preferred_factory
100
+ end
101
+
102
+ # Returns true if this parser has a factory_from_srid procedure.
103
+ # See WKBParser for details.
104
+ def has_factory_from_srid?
105
+ @factory_from_srid ? true : false
106
+ end
107
+
108
+ # Sets the factory_from_srid. See WKBParser for details.
109
+ def factory_from_srid=(value_)
110
+ @factory_from_srid = value_
111
+ end
112
+
113
+ # Sets the factory_from_srid to the given block.
114
+ # See WKBParser for details.
115
+ def set_factory_from_srid(&block_)
116
+ @factory_from_srid = block_
117
+ end
118
+
119
+ # Returns true if this parser supports EWKB.
120
+ # See WKBParser for details.
121
+ def support_ewkb?
122
+ @support_ewkb
123
+ end
124
+
125
+ # Sets the the support_ewkb flag. See WKBParser for details.
126
+ def support_ewkb=(value_)
127
+ @support_ewkb = value_ ? true : false
128
+ end
129
+
130
+ # Returns true if this parser supports SFS 1.2 extensions.
131
+ # See WKBParser for details.
132
+ def support_wkb12?
133
+ @support_wkb12
134
+ end
135
+
136
+ # Sets the the support_wkb12 flag. See WKBParser for details.
137
+ def support_wkb12=(value_)
138
+ @support_wkb12 = value_ ? true : false
139
+ end
140
+
141
+ # Returns true if this parser ignores extra bytes.
142
+ # See WKBParser for details.
143
+ def ignore_extra_bytes?
144
+ @ignore_extra_bytes
51
145
  end
52
146
 
147
+ # Sets the the ignore_extra_bytes flag. See WKBParser for details.
148
+ def ignore_extra_bytes=(value_)
149
+ @ignore_extra_bytes = value_ ? true : false
150
+ end
151
+
152
+
153
+ # Parse the given hex string, and return a geometry object.
154
+
155
+ def parse_hex(str_)
156
+ parse([str_].pack('H*'))
157
+ end
158
+
159
+
160
+ # Parse the given binary data, and return a geometry object.
53
161
 
54
162
  def parse(data_)
55
163
  @cur_has_z = nil
56
164
  @cur_has_m = nil
57
165
  @cur_srid = nil
58
166
  @cur_dims = 2
59
- @cur_factory = @factory
167
+ @cur_factory = @default_factory
60
168
  begin
61
169
  _start_scanner(data_)
62
170
  obj_ = _parse_object(false)
@@ -108,8 +216,8 @@ module RGeo
108
216
  @cur_has_m = has_m_
109
217
  @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
110
218
  @cur_srid = srid_
111
- if srid_ && @factory_factory
112
- @cur_factory = @factory_factory.call(srid_)
219
+ if srid_ && @factory_from_srid
220
+ @cur_factory = @factory_from_srid.call(srid_)
113
221
  end
114
222
  if @cur_has_z && !@cur_factory.has_capability?(:z_coordinate)
115
223
  raise Errors::ParseError, "Data has Z coordinates but the factory doesn't have z_coordinate capability"
@@ -39,19 +39,107 @@ module RGeo
39
39
  module WKRep
40
40
 
41
41
 
42
+ # This class provides the functionality of serializing a geometry as
43
+ # WKT (well-known text) format. You may also customize the serializer
44
+ # to generate PostGIS EWKT extensions to the output, or to follow the
45
+ # Simple Features Specification 1.2 extensions for Z and M.
46
+ #
47
+ # To use this class, create an instance with the desired settings and
48
+ # customizations, and call the generate method.
49
+ #
50
+ # === Configuration options
51
+ #
52
+ # The following options are recognized. These can be passed to the
53
+ # constructor, or set on the object afterwards.
54
+ #
55
+ # <tt>:tag_format</tt>::
56
+ # The format for tags. Possible values are <tt>:wkt11</tt>,
57
+ # indicating SFS 1.1 WKT (i.e. no Z or M markers in the tags) but
58
+ # with Z and/or M values added in if they are present;
59
+ # <tt>:wkt11_strict</tt>, indicating SFS 1.1 WKT with Z and M
60
+ # dropped from the output (since WKT strictly does not support
61
+ # the Z or M dimensions); <tt>:ewkt</tt>, indicating the PostGIS
62
+ # EWKT extensions (i.e. "M" appended to tag names if M but not
63
+ # Z is present); or <tt>:wkt12</tt>, indicating SFS 1.2 WKT
64
+ # tags that indicate the presence of Z and M in a separate token.
65
+ # Default is <tt>:wkt11</tt>.
66
+ # <tt>:emit_ewkt_srid</tt>::
67
+ # If true, embed the SRID of the toplevel geometry. Available only
68
+ # if <tt>:type_format</tt> is <tt>:ewkt</tt>. Default is false.
69
+ # <tt>:square_brackets</tt>::
70
+ # If true, uses square brackets rather than parentheses.
71
+ # Default is false.
72
+ # <tt>:convert_case</tt>::
73
+ # Possible values are <tt>:upper</tt>, which changes all letters
74
+ # in the output to ALL CAPS; <tt>:lower</tt>, which changes all
75
+ # letters to lower case; or nil, indicating no case changes from
76
+ # the default (which is not specified exactly, but is chosen by the
77
+ # generator to emphasize readability.) Default is nil.
78
+
42
79
  class WKTGenerator
43
80
 
44
81
 
82
+ # Create and configure a WKT generator. See the WKTGenerator
83
+ # documentation for the options that can be passed.
84
+
45
85
  def initialize(opts_={})
46
- @tag_format = opts_[:tag_format]
47
- @emit_ewkt_srid = opts_[:emit_ewkt_srid] if @tag_format == :ewkt
48
- @begin_bracket = opts_[:square_brackets] ? '[' : '('
49
- @end_bracket = opts_[:square_brackets] ? ']' : ')'
50
- @case = opts_[:case]
86
+ @tag_format = opts_[:tag_format] || :wkt11
87
+ @emit_ewkt_srid = opts_[:emit_ewkt_srid] ? true : false if @tag_format == :ewkt
88
+ @square_brackets = opts_[:square_brackets] ? true : false
89
+ @convert_case = opts_[:convert_case]
51
90
  end
52
91
 
53
92
 
93
+ # Returns the format for type tags. See WKTGenerator for details.
94
+ def tag_format
95
+ @tag_format
96
+ end
97
+
98
+ # Sets the format for type tags. See WKTGenerator for details.
99
+ def tag_format=(value_)
100
+ @tag_format = value_
101
+ end
102
+
103
+ # Returns whether SRID is embedded. See WKTGenerator for details.
104
+ def emit_ewkt_srid?
105
+ @emit_ewkt_srid
106
+ end
107
+
108
+ # Sets whether SRID is embedded. Available only when the tag_format
109
+ # is <tt>:ewkt</tt>. See WKTGenerator for details.
110
+ def emit_ewkt_srid=(value_)
111
+ @emit_ewkt_srid = @type_format == :ewkt && value_
112
+ end
113
+
114
+ # Returns whether square brackets rather than parens are output.
115
+ # See WKTGenerator for details.
116
+ def square_brackets?
117
+ @square_brackets
118
+ end
119
+
120
+ # Sets whether square brackets rather than parens are output.
121
+ # See WKTGenerator for details.
122
+ def square_brackets=(value_)
123
+ @square_brackets = value_ ? true : false
124
+ end
125
+
126
+ # Returns the case for output. See WKTGenerator for details.
127
+ def convert_case
128
+ @convert_case
129
+ end
130
+
131
+ # Sets the case for output. See WKTGenerator for details.
132
+ def convert_case=(value_)
133
+ @convert_case = value_
134
+ end
135
+
136
+
137
+ # Generate and return the WKT format for the given geometry object,
138
+ # according to the current settings.
139
+
54
140
  def generate(obj_)
141
+ @begin_bracket = @square_brackets ? '[' : '('
142
+ @end_bracket = @square_brackets ? ']' : ')'
55
143
  factory_ = obj_.factory
56
144
  if @tag_format == :wkt11_strict
57
145
  @cur_support_z = nil
@@ -61,9 +149,9 @@ module RGeo
61
149
  @cur_support_m = factory_.has_capability?(:m_coordinate)
62
150
  end
63
151
  str_ = _generate_feature(obj_, true)
64
- if @case == :upper
152
+ if @convert_case == :upper
65
153
  str_.upcase
66
- elsif @case == :lower
154
+ elsif @convert_case == :lower
67
155
  str_.downcase
68
156
  else
69
157
  str_
@@ -78,6 +166,9 @@ module RGeo
78
166
  if @cur_support_m && !@cur_support_z
79
167
  tag_ << 'M'
80
168
  end
169
+ if toplevel_ && @emit_ewkt_srid
170
+ tag_ = "SRID=#{obj_.srid};#{tag_}"
171
+ end
81
172
  elsif @tag_format == :wkt12
82
173
  if @cur_support_z
83
174
  if @cur_support_m
@@ -89,9 +180,6 @@ module RGeo
89
180
  tag_ << ' M'
90
181
  end
91
182
  end
92
- if toplevel_ && @emit_ewkt_srid
93
- tag_ = "SRID=#{obj_.srid};#{tag_}"
94
- end
95
183
  if type_ == Features::Point
96
184
  tag_ + _generate_point(obj_)
97
185
  elsif type_.subtype_of?(Features::LineString)
@@ -125,27 +213,27 @@ module RGeo
125
213
  end
126
214
 
127
215
 
128
- def _generate_line_string(obj_) # :nodoc:
216
+ def _generate_line_string(obj_, contained_=false) # :nodoc:
129
217
  if obj_.is_empty?
130
- " EMPTY"
218
+ contained_ ? 'EMPTY' : ' EMPTY'
131
219
  else
132
220
  "#{@begin_bracket}#{obj_.points.map{ |p_| _generate_coords(p_) }.join(',')}#{@end_bracket}"
133
221
  end
134
222
  end
135
223
 
136
224
 
137
- def _generate_polygon(obj_) # :nodoc:
225
+ def _generate_polygon(obj_, contained_=false) # :nodoc:
138
226
  if obj_.is_empty?
139
- " EMPTY"
227
+ contained_ ? 'EMPTY' : ' EMPTY'
140
228
  else
141
- "#{@begin_bracket}#{([_generate_line_string(obj_.exterior_ring)] + obj_.interior_rings.map{ |r_| _generate_line_string(r_) }).join(',')}#{@end_bracket}"
229
+ "#{@begin_bracket}#{([_generate_line_string(obj_.exterior_ring, true)] + obj_.interior_rings.map{ |r_| _generate_line_string(r_, true) }).join(',')}#{@end_bracket}"
142
230
  end
143
231
  end
144
232
 
145
233
 
146
234
  def _generate_geometry_collection(obj_) # :nodoc:
147
235
  if obj_.is_empty?
148
- " EMPTY"
236
+ ' EMPTY'
149
237
  else
150
238
  "#{@begin_bracket}#{obj_.map{ |f_| _generate_feature(f_) }.join(',')}#{@end_bracket}"
151
239
  end
@@ -165,7 +253,7 @@ module RGeo
165
253
  if obj_.is_empty?
166
254
  " EMPTY"
167
255
  else
168
- "#{@begin_bracket}#{obj_.map{ |f_| _generate_line_string(f_) }.join(',')}#{@end_bracket}"
256
+ "#{@begin_bracket}#{obj_.map{ |f_| _generate_line_string(f_, true) }.join(',')}#{@end_bracket}"
169
257
  end
170
258
  end
171
259
 
@@ -174,7 +262,7 @@ module RGeo
174
262
  if obj_.is_empty?
175
263
  " EMPTY"
176
264
  else
177
- "#{@begin_bracket}#{obj_.map{ |f_| _generate_polygon(f_) }.join(',')}#{@end_bracket}"
265
+ "#{@begin_bracket}#{obj_.map{ |f_| _generate_polygon(f_, true) }.join(',')}#{@end_bracket}"
178
266
  end
179
267
  end
180
268
 
@@ -42,42 +42,164 @@ module RGeo
42
42
  module WKRep
43
43
 
44
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
+ # The following options are recognized. These can be passed to the
56
+ # constructor, or set on the object afterwards.
57
+ #
58
+ # <tt>:default_factory</tt>::
59
+ # The default factory for generated geometries, used when no SRID
60
+ # is explicitly specified in the input. If none is provided, the
61
+ # default cartesian factory will be used.
62
+ # <tt>:factory_from_srid</tt>::
63
+ # A Proc that takes an SRID as the sole argument, and returns a
64
+ # factory for generated geometries when that SRID is specified in
65
+ # the input. If no such Proc is provided, the default_factory is
66
+ # used, regardless of the input SRID.
67
+ # <tt>:support_ewkt</tt>::
68
+ # Activate support for PostGIS EWKT type tags, which appends an "M"
69
+ # to tags to indicate the presence of M but not Z, and also
70
+ # recognizes the SRID prefix. Default is false.
71
+ # <tt>:support_wkt12</tt>::
72
+ # Activate support for SFS 1.2 extensions to the type codes, which
73
+ # use a "M", "Z", or "ZM" token to signal the presence of Z and M
74
+ # values in the data. SFS 1.2 types such as triangle, tin, and
75
+ # polyhedralsurface are NOT yet supported. Default is false.
76
+ # <tt>:strict_wkt11</tt>::
77
+ # If true, parsing will proceed in SFS 1.1 strict mode, which
78
+ # disallows any values other than X or Y. This has no effect if
79
+ # support_ewkt or support_wkt12 are active. Default is false.
80
+ # <tt>:ignore_extra_tokens</tt>::
81
+ # If true, extra tokens at the end of the data are ignored. If
82
+ # false (the default), extra tokens will trigger a parse error.
83
+
45
84
  class WKTParser
46
85
 
47
86
 
48
- def initialize(factory_, opts_={}, &block_)
49
- @factory = factory_
50
- @factory_factory = block_
51
- @support_ewkt = opts_[:support_ewkt]
52
- @support_wkt12 = opts_[:support_wkt12]
53
- @support_higher_dimensions = opts_[:support_higher_dimensions] || @support_ewkt || @support_wkt12
54
- @ignore_extra_tokens = opts_[:ignore_extra_tokens]
87
+ # Create and configure a WKT parser. See the WKTParser
88
+ # documentation for the options that can be passed.
89
+
90
+ def initialize(opts_={})
91
+ @default_factory = opts_[:default_factory] || Cartesian.preferred_factory
92
+ @factory_from_srid = opts_[:factory_from_srid]
93
+ @support_ewkt = opts_[:support_ewkt] ? true : false
94
+ @support_wkt12 = opts_[:support_wkt12] ? true : false
95
+ @strict_wkt11 = @support_ewkt || @support_wkt12 ? false : opts_[:strict_wkt11] ? true : false
96
+ @ignore_extra_tokens = opts_[:ignore_extra_tokens] ? true : false
97
+ end
98
+
99
+
100
+ # Returns the default factory. See WKTParser for details.
101
+ def default_factory
102
+ @default_factory
103
+ end
104
+
105
+ # Sets the default factory. See WKTParser for details.
106
+ def default_factory=(value_)
107
+ @default_factory = value_ || Cartesian.preferred_factory
108
+ end
109
+
110
+ # Returns true if this parser has a factory_from_srid procedure.
111
+ # See WKTParser for details.
112
+ def has_factory_from_srid?
113
+ @factory_from_srid ? true : false
114
+ end
115
+
116
+ # Sets the factory_from_srid. See WKTParser for details.
117
+ def factory_from_srid=(value_)
118
+ @factory_from_srid = value_
119
+ end
120
+
121
+ # Sets the factory_from_srid to the given block.
122
+ # See WKTParser for details.
123
+ def set_factory_from_srid(&block_)
124
+ @factory_from_srid = block_
125
+ end
126
+
127
+ # Returns true if this parser supports EWKT.
128
+ # See WKTParser for details.
129
+ def support_ewkt?
130
+ @support_ewkt
55
131
  end
56
132
 
133
+ # Sets the the support_ewkt flag. See WKTParser for details.
134
+ def support_ewkt=(value_)
135
+ @support_ewkt = value_ ? true : false
136
+ end
137
+
138
+ # Returns true if this parser supports SFS 1.2 extensions.
139
+ # See WKTParser for details.
140
+ def support_wkt12?
141
+ @support_wkt12
142
+ end
143
+
144
+ # Sets the the support_wkt12 flag. See WKTParser for details.
145
+ def support_wkt12=(value_)
146
+ @support_wkt12 = value_ ? true : false
147
+ end
148
+
149
+ # Returns true if this parser strictly adheres to WKT 1.1.
150
+ # See WKTParser for details.
151
+ def strict_wkt11?
152
+ @strict_wkt11
153
+ end
154
+
155
+ # Sets the the strict_wkt11 flag. See WKTParser for details.
156
+ def strict_wkt11=(value_)
157
+ @strict_wkt11 = value_ ? true : false
158
+ end
159
+
160
+ # Returns true if this parser ignores extra tokens.
161
+ # See WKTParser for details.
162
+ def ignore_extra_tokens?
163
+ @ignore_extra_tokens
164
+ end
165
+
166
+ # Sets the the ignore_extra_tokens flag. See WKTParser for details.
167
+ def ignore_extra_tokens=(value_)
168
+ @ignore_extra_tokens = value_ ? true : false
169
+ end
170
+
171
+
172
+ # Parse the given string, and return a geometry object.
57
173
 
58
174
  def parse(str_)
59
- @cur_factory = @factory
175
+ @cur_factory = @default_factory
60
176
  str_ = str_.downcase
61
- if @support_ewkt && str_ =~ /^srid=(\d+);/
177
+ if @support_ewkt && str_ =~ /^srid=(\d+);/i
62
178
  str_ = $'
63
- if @factory_factory
64
- @cur_factory = @factory_factory.call($1.to_i)
179
+ if @factory_from_srid
180
+ @cur_factory = @factory_from_srid.call($1.to_i)
65
181
  end
66
182
  end
67
183
  @cur_factory_support_z = @cur_factory.has_capability?(:z_coordinate)
68
184
  @cur_factory_support_m = @cur_factory.has_capability?(:m_coordinate)
69
- _start_scanner(str_)
70
- obj_ = _parse_type_tag(nil, nil)
71
- if @cur_token && !@ignore_extra_tokens
72
- raise Errors::ParseError, "Extra tokens beginning with #{@cur_token.inspect}."
185
+ @cur_expect_z = nil
186
+ @cur_expect_m = nil
187
+ begin
188
+ _start_scanner(str_)
189
+ obj_ = _parse_type_tag
190
+ if @cur_token && !@ignore_extra_tokens
191
+ raise Errors::ParseError, "Extra tokens beginning with #{@cur_token.inspect}."
192
+ end
193
+ ensure
194
+ _clean_scanner
73
195
  end
74
196
  obj_
75
197
  end
76
198
 
77
199
 
78
- def _parse_type_tag(expect_z_, expect_m_) # :nodoc:
200
+ def _parse_type_tag # :nodoc:
79
201
  _expect_token_type(::String)
80
- if @support_ekwt && @cur_token =~ /^(.+)(z?m?)$/
202
+ if @support_ewkt && @cur_token =~ /^(.+)(m)$/
81
203
  type_ = $1
82
204
  zm_ = $2
83
205
  else
@@ -89,46 +211,46 @@ module RGeo
89
211
  zm_ = @cur_token
90
212
  _next_token
91
213
  end
92
- if zm_.length > 0 || !@support_higher_dimensions
93
- nexpect_z_ = zm_[0,1] == 'z'
94
- if !expect_z_.nil? && nexpect_z_ != expect_z_
214
+ if zm_.length > 0 || @strict_wkt11
215
+ expect_z_ = zm_[0,1] == 'z'
216
+ if !@cur_expect_z.nil? && expect_z_ != @cur_expect_z
95
217
  raise Errors::ParseError, "Surrounding collection has Z but contained geometry doesn't."
96
218
  end
97
- expect_z_ = nexpect_z_
98
- nexpect_m_ = zm_[-1,1] == 'm'
99
- if !expect_m_.nil? && nexpect_m_ != expect_m_
219
+ @cur_expect_z = expect_z_
220
+ expect_m_ = zm_[-1,1] == 'm'
221
+ if !@cur_expect_m.nil? && expect_m_ != @cur_expect_m
100
222
  raise Errors::ParseError, "Surrounding collection has M but contained geometry doesn't."
101
223
  end
102
- expect_m_ = nexpect_m_
224
+ @cur_expect_m = expect_m_
103
225
  end
104
- if expect_z_ && !@cur_factory_support_z
226
+ if @cur_expect_z && !@cur_factory_support_z
105
227
  raise Errors::ParseError, "Type tag declares #{zm_.inspect} but factory doesn't support Z."
106
228
  end
107
- if expect_m_ && !@cur_factory_support_m
229
+ if @cur_expect_m && !@cur_factory_support_m
108
230
  raise Errors::ParseError, "Type tag declares #{zm_.inspect} but factory doesn't support M."
109
231
  end
110
232
  case type_
111
233
  when 'point'
112
- _parse_point(expect_z_, expect_m_, true)
234
+ _parse_point(true)
113
235
  when 'linestring'
114
- _parse_line_string(expect_z_, expect_m_)
236
+ _parse_line_string
115
237
  when 'polygon'
116
- _parse_polygon(expect_z_, expect_m_)
238
+ _parse_polygon
117
239
  when 'geometrycollection'
118
- _parse_geometry_collection(expect_z_, expect_m_)
240
+ _parse_geometry_collection
119
241
  when 'multipoint'
120
- _parse_multi_point(expect_z_, expect_m_)
242
+ _parse_multi_point
121
243
  when 'multilinestring'
122
- _parse_multi_line_string(expect_z_, expect_m_)
244
+ _parse_multi_line_string
123
245
  when 'multipolygon'
124
- _parse_multi_polygon(expect_z_, expect_m_)
246
+ _parse_multi_polygon
125
247
  else
126
- raise Errors::ParseError, "Unknown type tag: #{@cur_token.inspect}."
248
+ raise Errors::ParseError, "Unknown type tag: #{type_.inspect}."
127
249
  end
128
250
  end
129
251
 
130
252
 
131
- def _parse_coords(expect_z_, expect_m_) # :nodoc:
253
+ def _parse_coords # :nodoc:
132
254
  _expect_token_type(::Numeric)
133
255
  x_ = @cur_token
134
256
  _next_token
@@ -136,34 +258,50 @@ module RGeo
136
258
  y_ = @cur_token
137
259
  _next_token
138
260
  extra_ = []
139
- if expect_z_.nil?
261
+ if @cur_expect_z.nil?
140
262
  while ::Numeric === @cur_token
141
263
  extra_ << @cur_token
142
264
  _next_token
143
265
  end
266
+ num_extras_ = extra_.size
267
+ @cur_expect_z = num_extras_ > 0 && @cur_factory_support_z ? true : false
268
+ num_extras_ -= 1 if @cur_expect_z
269
+ @cur_expect_m = num_extras_ > 0 && @cur_factory_support_m ? true : false
270
+ num_extras_ -= 1 if @cur_expect_m
271
+ if num_extras_ > 0
272
+ raise Errors::ParseError, "Found #{extra_.size+2} coordinates, which is too many for this factory."
273
+ end
144
274
  else
145
- if expect_z_
146
- _expect_token_type(::Numeric)
147
- extra_ << @cur_token
148
- _next_token
275
+ if @cur_factory_support_z
276
+ if @cur_expect_z
277
+ _expect_token_type(::Numeric)
278
+ extra_ << @cur_token
279
+ _next_token
280
+ else
281
+ extra_ << 0
282
+ end
149
283
  end
150
- if expect_m_
151
- _expect_token_type(::Numeric)
152
- extra_ << @cur_token
153
- _next_token
284
+ if @cur_factory_support_m
285
+ if @cur_expect_m
286
+ _expect_token_type(::Numeric)
287
+ extra_ << @cur_token
288
+ _next_token
289
+ else
290
+ extra_ << 0
291
+ end
154
292
  end
155
293
  end
156
294
  @cur_factory.point(x_, y_, *extra_)
157
295
  end
158
296
 
159
297
 
160
- def _parse_point(expect_z_, expect_m_, convert_empty_=false) # :nodoc:
298
+ def _parse_point(convert_empty_=false) # :nodoc:
161
299
  if convert_empty_ && @cur_token == 'empty'
162
300
  point_ = @cur_factory.multi_point([])
163
301
  else
164
302
  _expect_token_type(:begin)
165
303
  _next_token
166
- point_ = _parse_coords(expect_z_, expect_m_)
304
+ point_ = _parse_coords
167
305
  _expect_token_type(:end)
168
306
  end
169
307
  _next_token
@@ -171,13 +309,13 @@ module RGeo
171
309
  end
172
310
 
173
311
 
174
- def _parse_line_string(expect_z_, expect_m_) # :nodoc:
312
+ def _parse_line_string # :nodoc:
175
313
  points_ = []
176
314
  if @cur_token != 'empty'
177
315
  _expect_token_type(:begin)
178
316
  _next_token
179
317
  loop do
180
- points_ << _parse_coords(expect_z_, expect_m_)
318
+ points_ << _parse_coords
181
319
  break if @cur_token == :end
182
320
  _expect_token_type(:comma)
183
321
  _next_token
@@ -188,19 +326,19 @@ module RGeo
188
326
  end
189
327
 
190
328
 
191
- def _parse_polygon(expect_z_, expect_m_) # :nodoc:
329
+ def _parse_polygon # :nodoc:
192
330
  inner_rings_ = []
193
331
  if @cur_token == 'empty'
194
332
  outer_ring_ = @cur_factory.linear_ring([])
195
333
  else
196
334
  _expect_token_type(:begin)
197
335
  _next_token
198
- outer_ring_ = _parse_line_string(expect_z_, expect_m_)
336
+ outer_ring_ = _parse_line_string
199
337
  loop do
200
338
  break if @cur_token == :end
201
339
  _expect_token_type(:comma)
202
340
  _next_token
203
- inner_rings_ << _parse_line_string(expect_z_, expect_m_)
341
+ inner_rings_ << _parse_line_string
204
342
  end
205
343
  end
206
344
  _next_token
@@ -208,13 +346,13 @@ module RGeo
208
346
  end
209
347
 
210
348
 
211
- def _parse_geometry_collection(expect_z_, expect_m_) # :nodoc:
349
+ def _parse_geometry_collection # :nodoc:
212
350
  geometries_ = []
213
351
  if @cur_token != 'empty'
214
352
  _expect_token_type(:begin)
215
353
  _next_token
216
354
  loop do
217
- geometries_ << _parse_type_tag(expect_z_, expect_m_)
355
+ geometries_ << _parse_type_tag
218
356
  break if @cur_token == :end
219
357
  _expect_token_type(:comma)
220
358
  _next_token
@@ -225,13 +363,13 @@ module RGeo
225
363
  end
226
364
 
227
365
 
228
- def _parse_multi_point(expect_z_, expect_m_) # :nodoc:
366
+ def _parse_multi_point # :nodoc:
229
367
  points_ = []
230
368
  if @cur_token != 'empty'
231
369
  _expect_token_type(:begin)
232
370
  _next_token
233
371
  loop do
234
- points_ << _parse_point(expect_z_, expect_m_)
372
+ points_ << _parse_point
235
373
  break if @cur_token == :end
236
374
  _expect_token_type(:comma)
237
375
  _next_token
@@ -242,13 +380,13 @@ module RGeo
242
380
  end
243
381
 
244
382
 
245
- def _parse_multi_line_string(expect_z_, expect_m_) # :nodoc:
383
+ def _parse_multi_line_string # :nodoc:
246
384
  line_strings_ = []
247
385
  if @cur_token != 'empty'
248
386
  _expect_token_type(:begin)
249
387
  _next_token
250
388
  loop do
251
- line_strings_ << _parse_line_string(expect_z_, expect_m_)
389
+ line_strings_ << _parse_line_string
252
390
  break if @cur_token == :end
253
391
  _expect_token_type(:comma)
254
392
  _next_token
@@ -259,13 +397,13 @@ module RGeo
259
397
  end
260
398
 
261
399
 
262
- def _parse_multi_polygon(expect_z_, expect_m_) # :nodoc:
400
+ def _parse_multi_polygon # :nodoc:
263
401
  polygons_ = []
264
402
  if @cur_token != 'empty'
265
403
  _expect_token_type(:begin)
266
404
  _next_token
267
405
  loop do
268
- polygons_ << _parse_polygon(expect_z_, expect_m_)
406
+ polygons_ << _parse_polygon
269
407
  break if @cur_token == :end
270
408
  _expect_token_type(:comma)
271
409
  _next_token
@@ -282,6 +420,12 @@ module RGeo
282
420
  end
283
421
 
284
422
 
423
+ def _clean_scanner # :nodoc:
424
+ @_scanner = nil
425
+ @cur_token = nil
426
+ end
427
+
428
+
285
429
  def _expect_token_type(type_) # :nodoc:
286
430
  unless type_ === @cur_token
287
431
  raise Errors::ParseError, "#{type_.inspect} expected but #{@cur_token.inspect} found."