rgeo 0.1.14 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
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."