rgeo 3.0.0.pre.rc.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -1
  3. data/ext/geos_c_impl/analysis.c +26 -23
  4. data/ext/geos_c_impl/analysis.h +8 -5
  5. data/ext/geos_c_impl/coordinates.c +27 -21
  6. data/ext/geos_c_impl/coordinates.h +5 -2
  7. data/ext/geos_c_impl/errors.c +15 -8
  8. data/ext/geos_c_impl/errors.h +4 -1
  9. data/ext/geos_c_impl/extconf.rb +40 -30
  10. data/ext/geos_c_impl/factory.c +405 -389
  11. data/ext/geos_c_impl/factory.h +71 -49
  12. data/ext/geos_c_impl/geometry.c +485 -408
  13. data/ext/geos_c_impl/geometry.h +5 -5
  14. data/ext/geos_c_impl/geometry_collection.c +269 -198
  15. data/ext/geos_c_impl/geometry_collection.h +6 -7
  16. data/ext/geos_c_impl/globals.c +99 -21
  17. data/ext/geos_c_impl/globals.h +3 -2
  18. data/ext/geos_c_impl/line_string.c +261 -220
  19. data/ext/geos_c_impl/line_string.h +5 -6
  20. data/ext/geos_c_impl/main.c +8 -9
  21. data/ext/geos_c_impl/point.c +62 -65
  22. data/ext/geos_c_impl/point.h +4 -5
  23. data/ext/geos_c_impl/polygon.c +121 -90
  24. data/ext/geos_c_impl/polygon.h +11 -9
  25. data/ext/geos_c_impl/preface.h +4 -13
  26. data/ext/geos_c_impl/ruby_more.c +39 -37
  27. data/ext/geos_c_impl/ruby_more.h +11 -2
  28. data/lib/rgeo/cartesian/analysis.rb +5 -3
  29. data/lib/rgeo/cartesian/bounding_box.rb +74 -79
  30. data/lib/rgeo/cartesian/calculations.rb +20 -26
  31. data/lib/rgeo/cartesian/factory.rb +52 -89
  32. data/lib/rgeo/cartesian/feature_methods.rb +0 -5
  33. data/lib/rgeo/cartesian/interface.rb +6 -5
  34. data/lib/rgeo/cartesian/planar_graph.rb +10 -16
  35. data/lib/rgeo/cartesian/sweepline_intersector.rb +1 -3
  36. data/lib/rgeo/cartesian/valid_op.rb +1 -3
  37. data/lib/rgeo/coord_sys/cs/entities.rb +299 -99
  38. data/lib/rgeo/coord_sys/cs/factories.rb +0 -2
  39. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +85 -37
  40. data/lib/rgeo/coord_sys.rb +1 -9
  41. data/lib/rgeo/feature/curve.rb +0 -11
  42. data/lib/rgeo/feature/factory.rb +26 -36
  43. data/lib/rgeo/feature/factory_generator.rb +6 -11
  44. data/lib/rgeo/feature/geometry.rb +41 -40
  45. data/lib/rgeo/feature/geometry_collection.rb +3 -4
  46. data/lib/rgeo/feature/line_string.rb +1 -2
  47. data/lib/rgeo/feature/linear_ring.rb +0 -1
  48. data/lib/rgeo/feature/multi_curve.rb +0 -6
  49. data/lib/rgeo/feature/multi_surface.rb +0 -1
  50. data/lib/rgeo/feature/point.rb +0 -1
  51. data/lib/rgeo/feature/polygon.rb +1 -2
  52. data/lib/rgeo/feature/surface.rb +0 -1
  53. data/lib/rgeo/feature/types.rb +73 -83
  54. data/lib/rgeo/geographic/factory.rb +93 -119
  55. data/lib/rgeo/geographic/interface.rb +62 -116
  56. data/lib/rgeo/geographic/projected_feature_methods.rb +2 -16
  57. data/lib/rgeo/geographic/projected_window.rb +36 -22
  58. data/lib/rgeo/geographic/{proj4_projector.rb → projector.rb} +3 -3
  59. data/lib/rgeo/geographic/simple_mercator_projector.rb +24 -21
  60. data/lib/rgeo/geographic/spherical_feature_methods.rb +8 -8
  61. data/lib/rgeo/geographic/spherical_math.rb +17 -20
  62. data/lib/rgeo/geographic.rb +1 -1
  63. data/lib/rgeo/geos/capi_factory.rb +70 -124
  64. data/lib/rgeo/geos/capi_feature_classes.rb +0 -29
  65. data/lib/rgeo/geos/ffi_factory.rb +90 -131
  66. data/lib/rgeo/geos/ffi_feature_methods.rb +73 -128
  67. data/lib/rgeo/geos/interface.rb +21 -36
  68. data/lib/rgeo/geos/utils.rb +3 -3
  69. data/lib/rgeo/geos/zm_factory.rb +53 -76
  70. data/lib/rgeo/geos/zm_feature_methods.rb +16 -34
  71. data/lib/rgeo/geos.rb +2 -5
  72. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +5 -18
  73. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -2
  74. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +18 -42
  75. data/lib/rgeo/impl_helper/basic_point_methods.rb +1 -13
  76. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +16 -25
  77. data/lib/rgeo/impl_helper/utils.rb +21 -0
  78. data/lib/rgeo/impl_helper/valid_op.rb +12 -16
  79. data/lib/rgeo/impl_helper/validity_check.rb +3 -3
  80. data/lib/rgeo/version.rb +1 -1
  81. data/lib/rgeo/wkrep/wkb_generator.rb +73 -63
  82. data/lib/rgeo/wkrep/wkb_parser.rb +33 -31
  83. data/lib/rgeo/wkrep/wkt_generator.rb +52 -45
  84. data/lib/rgeo/wkrep/wkt_parser.rb +48 -35
  85. data/lib/rgeo.rb +1 -3
  86. metadata +10 -9
@@ -9,6 +9,27 @@
9
9
  module RGeo
10
10
  module ImplHelper # :nodoc:
11
11
  module Utils # :nodoc:
12
+ # Helper function to create coord_sys from
13
+ # common options in most factories. Returns
14
+ # a hash with finalized coord sys info after processing.
15
+ #
16
+ # The reason we return the data as a hash instead of assigning
17
+ # instance variables is because some classes need to do this
18
+ # multiple times with different values and others pass the data
19
+ # to a CAPI or FFI.
20
+ def self.setup_coord_sys(srid, coord_sys, coord_sys_class)
21
+ coord_sys_class = CoordSys::CONFIG.default_coord_sys_class unless coord_sys_class.is_a?(Class)
22
+
23
+ coord_sys = coord_sys_class.create_from_wkt(coord_sys) if coord_sys.is_a?(String)
24
+
25
+ srid ||= coord_sys.authority_code if coord_sys
26
+ srid = srid.to_i
27
+ # Create a coord sys based on the SRID if one was not given
28
+ coord_sys = coord_sys_class.create(srid) if coord_sys.nil? && srid != 0
29
+
30
+ { coord_sys: coord_sys, srid: srid }
31
+ end
32
+
12
33
  private
13
34
 
14
35
  def symbolize_hash(hash)
@@ -181,12 +181,12 @@ module RGeo
181
181
 
182
182
  # Checks that the given point has valid coordinates.
183
183
  #
184
- # @param pt [RGeo::Feature::Point]
184
+ # @param point [RGeo::Feature::Point]
185
185
  #
186
186
  # @return [String] invalid_reason
187
- def check_invalid_coordinate(pt)
188
- x = pt.x
189
- y = pt.y
187
+ def check_invalid_coordinate(point)
188
+ x = point.x
189
+ y = point.y
190
190
  return if x.finite? && y.finite? && x.real? && y.real?
191
191
 
192
192
  Error::INVALID_COORDINATE
@@ -265,9 +265,7 @@ module RGeo
265
265
 
266
266
  poly.interior_rings.each do |interior|
267
267
  test_pt = interior.start_point
268
- unless shell.contains?(test_pt) || poly.exterior_ring.contains?(test_pt)
269
- return Error::HOLE_OUTSIDE_SHELL
270
- end
268
+ return Error::HOLE_OUTSIDE_SHELL unless shell.contains?(test_pt) || poly.exterior_ring.contains?(test_pt)
271
269
  end
272
270
 
273
271
  nil
@@ -322,27 +320,25 @@ module RGeo
322
320
 
323
321
  # Checks that polygons do not intersect in a multipolygon.
324
322
  #
325
- # @param mp [RGeo::Feature::MultiPolygon]
323
+ # @param mpoly [RGeo::Feature::MultiPolygon]
326
324
  #
327
325
  # @return [String] invalid_reason
328
- def check_consistent_area_mp(mp)
329
- mp.geometries.combination(2) do |p1, p2|
330
- if p1.exterior_ring.crosses?(p2.exterior_ring)
331
- return Error::SELF_INTERSECTION
332
- end
326
+ def check_consistent_area_mp(mpoly)
327
+ mpoly.geometries.combination(2) do |p1, p2|
328
+ return Error::SELF_INTERSECTION if p1.exterior_ring.crosses?(p2.exterior_ring)
333
329
  end
334
330
  nil
335
331
  end
336
332
 
337
333
  # Checks that individual polygons within a multipolygon are not nested.
338
334
  #
339
- # @param mp [RGeo::Feature::MultiPolygon]
335
+ # @param mpoly [RGeo::Feature::MultiPolygon]
340
336
  #
341
337
  # @return [String] invalid_reason
342
- def check_shells_not_nested(mp)
338
+ def check_shells_not_nested(mpoly)
343
339
  # Since we've passed the consistent area test, we can just check
344
340
  # that one point lies in the other.
345
- mp.geometries.combination(2) do |p1, p2|
341
+ mpoly.geometries.combination(2) do |p1, p2|
346
342
  if p1.contains?(p2.exterior_ring.start_point) || p2.contains?(p1.exterior_ring.start_point)
347
343
  return Error::NESTED_SHELLS
348
344
  end
@@ -31,7 +31,7 @@ module RGeo
31
31
  # Trivial methods
32
32
  :num_points, :locate_along, :locate_between,
33
33
  # Comparison
34
- :equals?, :rep_equals?, :eql?, :==, :'!='
34
+ :equals?, :rep_equals?, :eql?, :==, :"!="
35
35
  ].freeze
36
36
  private_constant :UNCHECKED_METHODS
37
37
 
@@ -109,7 +109,7 @@ module RGeo
109
109
  def invalid_reason
110
110
  if defined?(super) == "super"
111
111
  raise Error::RGeoError, "ValidityCheck MUST be loaded before " \
112
- "definition of #{self.class}##{__method__}."
112
+ "definition of #{self.class}##{__method__}."
113
113
  end
114
114
 
115
115
  raise Error::UnsupportedOperation, "Method #{self.class}##{__method__} not defined."
@@ -120,7 +120,7 @@ module RGeo
120
120
  def make_valid
121
121
  if defined?(super) == "super"
122
122
  raise Error::RGeoError, "ValidityCheck MUST be loaded before " \
123
- "definition of #{self.class}##{__method__}."
123
+ "definition of #{self.class}##{__method__}."
124
124
  end
125
125
 
126
126
  raise Error::UnsupportedOperation, "Method #{self.class}##{__method__} not defined."
data/lib/rgeo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RGeo
4
- VERSION = "3.0.0-rc.2"
4
+ VERSION = "3.0.0"
5
5
  end
@@ -39,7 +39,6 @@ module RGeo
39
39
  # [<tt>:little_endian</tt>]
40
40
  # If true, output little endian (NDR) byte order. If false, output
41
41
  # big endian (XDR), or network byte order. Default is false.
42
-
43
42
  class WKBGenerator
44
43
  # :stopdoc:
45
44
  TYPE_CODES = {
@@ -100,103 +99,114 @@ module RGeo
100
99
  def generate(obj)
101
100
  factory = obj.factory
102
101
  if @type_format == :ewkb || @type_format == :wkb12
103
- @cur_has_z = factory.property(:has_z_coordinate)
104
- @cur_has_m = factory.property(:has_m_coordinate)
102
+ has_z = factory.property(:has_z_coordinate)
103
+ has_m = factory.property(:has_m_coordinate)
105
104
  else
106
- @cur_has_z = nil
107
- @cur_has_m = nil
105
+ has_z = false
106
+ has_m = false
108
107
  end
109
- @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
110
- start_emitter
111
- generate_feature(obj, true)
112
- finish_emitter
108
+ result = Result.new(has_z, has_m)
109
+ generate_feature(obj, result, toplevel: true)
110
+ result.emit(@hex_format)
113
111
  end
114
112
 
115
113
  private
116
114
 
117
- def emit_byte(value)
118
- @cur_array << [value].pack("C")
115
+ class Result
116
+ def initialize(has_z, has_m)
117
+ @buffer = []
118
+ @has_z = has_z
119
+ @has_m = has_m
120
+ end
121
+
122
+ def <<(data)
123
+ @buffer << data
124
+ end
125
+
126
+ def emit(hex_format)
127
+ str = @buffer.join
128
+ hex_format ? str.unpack1("H*") : str
129
+ end
130
+
131
+ def z?
132
+ @has_z
133
+ end
134
+
135
+ def m?
136
+ @has_m
137
+ end
138
+ end
139
+ private_constant :Result
140
+
141
+ def emit_byte(value, rval)
142
+ rval << [value].pack("C")
119
143
  end
120
144
 
121
- def emit_integer(value)
122
- @cur_array << [value].pack(@little_endian ? "V" : "N")
145
+ def emit_integer(value, rval)
146
+ rval << [value].pack(@little_endian ? "V" : "N")
123
147
  end
124
148
 
125
- def emit_doubles(array)
126
- @cur_array << array.pack(@little_endian ? "E*" : "G*")
149
+ def emit_doubles(array, rval)
150
+ rval << array.pack(@little_endian ? "E*" : "G*")
127
151
  end
128
152
 
129
- def emit_line_string_coords(obj)
153
+ def emit_line_string_coords(obj, rval)
130
154
  array = []
131
- obj.points.each { |p| point_coords(p, array) }
132
- emit_integer(obj.num_points)
133
- emit_doubles(array)
155
+ obj.points.each { |pt| point_coords(pt, rval, array) }
156
+ emit_integer(obj.num_points, rval)
157
+ emit_doubles(array, rval)
134
158
  end
135
159
 
136
- def point_coords(obj, array = [])
160
+ def point_coords(obj, rval, array = [])
137
161
  array << obj.x
138
162
  array << obj.y
139
- array << obj.z if @cur_has_z
140
- array << obj.m if @cur_has_m
163
+ array << obj.z if rval.z?
164
+ array << obj.m if rval.m?
141
165
  array
142
166
  end
143
167
 
144
- def start_emitter
145
- @cur_array = []
146
- end
147
-
148
- def finish_emitter
149
- str = @cur_array.join
150
- @cur_array = nil
151
- @hex_format ? str.unpack("H*")[0] : str
152
- end
153
-
154
- def generate_feature(obj, toplevel = false)
155
- emit_byte(@little_endian ? 1 : 0)
168
+ def generate_feature(obj, rval, toplevel: false)
169
+ emit_byte(@little_endian ? 1 : 0, rval)
156
170
  type = obj.geometry_type
157
171
  type_code = TYPE_CODES[type]
158
- unless type_code
159
- raise Error::ParseError, "Unrecognized Geometry Type: #{type}"
160
- end
172
+ raise Error::ParseError, "Unrecognized Geometry Type: #{type}" unless type_code
161
173
  emit_srid = false
162
- if @type_format == :ewkb
163
- type_code |= 0x80000000 if @cur_has_z
164
- type_code |= 0x40000000 if @cur_has_m
174
+ case @type_format
175
+ when :ewkb
176
+ type_code |= 0x80000000 if rval.z?
177
+ type_code |= 0x40000000 if rval.m?
165
178
  if @emit_ewkb_srid && toplevel
166
179
  type_code |= 0x20000000
167
180
  emit_srid = true
168
181
  end
169
- elsif @type_format == :wkb12
170
- type_code += 1000 if @cur_has_z
171
- type_code += 2000 if @cur_has_m
182
+ when :wkb12
183
+ type_code += 1000 if rval.z?
184
+ type_code += 2000 if rval.m?
172
185
  end
173
- emit_integer(type_code)
174
- emit_integer(obj.srid) if emit_srid
186
+ emit_integer(type_code, rval)
187
+ emit_integer(obj.srid, rval) if emit_srid
188
+ type_is_collection = [
189
+ Feature::GeometryCollection,
190
+ Feature::MultiPoint,
191
+ Feature::MultiLineString,
192
+ Feature::MultiPolygon
193
+ ].include?(type)
175
194
  if type == Feature::Point
176
- emit_doubles(point_coords(obj))
195
+ emit_doubles(point_coords(obj, rval), rval)
177
196
  elsif type.subtype_of?(Feature::LineString)
178
- emit_line_string_coords(obj)
197
+ emit_line_string_coords(obj, rval)
179
198
  elsif type == Feature::Polygon
180
199
  exterior_ring = obj.exterior_ring
181
200
  if exterior_ring.empty?
182
- emit_integer(0)
201
+ emit_integer(0, rval)
183
202
  else
184
- emit_integer(1 + obj.num_interior_rings)
185
- emit_line_string_coords(exterior_ring)
186
- obj.interior_rings.each { |r| emit_line_string_coords(r) }
203
+ emit_integer(1 + obj.num_interior_rings, rval)
204
+ emit_line_string_coords(exterior_ring, rval)
205
+ obj.interior_rings.each { |r| emit_line_string_coords(r, rval) }
187
206
  end
188
- elsif type == Feature::GeometryCollection
189
- emit_integer(obj.num_geometries)
190
- obj.each { |g| generate_feature(g) }
191
- elsif type == Feature::MultiPoint
192
- emit_integer(obj.num_geometries)
193
- obj.each { |g| generate_feature(g) }
194
- elsif type == Feature::MultiLineString
195
- emit_integer(obj.num_geometries)
196
- obj.each { |g| generate_feature(g) }
197
- elsif type == Feature::MultiPolygon
198
- emit_integer(obj.num_geometries)
199
- obj.each { |g| generate_feature(g) }
207
+ elsif type_is_collection
208
+ emit_integer(obj.num_geometries, rval)
209
+ obj.each { |g| generate_feature(g, rval) }
200
210
  end
201
211
  end
202
212
  end
@@ -42,7 +42,6 @@ module RGeo
42
42
  # [<tt>:default_srid</tt>]
43
43
  # A SRID to pass to the factory generator if no SRID is present in
44
44
  # the input. Defaults to nil (i.e. don't specify a SRID).
45
-
46
45
  class WKBParser
47
46
  # Create and configure a WKB parser. See the WKBParser
48
47
  # documentation for the options that can be passed.
@@ -62,6 +61,7 @@ module RGeo
62
61
  @support_wkb12 = opts[:support_wkb12] ? true : false
63
62
  @ignore_extra_bytes = opts[:ignore_extra_bytes] ? true : false
64
63
  @default_srid = opts[:default_srid]
64
+ @mutex = Mutex.new
65
65
  end
66
66
 
67
67
  # Returns the factory generator. See WKBParser for details.
@@ -105,32 +105,32 @@ module RGeo
105
105
  # reasons but deprecated. Use #parse instead.
106
106
 
107
107
  def parse(data)
108
- data = [data].pack("H*") if data[0, 1] =~ /[0-9a-fA-F]/
109
- @cur_has_z = nil
110
- @cur_has_m = nil
111
- @cur_srid = nil
112
- @cur_dims = 2
113
- @cur_factory = nil
114
- begin
115
- start_scanner(data)
116
- obj = parse_object(false)
117
- unless @ignore_extra_bytes
118
- bytes = bytes_remaining
119
- if bytes > 0
120
- raise Error::ParseError, "Found #{bytes} extra bytes at the end of the stream."
108
+ @mutex.synchronize do
109
+ data = [data].pack("H*") if data[0, 1] =~ /[0-9a-fA-F]/
110
+ @cur_has_z = nil
111
+ @cur_has_m = nil
112
+ @cur_srid = nil
113
+ @cur_dims = 2
114
+ @cur_factory = nil
115
+ begin
116
+ start_scanner(data)
117
+ obj = parse_object(false)
118
+ unless @ignore_extra_bytes
119
+ bytes = bytes_remaining
120
+ raise Error::ParseError, "Found #{bytes} extra bytes at the end of the stream." if bytes > 0
121
121
  end
122
+ ensure
123
+ @data = nil
122
124
  end
123
- ensure
124
- @data = nil
125
+ obj
125
126
  end
126
- obj
127
127
  end
128
128
  alias parse_hex parse
129
129
 
130
130
  private
131
131
 
132
132
  def parse_object(contained)
133
- endian_value = get_byte
133
+ endian_value = byte
134
134
  case endian_value
135
135
  when 0
136
136
  little_endian = false
@@ -158,14 +158,20 @@ module RGeo
158
158
  if contained != true && contained != type_code
159
159
  raise Error::ParseError, "Enclosed type=#{type_code} is different from container constraint #{contained}"
160
160
  end
161
+
161
162
  if has_z != @cur_has_z
162
163
  raise Error::ParseError, "Enclosed hasZ=#{has_z} is different from toplevel hasZ=#{@cur_has_z}"
163
164
  end
165
+
164
166
  if has_m != @cur_has_m
165
167
  raise Error::ParseError, "Enclosed hasM=#{has_m} is different from toplevel hasM=#{@cur_has_m}"
166
168
  end
169
+
167
170
  if srid && srid != @cur_srid
168
- raise Error::ParseError, "Enclosed SRID #{srid} is different from toplevel srid #{@cur_srid || '(unspecified)'}"
171
+ raise(
172
+ Error::ParseError,
173
+ "Enclosed SRID #{srid} is different from toplevel srid #{@cur_srid || '(unspecified)'}"
174
+ )
169
175
  end
170
176
  else
171
177
  @cur_has_z = has_z
@@ -173,9 +179,11 @@ module RGeo
173
179
  @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
174
180
  @cur_srid = srid
175
181
  @cur_factory = @factory_generator.call(srid: @cur_srid, has_z_coordinate: has_z, has_m_coordinate: has_m)
182
+
176
183
  if @cur_has_z && !@cur_factory.property(:has_z_coordinate)
177
184
  raise Error::ParseError, "Data has Z coordinates but the factory doesn't have Z coordinates"
178
185
  end
186
+
179
187
  if @cur_has_m && !@cur_factory.property(:has_m_coordinate)
180
188
  raise Error::ParseError, "Data has M coordinates but the factory doesn't have M coordinates"
181
189
  end
@@ -219,29 +227,23 @@ module RGeo
219
227
  @len - @pos
220
228
  end
221
229
 
222
- def get_byte
223
- if @pos + 1 > @len
224
- raise Error::ParseError, "Not enough bytes left to fulfill 1 byte"
225
- end
230
+ def byte
231
+ raise Error::ParseError, "Not enough bytes left to fulfill 1 byte" if @pos + 1 > @len
226
232
  str = @data[@pos, 1]
227
233
  @pos += 1
228
- str.unpack("C").first
234
+ str.unpack1("C")
229
235
  end
230
236
 
231
237
  def get_integer(little_endian)
232
- if @pos + 4 > @len
233
- raise Error::ParseError, "Not enough bytes left to fulfill 1 integer"
234
- end
238
+ raise Error::ParseError, "Not enough bytes left to fulfill 1 integer" if @pos + 4 > @len
235
239
  str = @data[@pos, 4]
236
240
  @pos += 4
237
- str.unpack(little_endian ? "V" : "N").first
241
+ str.unpack1(little_endian ? "V" : "N")
238
242
  end
239
243
 
240
244
  def get_doubles(little_endian, count)
241
245
  len = 8 * count
242
- if @pos + len > @len
243
- raise Error::ParseError, "Not enough bytes left to fulfill #{count} doubles"
244
- end
246
+ raise Error::ParseError, "Not enough bytes left to fulfill #{count} doubles" if @pos + len > @len
245
247
  str = @data[@pos, len]
246
248
  @pos += len
247
249
  str.unpack("#{little_endian ? 'E' : 'G'}*")
@@ -45,15 +45,16 @@ module RGeo
45
45
  # letters to lower case; or nil, indicating no case changes from
46
46
  # the default (which is not specified exactly, but is chosen by the
47
47
  # generator to emphasize readability.) Default is nil.
48
-
49
48
  class WKTGenerator
50
49
  # Create and configure a WKT generator. See the WKTGenerator
51
50
  # documentation for the options that can be passed.
52
51
 
53
52
  def initialize(opts = {})
54
53
  @tag_format = opts[:tag_format] || opts[:type_format] || :wkt11
55
- @emit_ewkt_srid = @tag_format == :ewkt ?
56
- (opts[:emit_ewkt_srid] ? true : false) : nil
54
+ @emit_ewkt_srid =
55
+ if @tag_format == :ewkt
56
+ (opts[:emit_ewkt_srid] ? true : false)
57
+ end
57
58
  @square_brackets = opts[:square_brackets] ? true : false
58
59
  @convert_case = opts[:convert_case]
59
60
  end
@@ -93,16 +94,17 @@ module RGeo
93
94
  @end_bracket = @square_brackets ? "]" : ")"
94
95
  factory = obj.factory
95
96
  if @tag_format == :wkt11_strict
96
- @cur_support_z = nil
97
- @cur_support_m = nil
97
+ support_z = false
98
+ support_m = false
98
99
  else
99
- @cur_support_z = factory.property(:has_z_coordinate)
100
- @cur_support_m = factory.property(:has_m_coordinate)
100
+ support_z = factory.property(:has_z_coordinate)
101
+ support_m = factory.property(:has_m_coordinate)
101
102
  end
102
- str = generate_feature(obj, true)
103
- if @convert_case == :upper
103
+ str = generate_feature(obj, support_z, support_m, toplevel: true)
104
+ case @convert_case
105
+ when :upper
104
106
  str.upcase
105
- elsif @convert_case == :lower
107
+ when :lower
106
108
  str.downcase
107
109
  else
108
110
  str
@@ -111,99 +113,104 @@ module RGeo
111
113
 
112
114
  private
113
115
 
114
- def generate_feature(obj, toplevel = false)
116
+ def generate_feature(obj, support_z, support_m, toplevel: false)
115
117
  type = obj.geometry_type
116
118
  type = Feature::LineString if type.subtype_of?(Feature::LineString)
117
119
  tag = type.type_name.dup
118
- if @tag_format == :ewkt
119
- tag << "M" if @cur_support_m && !@cur_support_z
120
+ case @tag_format
121
+ when :ewkt
122
+ tag << "M" if support_m && !support_z
120
123
  tag = "SRID=#{obj.srid};#{tag}" if toplevel && @emit_ewkt_srid
121
- elsif @tag_format == :wkt12
122
- if @cur_support_z
123
- if @cur_support_m
124
- tag << " ZM"
125
- else
126
- tag << " Z"
127
- end
128
- elsif @cur_support_m
124
+ when :wkt12
125
+ if support_z
126
+ tag <<
127
+ if support_m
128
+ " ZM"
129
+ else
130
+ " Z"
131
+ end
132
+ elsif support_m
129
133
  tag << " M"
130
134
  end
131
135
  end
132
136
  if type == Feature::Point
133
- "#{tag} #{generate_point(obj)}"
137
+ "#{tag} #{generate_point(obj, support_z, support_m)}"
134
138
  elsif type == Feature::LineString
135
- "#{tag} #{generate_line_string(obj)}"
139
+ "#{tag} #{generate_line_string(obj, support_z, support_m)}"
136
140
  elsif type == Feature::Polygon
137
- "#{tag} #{generate_polygon(obj)}"
141
+ "#{tag} #{generate_polygon(obj, support_z, support_m)}"
138
142
  elsif type == Feature::GeometryCollection
139
- "#{tag} #{generate_geometry_collection(obj)}"
143
+ "#{tag} #{generate_geometry_collection(obj, support_z, support_m)}"
140
144
  elsif type == Feature::MultiPoint
141
- "#{tag} #{generate_multi_point(obj)}"
145
+ "#{tag} #{generate_multi_point(obj, support_z, support_m)}"
142
146
  elsif type == Feature::MultiLineString
143
- "#{tag} #{generate_multi_line_string(obj)}"
147
+ "#{tag} #{generate_multi_line_string(obj, support_z, support_m)}"
144
148
  elsif type == Feature::MultiPolygon
145
- "#{tag} #{generate_multi_polygon(obj)}"
149
+ "#{tag} #{generate_multi_polygon(obj, support_z, support_m)}"
146
150
  else
147
151
  raise Error::ParseError, "Unrecognized geometry type: #{type}"
148
152
  end
149
153
  end
150
154
 
151
- def generate_coords(obj)
155
+ def generate_coords(obj, support_z, support_m)
152
156
  str = +"#{obj.x} #{obj.y}"
153
- str << " #{obj.z}" if @cur_support_z
154
- str << " #{obj.m}" if @cur_support_m
157
+ str << " #{obj.z}" if support_z
158
+ str << " #{obj.m}" if support_m
155
159
  str
156
160
  end
157
161
 
158
- def generate_point(obj)
159
- "#{@begin_bracket}#{generate_coords(obj)}#{@end_bracket}"
162
+ def generate_point(obj, support_z, support_m)
163
+ "#{@begin_bracket}#{generate_coords(obj, support_z, support_m)}#{@end_bracket}"
160
164
  end
161
165
 
162
- def generate_line_string(obj)
166
+ def generate_line_string(obj, support_z, support_m)
163
167
  if obj.empty?
164
168
  "EMPTY"
165
169
  else
166
- "#{@begin_bracket}#{obj.points.map { |p| generate_coords(p) }.join(', ')}#{@end_bracket}"
170
+ points = obj.points.map { |p| generate_coords(p, support_z, support_m) }
171
+ "#{@begin_bracket}#{points.join(', ')}#{@end_bracket}"
167
172
  end
168
173
  end
169
174
 
170
- def generate_polygon(obj)
175
+ def generate_polygon(obj, support_z, support_m)
171
176
  if obj.empty?
172
177
  "EMPTY"
173
178
  else
174
- "#{@begin_bracket}#{([generate_line_string(obj.exterior_ring)] + obj.interior_rings.map { |r| generate_line_string(r) }).join(', ')}#{@end_bracket}"
179
+ lines = [generate_line_string(obj.exterior_ring, support_z, support_m)]
180
+ lines += obj.interior_rings.map { |r| generate_line_string(r, support_z, support_m) }
181
+ "#{@begin_bracket}#{lines.join(', ')}#{@end_bracket}"
175
182
  end
176
183
  end
177
184
 
178
- def generate_geometry_collection(obj)
185
+ def generate_geometry_collection(obj, support_z, support_m)
179
186
  if obj.empty?
180
187
  "EMPTY"
181
188
  else
182
- "#{@begin_bracket}#{obj.map { |f| generate_feature(f) }.join(', ')}#{@end_bracket}"
189
+ "#{@begin_bracket}#{obj.map { |f| generate_feature(f, support_z, support_m) }.join(', ')}#{@end_bracket}"
183
190
  end
184
191
  end
185
192
 
186
- def generate_multi_point(obj)
193
+ def generate_multi_point(obj, support_z, support_m)
187
194
  if obj.empty?
188
195
  "EMPTY"
189
196
  else
190
- "#{@begin_bracket}#{obj.map { |f| generate_point(f) }.join(', ')}#{@end_bracket}"
197
+ "#{@begin_bracket}#{obj.map { |f| generate_point(f, support_z, support_m) }.join(', ')}#{@end_bracket}"
191
198
  end
192
199
  end
193
200
 
194
- def generate_multi_line_string(obj)
201
+ def generate_multi_line_string(obj, support_z, support_m)
195
202
  if obj.empty?
196
203
  "EMPTY"
197
204
  else
198
- "#{@begin_bracket}#{obj.map { |f| generate_line_string(f) }.join(', ')}#{@end_bracket}"
205
+ "#{@begin_bracket}#{obj.map { |f| generate_line_string(f, support_z, support_m) }.join(', ')}#{@end_bracket}"
199
206
  end
200
207
  end
201
208
 
202
- def generate_multi_polygon(obj)
209
+ def generate_multi_polygon(obj, support_z, support_m)
203
210
  if obj.empty?
204
211
  "EMPTY"
205
212
  else
206
- "#{@begin_bracket}#{obj.map { |f| generate_polygon(f) }.join(', ')}#{@end_bracket}"
213
+ "#{@begin_bracket}#{obj.map { |f| generate_polygon(f, support_z, support_m) }.join(', ')}#{@end_bracket}"
207
214
  end
208
215
  end
209
216
  end