Class: RGeo::WKRep::WKTParser

Inherits:
Object
  • Object
show all
Defined in:
lib/rgeo/wkrep/wkt_parser.rb

Overview

This class provides the functionality of parsing a geometry from WKT (well-known text) format. You may also customize the parser to recognize PostGIS EWKT extensions to the input, or Simple Features Specification 1.2 extensions for Z and M coordinates.

To use this class, create an instance with the desired settings and customizations, and call the parse method.

Configuration options

You must provide each parser with an RGeo::Feature::FactoryGenerator. It should understand the configuration options :srid, :has_z_coordinate, and :has_m_coordinate. You may also pass a specific RGeo::Feature::Factory, or nil to specify the default Cartesian FactoryGenerator.

The following additional options are recognized. These can be passed to the constructor, or set on the object afterwards.

:support_ewkt

Activate support for PostGIS EWKT type tags, which appends an “M” to tags to indicate the presence of M but not Z, and also recognizes the SRID prefix. Default is false.

:support_wkt12

Activate support for SFS 1.2 extensions to the type codes, which use a “M”, “Z”, or “ZM” token to signal the presence of Z and M values in the data. SFS 1.2 types such as triangle, tin, and polyhedralsurface are NOT yet supported. Default is false.

:strict_wkt11

If true, parsing will proceed in SFS 1.1 strict mode, which disallows any values other than X or Y. This has no effect if support_ewkt or support_wkt12 are active. Default is false.

:ignore_extra_tokens

If true, extra tokens at the end of the data are ignored. If false (the default), extra tokens will trigger a parse error.

:default_srid

A SRID to pass to the factory generator if no SRID is present in the input. Defaults to nil (i.e. don’t specify a SRID).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(factory_generator_ = nil, opts_ = {}) ⇒ WKTParser

Create and configure a WKT parser. See the WKTParser documentation for the options that can be passed.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 54

def initialize(factory_generator_ = nil, opts_ = {})
  if factory_generator_.is_a?(Feature::Factory::Instance)
    @factory_generator = Feature::FactoryGenerator.single(factory_generator_)
    @exact_factory = factory_generator_
  elsif factory_generator_.respond_to?(:call)
    @factory_generator = factory_generator_
    @exact_factory = nil
  else
    @factory_generator = Cartesian.method(:preferred_factory)
    @exact_factory = nil
  end
  @support_ewkt = opts_[:support_ewkt] ? true : false
  @support_wkt12 = opts_[:support_wkt12] ? true : false
  @strict_wkt11 = @support_ewkt || @support_wkt12 ? false : opts_[:strict_wkt11] ? true : false
  @ignore_extra_tokens = opts_[:ignore_extra_tokens] ? true : false
  @default_srid = opts_[:default_srid]
end

Instance Attribute Details

#exact_factoryObject (readonly)

If this parser was given an exact factory, returns it; otherwise returns nil.



77
78
79
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 77

def exact_factory
  @exact_factory
end

#factory_generatorObject (readonly)

Returns the factory generator. See WKTParser for details.



73
74
75
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 73

def factory_generator
  @factory_generator
end

Instance Method Details

#_check_factory_supportObject

:nodoc:



141
142
143
144
145
146
147
148
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 141

def _check_factory_support # :nodoc:
  if @cur_expect_z && !@cur_factory_support_z
    raise Error::ParseError, "Geometry calls for Z coordinate but factory doesn't support it."
  end
  if @cur_expect_m && !@cur_factory_support_m
    raise Error::ParseError, "Geometry calls for M coordinate but factory doesn't support it."
  end
end

#_clean_scannerObject

:nodoc:



380
381
382
383
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 380

def _clean_scanner # :nodoc:
  @_scanner = nil
  @cur_token = nil
end

#_ensure_factoryObject

:nodoc:



150
151
152
153
154
155
156
157
158
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 150

def _ensure_factory # :nodoc:
  unless @cur_factory
    @cur_factory = @factory_generator.call(srid: @cur_srid, has_z_coordinate: @cur_expect_z, has_m_coordinate: @cur_expect_m)
    @cur_factory_support_z = @cur_factory.property(:has_z_coordinate) ? true : false
    @cur_factory_support_m = @cur_factory.property(:has_m_coordinate) ? true : false
    _check_factory_support unless @cur_expect_z.nil?
  end
  @cur_factory
end

#_expect_token_type(type_) ⇒ Object

:nodoc:



385
386
387
388
389
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 385

def _expect_token_type(type_) # :nodoc:
  unless type_ === @cur_token
    raise Error::ParseError, "#{type_.inspect} expected but #{@cur_token.inspect} found."
  end
end

#_next_tokenObject

:nodoc:



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 391

def _next_token # :nodoc:
  if @_scanner.scan_until(/\(|\)|\[|\]|,|[^\s\(\)\[\],]+/)
    token_ = @_scanner.matched
    case token_
    when /^[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?$/
      @cur_token = token_.to_f
    when /^[a-z]+$/
      @cur_token = token_
    when ","
      @cur_token = :comma
    when "(", "["
      @cur_token = :begin
    when "]", ")"
      @cur_token = :end
    else
      raise Error::ParseError, "Bad token: #{token_.inspect}"
    end
  else
    @cur_token = nil
  end
  @cur_token
end

#_parse_coordsObject

:nodoc:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 216

def _parse_coords # :nodoc:
  _expect_token_type(::Numeric)
  x_ = @cur_token
  _next_token
  _expect_token_type(::Numeric)
  y_ = @cur_token
  _next_token
  extra_ = []
  if @cur_expect_z.nil?
    while ::Numeric === @cur_token
      extra_ << @cur_token
      _next_token
    end
    num_extras_ = extra_.size
    @cur_expect_z = num_extras_ > 0 && (!@cur_factory || @cur_factory_support_z) ? true : false
    num_extras_ -= 1 if @cur_expect_z
    @cur_expect_m = num_extras_ > 0 && (!@cur_factory || @cur_factory_support_m) ? true : false
    num_extras_ -= 1 if @cur_expect_m
    if num_extras_ > 0
      raise Error::ParseError, "Found #{extra_.size + 2} coordinates, which is too many for this factory."
    end
    _ensure_factory
  else
    val_ = 0
    if @cur_expect_z
      _expect_token_type(::Numeric)
      val_ = @cur_token
      _next_token
    end
    extra_ << val_ if @cur_factory_support_z
    val_ = 0
    if @cur_expect_m
      _expect_token_type(::Numeric)
      val_ = @cur_token
      _next_token
    end
    extra_ << val_ if @cur_factory_support_m
  end
  @cur_factory.point(x_, y_, *extra_)
end

#_parse_geometry_collectionObject

:nodoc:



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 305

def _parse_geometry_collection # :nodoc:
  geometries_ = []
  if @cur_token != "empty"
    _expect_token_type(:begin)
    _next_token
    loop do
      geometries_ << _parse_type_tag(true)
      break if @cur_token == :end
      _expect_token_type(:comma)
      _next_token
    end
  end
  _next_token
  _ensure_factory.collection(geometries_)
end

#_parse_line_stringObject

:nodoc:



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 270

def _parse_line_string # :nodoc:
  points_ = []
  if @cur_token != "empty"
    _expect_token_type(:begin)
    _next_token
    loop do
      points_ << _parse_coords
      break if @cur_token == :end
      _expect_token_type(:comma)
      _next_token
    end
  end
  _next_token
  _ensure_factory.line_string(points_)
end

#_parse_multi_line_stringObject

:nodoc:



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 343

def _parse_multi_line_string # :nodoc:
  line_strings_ = []
  if @cur_token != "empty"
    _expect_token_type(:begin)
    _next_token
    loop do
      line_strings_ << _parse_line_string
      break if @cur_token == :end
      _expect_token_type(:comma)
      _next_token
    end
  end
  _next_token
  _ensure_factory.multi_line_string(line_strings_)
end

#_parse_multi_pointObject

:nodoc:



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 321

def _parse_multi_point # :nodoc:
  points_ = []
  if @cur_token != "empty"
    _expect_token_type(:begin)
    _next_token
    loop do
      uses_paren_ = @cur_token == :begin
      _next_token if uses_paren_
      points_ << _parse_coords
      if uses_paren_
        _expect_token_type(:end)
        _next_token
      end
      break if @cur_token == :end
      _expect_token_type(:comma)
      _next_token
    end
  end
  _next_token
  _ensure_factory.multi_point(points_)
end

#_parse_multi_polygonObject

:nodoc:



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 359

def _parse_multi_polygon  # :nodoc:
  polygons_ = []
  if @cur_token != "empty"
    _expect_token_type(:begin)
    _next_token
    loop do
      polygons_ << _parse_polygon
      break if @cur_token == :end
      _expect_token_type(:comma)
      _next_token
    end
  end
  _next_token
  _ensure_factory.multi_polygon(polygons_)
end

#_parse_point(convert_empty_ = false) ⇒ Object

:nodoc:



257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 257

def _parse_point(convert_empty_ = false) # :nodoc:
  if convert_empty_ && @cur_token == "empty"
    point_ = _ensure_factory.multi_point([])
  else
    _expect_token_type(:begin)
    _next_token
    point_ = _parse_coords
    _expect_token_type(:end)
  end
  _next_token
  point_
end

#_parse_polygonObject

:nodoc:



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 286

def _parse_polygon # :nodoc:
  inner_rings_ = []
  if @cur_token == "empty"
    outer_ring_ = _ensure_factory.linear_ring([])
  else
    _expect_token_type(:begin)
    _next_token
    outer_ring_ = _parse_line_string
    loop do
      break if @cur_token == :end
      _expect_token_type(:comma)
      _next_token
      inner_rings_ << _parse_line_string
    end
  end
  _next_token
  _ensure_factory.polygon(outer_ring_, inner_rings_)
end

#_parse_type_tag(_contained_) ⇒ Object

:nodoc:



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 160

def _parse_type_tag(_contained_) # :nodoc:
  _expect_token_type(::String)
  if @support_ewkt && @cur_token =~ /^(.+)(m)$/
    type_ = Regexp.last_match(1)
    zm_ = Regexp.last_match(2)
  else
    type_ = @cur_token
    zm_ = ""
  end
  _next_token
  if zm_.length == 0 && @support_wkt12 && @cur_token.is_a?(::String) && @cur_token =~ /^z?m?$/
    zm_ = @cur_token
    _next_token
  end
  if zm_.length > 0 || @strict_wkt11
    creating_expectation_ = @cur_expect_z.nil?
    expect_z_ = zm_[0, 1] == "z" ? true : false
    if @cur_expect_z.nil?
      @cur_expect_z = expect_z_
    elsif expect_z_ != @cur_expect_z
      raise Error::ParseError, "Surrounding collection has Z but contained geometry doesn't."
    end
    expect_m_ = zm_[-1, 1] == "m" ? true : false
    if @cur_expect_m.nil?
      @cur_expect_m = expect_m_
    elsif expect_m_ != @cur_expect_m
      raise Error::ParseError, "Surrounding collection has M but contained geometry doesn't."
    end
    if creating_expectation_
      if @cur_factory
        _check_factory_support
      else
        _ensure_factory
      end
    end
  end
  case type_
  when "point"
    _parse_point(true)
  when "linestring"
    _parse_line_string
  when "polygon"
    _parse_polygon
  when "geometrycollection"
    _parse_geometry_collection
  when "multipoint"
    _parse_multi_point
  when "multilinestring"
    _parse_multi_line_string
  when "multipolygon"
    _parse_multi_polygon
  else
    raise Error::ParseError, "Unknown type tag: #{type_.inspect}."
  end
end

#_propertiesObject

:nodoc:



103
104
105
106
107
108
109
110
111
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 103

def _properties # :nodoc:
  {
    "support_ewkt" => @support_ewkt,
    "support_wkt12" => @support_wkt12,
    "strict_wkt11" => @strict_wkt11,
    "ignore_extra_tokens" => @ignore_extra_tokens,
    "default_srid" => @default_srid
  }
end

#_start_scanner(str_) ⇒ Object

:nodoc:



375
376
377
378
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 375

def _start_scanner(str_)  # :nodoc:
  @_scanner = ::StringScanner.new(str_)
  _next_token
end

#ignore_extra_tokens?Boolean

Returns true if this parser ignores extra tokens. See WKTParser for details.

Returns:

  • (Boolean)


99
100
101
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 99

def ignore_extra_tokens?
  @ignore_extra_tokens
end

#parse(str_) ⇒ Object

Parse the given string, and return a geometry object.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 115

def parse(str_)
  str_ = str_.downcase
  @cur_factory = @exact_factory
  if @cur_factory
    @cur_factory_support_z = @cur_factory.property(:has_z_coordinate) ? true : false
    @cur_factory_support_m = @cur_factory.property(:has_m_coordinate) ? true : false
  end
  @cur_expect_z = nil
  @cur_expect_m = nil
  @cur_srid = @default_srid
  if @support_ewkt && str_ =~ /^srid=(\d+);/i
    str_ = $'
    @cur_srid = Regexp.last_match(1).to_i
  end
  begin
    _start_scanner(str_)
    obj_ = _parse_type_tag(false)
    if @cur_token && !@ignore_extra_tokens
      raise Error::ParseError, "Extra tokens beginning with #{@cur_token.inspect}."
    end
  ensure
    _clean_scanner
  end
  obj_
end

#strict_wkt11?Boolean

Returns true if this parser strictly adheres to WKT 1.1. See WKTParser for details.

Returns:

  • (Boolean)


93
94
95
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 93

def strict_wkt11?
  @strict_wkt11
end

#support_ewkt?Boolean

Returns true if this parser supports EWKT. See WKTParser for details.

Returns:

  • (Boolean)


81
82
83
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 81

def support_ewkt?
  @support_ewkt
end

#support_wkt12?Boolean

Returns true if this parser supports SFS 1.2 extensions. See WKTParser for details.

Returns:

  • (Boolean)


87
88
89
# File 'lib/rgeo/wkrep/wkt_parser.rb', line 87

def support_wkt12?
  @support_wkt12
end