rgeo 3.0.0.pre.rc.3 → 3.0.0
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.
- checksums.yaml +4 -4
- data/README.md +9 -0
- data/ext/geos_c_impl/factory.c +41 -5
- data/ext/geos_c_impl/factory.h +13 -2
- data/ext/geos_c_impl/geometry.c +151 -122
- data/ext/geos_c_impl/geometry_collection.c +17 -19
- data/ext/geos_c_impl/line_string.c +46 -36
- data/ext/geos_c_impl/point.c +0 -2
- data/ext/geos_c_impl/polygon.c +10 -11
- data/ext/geos_c_impl/polygon.h +1 -1
- data/ext/geos_c_impl/ruby_more.c +7 -0
- data/ext/geos_c_impl/ruby_more.h +8 -0
- data/lib/rgeo/cartesian/analysis.rb +5 -3
- data/lib/rgeo/cartesian/bounding_box.rb +74 -79
- data/lib/rgeo/cartesian/calculations.rb +20 -26
- data/lib/rgeo/cartesian/factory.rb +47 -49
- data/lib/rgeo/cartesian/planar_graph.rb +10 -16
- data/lib/rgeo/cartesian/sweepline_intersector.rb +1 -3
- data/lib/rgeo/cartesian/valid_op.rb +1 -3
- data/lib/rgeo/coord_sys/cs/entities.rb +87 -101
- data/lib/rgeo/coord_sys/cs/factories.rb +0 -2
- data/lib/rgeo/coord_sys/cs/wkt_parser.rb +70 -29
- data/lib/rgeo/feature/curve.rb +0 -1
- data/lib/rgeo/feature/factory.rb +25 -27
- data/lib/rgeo/feature/factory_generator.rb +3 -4
- data/lib/rgeo/feature/geometry.rb +41 -30
- data/lib/rgeo/feature/geometry_collection.rb +3 -4
- data/lib/rgeo/feature/line_string.rb +1 -2
- data/lib/rgeo/feature/linear_ring.rb +0 -1
- data/lib/rgeo/feature/multi_curve.rb +0 -1
- data/lib/rgeo/feature/multi_surface.rb +0 -1
- data/lib/rgeo/feature/point.rb +0 -1
- data/lib/rgeo/feature/polygon.rb +1 -2
- data/lib/rgeo/feature/surface.rb +0 -1
- data/lib/rgeo/feature/types.rb +73 -83
- data/lib/rgeo/geographic/factory.rb +87 -80
- data/lib/rgeo/geographic/interface.rb +40 -23
- data/lib/rgeo/geographic/projected_feature_methods.rb +2 -6
- data/lib/rgeo/geographic/projected_window.rb +35 -21
- data/lib/rgeo/geographic/simple_mercator_projector.rb +25 -13
- data/lib/rgeo/geographic/spherical_feature_methods.rb +8 -3
- data/lib/rgeo/geographic/spherical_math.rb +17 -20
- data/lib/rgeo/geos/capi_factory.rb +50 -50
- data/lib/rgeo/geos/ffi_factory.rb +41 -42
- data/lib/rgeo/geos/ffi_feature_methods.rb +72 -97
- data/lib/rgeo/geos/interface.rb +16 -16
- data/lib/rgeo/geos/utils.rb +3 -3
- data/lib/rgeo/geos/zm_factory.rb +50 -42
- data/lib/rgeo/geos/zm_feature_methods.rb +15 -8
- data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +4 -4
- data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -2
- data/lib/rgeo/impl_helper/basic_line_string_methods.rb +18 -24
- data/lib/rgeo/impl_helper/basic_point_methods.rb +1 -3
- data/lib/rgeo/impl_helper/basic_polygon_methods.rb +15 -16
- data/lib/rgeo/impl_helper/utils.rb +3 -9
- data/lib/rgeo/impl_helper/valid_op.rb +12 -16
- data/lib/rgeo/version.rb +1 -1
- data/lib/rgeo/wkrep/wkb_generator.rb +42 -47
- data/lib/rgeo/wkrep/wkb_parser.rb +17 -18
- data/lib/rgeo/wkrep/wkt_generator.rb +23 -16
- data/lib/rgeo/wkrep/wkt_parser.rb +23 -13
- metadata +5 -5
@@ -19,9 +19,7 @@ module RGeo
|
|
19
19
|
# LineStrings in general need to check that there's not one point
|
20
20
|
# GEOS doesn't allow instantiation of single point LineStrings so
|
21
21
|
# we should handle it.
|
22
|
-
if @points.size == 1
|
23
|
-
raise Error::InvalidGeometry, "LineString Cannot Have 1 Point"
|
24
|
-
end
|
22
|
+
raise Error::InvalidGeometry, "LineString Cannot Have 1 Point" if @points.size == 1
|
25
23
|
init_geometry
|
26
24
|
end
|
27
25
|
|
@@ -29,8 +27,8 @@ module RGeo
|
|
29
27
|
@points.size
|
30
28
|
end
|
31
29
|
|
32
|
-
def point_n(
|
33
|
-
|
30
|
+
def point_n(idx)
|
31
|
+
idx < 0 ? nil : @points[idx]
|
34
32
|
end
|
35
33
|
|
36
34
|
def points
|
@@ -64,10 +62,9 @@ module RGeo
|
|
64
62
|
end
|
65
63
|
|
66
64
|
def closed?
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@closed
|
65
|
+
return @closed if defined?(@closed)
|
66
|
+
|
67
|
+
@closed = @points.size > 2 && @points.first == @points.last
|
71
68
|
end
|
72
69
|
|
73
70
|
def ring?
|
@@ -111,15 +108,15 @@ module RGeo
|
|
111
108
|
def point_intersect_segment?(point, start_point, end_point)
|
112
109
|
return false unless point_collinear?(point, start_point, end_point)
|
113
110
|
|
114
|
-
if start_point.x
|
115
|
-
between_coordinate?(point.x, start_point.x, end_point.x)
|
116
|
-
else
|
111
|
+
if start_point.x == end_point.x
|
117
112
|
between_coordinate?(point.y, start_point.y, end_point.y)
|
113
|
+
else
|
114
|
+
between_coordinate?(point.x, start_point.x, end_point.x)
|
118
115
|
end
|
119
116
|
end
|
120
117
|
|
121
|
-
def point_collinear?(
|
122
|
-
(
|
118
|
+
def point_collinear?(pt1, pt2, pt3)
|
119
|
+
(pt2.x - pt1.x) * (pt3.y - pt1.y) == (pt3.x - pt1.x) * (pt2.y - pt1.y)
|
123
120
|
end
|
124
121
|
|
125
122
|
def between_coordinate?(coord, start_coord, end_coord)
|
@@ -137,9 +134,7 @@ module RGeo
|
|
137
134
|
def initialize(factory, start, stop)
|
138
135
|
self.factory = factory
|
139
136
|
cstart = Feature.cast(start, factory, Feature::Point)
|
140
|
-
unless cstart
|
141
|
-
raise Error::InvalidGeometry, "Could not cast start: #{start}"
|
142
|
-
end
|
137
|
+
raise Error::InvalidGeometry, "Could not cast start: #{start}" unless cstart
|
143
138
|
cstop = Feature.cast(stop, factory, Feature::Point)
|
144
139
|
raise Error::InvalidGeometry, "Could not cast end: #{stop}" unless cstop
|
145
140
|
@points = [cstart, cstop]
|
@@ -158,9 +153,7 @@ module RGeo
|
|
158
153
|
module BasicLinearRingMethods # :nodoc:
|
159
154
|
def initialize(factory, points)
|
160
155
|
super
|
161
|
-
|
162
|
-
raise Error::InvalidGeometry, "LinearRings must have 0 or >= 4 points"
|
163
|
-
end
|
156
|
+
raise Error::InvalidGeometry, "LinearRings must have 0 or >= 4 points" if @points.size.between?(1, 3)
|
164
157
|
end
|
165
158
|
|
166
159
|
def geometry_type
|
@@ -176,10 +169,11 @@ module RGeo
|
|
176
169
|
# Close ring if necessary.
|
177
170
|
def init_geometry
|
178
171
|
super
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
172
|
+
|
173
|
+
return if @points.empty?
|
174
|
+
|
175
|
+
@points << @points.first if @points.first != @points.last
|
176
|
+
@points = @points.chunk { |x| x }.map(&:first)
|
183
177
|
end
|
184
178
|
end
|
185
179
|
end
|
@@ -15,9 +15,7 @@ module RGeo
|
|
15
15
|
@y = y.to_f
|
16
16
|
@z = factory.property(:has_z_coordinate) ? extra.shift.to_f : nil
|
17
17
|
@m = factory.property(:has_m_coordinate) ? extra.shift.to_f : nil
|
18
|
-
|
19
|
-
raise ArgumentError, "Too many arguments for point initializer"
|
20
|
-
end
|
18
|
+
raise ArgumentError, "Too many arguments for point initializer" unless extra.empty?
|
21
19
|
init_geometry
|
22
20
|
end
|
23
21
|
|
@@ -12,14 +12,10 @@ module RGeo
|
|
12
12
|
def initialize(factory, exterior_ring, interior_rings)
|
13
13
|
self.factory = factory
|
14
14
|
@exterior_ring = Feature.cast(exterior_ring, factory, Feature::LinearRing)
|
15
|
-
unless @exterior_ring
|
16
|
-
raise Error::InvalidGeometry, "Failed to cast exterior ring #{exterior_ring}"
|
17
|
-
end
|
15
|
+
raise Error::InvalidGeometry, "Failed to cast exterior ring #{exterior_ring}" unless @exterior_ring
|
18
16
|
@interior_rings = (interior_rings || []).map do |elem|
|
19
17
|
elem = Feature.cast(elem, factory, Feature::LinearRing)
|
20
|
-
unless elem
|
21
|
-
raise Error::InvalidGeometry, "Could not cast interior ring #{elem}"
|
22
|
-
end
|
18
|
+
raise Error::InvalidGeometry, "Could not cast interior ring #{elem}" unless elem
|
23
19
|
elem
|
24
20
|
end
|
25
21
|
init_geometry
|
@@ -33,8 +29,8 @@ module RGeo
|
|
33
29
|
@interior_rings.size
|
34
30
|
end
|
35
31
|
|
36
|
-
def interior_ring_n(
|
37
|
-
|
32
|
+
def interior_ring_n(idx)
|
33
|
+
idx < 0 ? nil : @interior_rings[idx]
|
38
34
|
end
|
39
35
|
|
40
36
|
def interior_rings
|
@@ -61,11 +57,14 @@ module RGeo
|
|
61
57
|
end
|
62
58
|
|
63
59
|
def rep_equals?(rhs)
|
64
|
-
|
65
|
-
rhs.
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
proper_match = rhs.is_a?(self.class) &&
|
61
|
+
rhs.factory.eql?(@factory) &&
|
62
|
+
@exterior_ring.rep_equals?(rhs.exterior_ring) &&
|
63
|
+
@interior_rings.size == rhs.num_interior_rings
|
64
|
+
|
65
|
+
return false unless proper_match
|
66
|
+
|
67
|
+
rhs.interior_rings.each_with_index { |r, i| return false unless @interior_rings[i].rep_equals?(r) }
|
69
68
|
end
|
70
69
|
|
71
70
|
def hash
|
@@ -80,7 +79,8 @@ module RGeo
|
|
80
79
|
if Feature::Point === rhs
|
81
80
|
contains_point?(rhs)
|
82
81
|
else
|
83
|
-
raise(
|
82
|
+
raise(
|
83
|
+
Error::UnsupportedOperation,
|
84
84
|
"Method Polygon#contains? is only defined for Point"
|
85
85
|
)
|
86
86
|
end
|
@@ -90,7 +90,7 @@ module RGeo
|
|
90
90
|
|
91
91
|
def contains_point?(point)
|
92
92
|
ring_encloses_point?(@exterior_ring, point) &&
|
93
|
-
|
93
|
+
@interior_rings.none? do |exclusion|
|
94
94
|
ring_encloses_point?(exclusion, point, on_border_return: true)
|
95
95
|
end
|
96
96
|
end
|
@@ -114,7 +114,6 @@ module RGeo
|
|
114
114
|
encloses_point
|
115
115
|
end
|
116
116
|
|
117
|
-
|
118
117
|
def copy_state_from(obj)
|
119
118
|
super
|
120
119
|
@exterior_ring = obj.exterior_ring
|
@@ -18,20 +18,14 @@ module RGeo
|
|
18
18
|
# multiple times with different values and others pass the data
|
19
19
|
# to a CAPI or FFI.
|
20
20
|
def self.setup_coord_sys(srid, coord_sys, coord_sys_class)
|
21
|
-
unless coord_sys_class.is_a?(Class)
|
22
|
-
coord_sys_class = CoordSys::CONFIG.default_coord_sys_class
|
23
|
-
end
|
21
|
+
coord_sys_class = CoordSys::CONFIG.default_coord_sys_class unless coord_sys_class.is_a?(Class)
|
24
22
|
|
25
|
-
if coord_sys.is_a?(String)
|
26
|
-
coord_sys = coord_sys_class.create_from_wkt(coord_sys)
|
27
|
-
end
|
23
|
+
coord_sys = coord_sys_class.create_from_wkt(coord_sys) if coord_sys.is_a?(String)
|
28
24
|
|
29
25
|
srid ||= coord_sys.authority_code if coord_sys
|
30
26
|
srid = srid.to_i
|
31
27
|
# Create a coord sys based on the SRID if one was not given
|
32
|
-
if coord_sys.nil? && srid != 0
|
33
|
-
coord_sys = coord_sys_class.create(srid)
|
34
|
-
end
|
28
|
+
coord_sys = coord_sys_class.create(srid) if coord_sys.nil? && srid != 0
|
35
29
|
|
36
30
|
{ coord_sys: coord_sys, srid: srid }
|
37
31
|
end
|
@@ -181,12 +181,12 @@ module RGeo
|
|
181
181
|
|
182
182
|
# Checks that the given point has valid coordinates.
|
183
183
|
#
|
184
|
-
# @param
|
184
|
+
# @param point [RGeo::Feature::Point]
|
185
185
|
#
|
186
186
|
# @return [String] invalid_reason
|
187
|
-
def check_invalid_coordinate(
|
188
|
-
x =
|
189
|
-
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
|
323
|
+
# @param mpoly [RGeo::Feature::MultiPolygon]
|
326
324
|
#
|
327
325
|
# @return [String] invalid_reason
|
328
|
-
def check_consistent_area_mp(
|
329
|
-
|
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
|
335
|
+
# @param mpoly [RGeo::Feature::MultiPolygon]
|
340
336
|
#
|
341
337
|
# @return [String] invalid_reason
|
342
|
-
def check_shells_not_nested(
|
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
|
-
|
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
|
data/lib/rgeo/version.rb
CHANGED
@@ -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 = {
|
@@ -107,7 +106,7 @@ module RGeo
|
|
107
106
|
has_m = false
|
108
107
|
end
|
109
108
|
result = Result.new(has_z, has_m)
|
110
|
-
generate_feature(obj, result, true)
|
109
|
+
generate_feature(obj, result, toplevel: true)
|
111
110
|
result.emit(@hex_format)
|
112
111
|
end
|
113
112
|
|
@@ -126,7 +125,7 @@ module RGeo
|
|
126
125
|
|
127
126
|
def emit(hex_format)
|
128
127
|
str = @buffer.join
|
129
|
-
hex_format ? str.
|
128
|
+
hex_format ? str.unpack1("H*") : str
|
130
129
|
end
|
131
130
|
|
132
131
|
def z?
|
@@ -139,79 +138,75 @@ module RGeo
|
|
139
138
|
end
|
140
139
|
private_constant :Result
|
141
140
|
|
142
|
-
def emit_byte(value,
|
143
|
-
|
141
|
+
def emit_byte(value, rval)
|
142
|
+
rval << [value].pack("C")
|
144
143
|
end
|
145
144
|
|
146
|
-
def emit_integer(value,
|
147
|
-
|
145
|
+
def emit_integer(value, rval)
|
146
|
+
rval << [value].pack(@little_endian ? "V" : "N")
|
148
147
|
end
|
149
148
|
|
150
|
-
def emit_doubles(array,
|
151
|
-
|
149
|
+
def emit_doubles(array, rval)
|
150
|
+
rval << array.pack(@little_endian ? "E*" : "G*")
|
152
151
|
end
|
153
152
|
|
154
|
-
def emit_line_string_coords(obj,
|
153
|
+
def emit_line_string_coords(obj, rval)
|
155
154
|
array = []
|
156
|
-
obj.points.each { |pt| point_coords(pt,
|
157
|
-
emit_integer(obj.num_points,
|
158
|
-
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)
|
159
158
|
end
|
160
159
|
|
161
|
-
def point_coords(obj,
|
160
|
+
def point_coords(obj, rval, array = [])
|
162
161
|
array << obj.x
|
163
162
|
array << obj.y
|
164
|
-
array << obj.z if
|
165
|
-
array << obj.m if
|
163
|
+
array << obj.z if rval.z?
|
164
|
+
array << obj.m if rval.m?
|
166
165
|
array
|
167
166
|
end
|
168
167
|
|
169
|
-
def generate_feature(obj,
|
170
|
-
emit_byte(@little_endian ? 1 : 0,
|
168
|
+
def generate_feature(obj, rval, toplevel: false)
|
169
|
+
emit_byte(@little_endian ? 1 : 0, rval)
|
171
170
|
type = obj.geometry_type
|
172
171
|
type_code = TYPE_CODES[type]
|
173
|
-
unless type_code
|
174
|
-
raise Error::ParseError, "Unrecognized Geometry Type: #{type}"
|
175
|
-
end
|
172
|
+
raise Error::ParseError, "Unrecognized Geometry Type: #{type}" unless type_code
|
176
173
|
emit_srid = false
|
177
|
-
|
178
|
-
|
179
|
-
type_code |=
|
174
|
+
case @type_format
|
175
|
+
when :ewkb
|
176
|
+
type_code |= 0x80000000 if rval.z?
|
177
|
+
type_code |= 0x40000000 if rval.m?
|
180
178
|
if @emit_ewkb_srid && toplevel
|
181
179
|
type_code |= 0x20000000
|
182
180
|
emit_srid = true
|
183
181
|
end
|
184
|
-
|
185
|
-
type_code += 1000 if
|
186
|
-
type_code += 2000 if
|
182
|
+
when :wkb12
|
183
|
+
type_code += 1000 if rval.z?
|
184
|
+
type_code += 2000 if rval.m?
|
187
185
|
end
|
188
|
-
emit_integer(type_code,
|
189
|
-
emit_integer(obj.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)
|
190
194
|
if type == Feature::Point
|
191
|
-
emit_doubles(point_coords(obj,
|
195
|
+
emit_doubles(point_coords(obj, rval), rval)
|
192
196
|
elsif type.subtype_of?(Feature::LineString)
|
193
|
-
emit_line_string_coords(obj,
|
197
|
+
emit_line_string_coords(obj, rval)
|
194
198
|
elsif type == Feature::Polygon
|
195
199
|
exterior_ring = obj.exterior_ring
|
196
200
|
if exterior_ring.empty?
|
197
|
-
emit_integer(0,
|
201
|
+
emit_integer(0, rval)
|
198
202
|
else
|
199
|
-
emit_integer(1 + obj.num_interior_rings,
|
200
|
-
emit_line_string_coords(exterior_ring,
|
201
|
-
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) }
|
202
206
|
end
|
203
|
-
elsif
|
204
|
-
emit_integer(obj.num_geometries,
|
205
|
-
obj.each { |g| generate_feature(g,
|
206
|
-
elsif type == Feature::MultiPoint
|
207
|
-
emit_integer(obj.num_geometries, rv)
|
208
|
-
obj.each { |g| generate_feature(g, rv) }
|
209
|
-
elsif type == Feature::MultiLineString
|
210
|
-
emit_integer(obj.num_geometries, rv)
|
211
|
-
obj.each { |g| generate_feature(g, rv) }
|
212
|
-
elsif type == Feature::MultiPolygon
|
213
|
-
emit_integer(obj.num_geometries, rv)
|
214
|
-
obj.each { |g| generate_feature(g, rv) }
|
207
|
+
elsif type_is_collection
|
208
|
+
emit_integer(obj.num_geometries, rval)
|
209
|
+
obj.each { |g| generate_feature(g, rval) }
|
215
210
|
end
|
216
211
|
end
|
217
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.
|
@@ -118,9 +117,7 @@ module RGeo
|
|
118
117
|
obj = parse_object(false)
|
119
118
|
unless @ignore_extra_bytes
|
120
119
|
bytes = bytes_remaining
|
121
|
-
if bytes > 0
|
122
|
-
raise Error::ParseError, "Found #{bytes} extra bytes at the end of the stream."
|
123
|
-
end
|
120
|
+
raise Error::ParseError, "Found #{bytes} extra bytes at the end of the stream." if bytes > 0
|
124
121
|
end
|
125
122
|
ensure
|
126
123
|
@data = nil
|
@@ -133,7 +130,7 @@ module RGeo
|
|
133
130
|
private
|
134
131
|
|
135
132
|
def parse_object(contained)
|
136
|
-
endian_value =
|
133
|
+
endian_value = byte
|
137
134
|
case endian_value
|
138
135
|
when 0
|
139
136
|
little_endian = false
|
@@ -161,14 +158,20 @@ module RGeo
|
|
161
158
|
if contained != true && contained != type_code
|
162
159
|
raise Error::ParseError, "Enclosed type=#{type_code} is different from container constraint #{contained}"
|
163
160
|
end
|
161
|
+
|
164
162
|
if has_z != @cur_has_z
|
165
163
|
raise Error::ParseError, "Enclosed hasZ=#{has_z} is different from toplevel hasZ=#{@cur_has_z}"
|
166
164
|
end
|
165
|
+
|
167
166
|
if has_m != @cur_has_m
|
168
167
|
raise Error::ParseError, "Enclosed hasM=#{has_m} is different from toplevel hasM=#{@cur_has_m}"
|
169
168
|
end
|
169
|
+
|
170
170
|
if srid && srid != @cur_srid
|
171
|
-
raise
|
171
|
+
raise(
|
172
|
+
Error::ParseError,
|
173
|
+
"Enclosed SRID #{srid} is different from toplevel srid #{@cur_srid || '(unspecified)'}"
|
174
|
+
)
|
172
175
|
end
|
173
176
|
else
|
174
177
|
@cur_has_z = has_z
|
@@ -176,9 +179,11 @@ module RGeo
|
|
176
179
|
@cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
|
177
180
|
@cur_srid = srid
|
178
181
|
@cur_factory = @factory_generator.call(srid: @cur_srid, has_z_coordinate: has_z, has_m_coordinate: has_m)
|
182
|
+
|
179
183
|
if @cur_has_z && !@cur_factory.property(:has_z_coordinate)
|
180
184
|
raise Error::ParseError, "Data has Z coordinates but the factory doesn't have Z coordinates"
|
181
185
|
end
|
186
|
+
|
182
187
|
if @cur_has_m && !@cur_factory.property(:has_m_coordinate)
|
183
188
|
raise Error::ParseError, "Data has M coordinates but the factory doesn't have M coordinates"
|
184
189
|
end
|
@@ -222,29 +227,23 @@ module RGeo
|
|
222
227
|
@len - @pos
|
223
228
|
end
|
224
229
|
|
225
|
-
def
|
226
|
-
if @pos + 1 > @len
|
227
|
-
raise Error::ParseError, "Not enough bytes left to fulfill 1 byte"
|
228
|
-
end
|
230
|
+
def byte
|
231
|
+
raise Error::ParseError, "Not enough bytes left to fulfill 1 byte" if @pos + 1 > @len
|
229
232
|
str = @data[@pos, 1]
|
230
233
|
@pos += 1
|
231
|
-
str.
|
234
|
+
str.unpack1("C")
|
232
235
|
end
|
233
236
|
|
234
237
|
def get_integer(little_endian)
|
235
|
-
if @pos + 4 > @len
|
236
|
-
raise Error::ParseError, "Not enough bytes left to fulfill 1 integer"
|
237
|
-
end
|
238
|
+
raise Error::ParseError, "Not enough bytes left to fulfill 1 integer" if @pos + 4 > @len
|
238
239
|
str = @data[@pos, 4]
|
239
240
|
@pos += 4
|
240
|
-
str.
|
241
|
+
str.unpack1(little_endian ? "V" : "N")
|
241
242
|
end
|
242
243
|
|
243
244
|
def get_doubles(little_endian, count)
|
244
245
|
len = 8 * count
|
245
|
-
if @pos + len > @len
|
246
|
-
raise Error::ParseError, "Not enough bytes left to fulfill #{count} doubles"
|
247
|
-
end
|
246
|
+
raise Error::ParseError, "Not enough bytes left to fulfill #{count} doubles" if @pos + len > @len
|
248
247
|
str = @data[@pos, len]
|
249
248
|
@pos += len
|
250
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 =
|
56
|
-
|
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
|
@@ -99,10 +100,11 @@ module RGeo
|
|
99
100
|
support_z = factory.property(:has_z_coordinate)
|
100
101
|
support_m = factory.property(:has_m_coordinate)
|
101
102
|
end
|
102
|
-
str = generate_feature(obj, support_z, support_m, true)
|
103
|
-
|
103
|
+
str = generate_feature(obj, support_z, support_m, toplevel: true)
|
104
|
+
case @convert_case
|
105
|
+
when :upper
|
104
106
|
str.upcase
|
105
|
-
|
107
|
+
when :lower
|
106
108
|
str.downcase
|
107
109
|
else
|
108
110
|
str
|
@@ -111,20 +113,22 @@ module RGeo
|
|
111
113
|
|
112
114
|
private
|
113
115
|
|
114
|
-
def generate_feature(obj, support_z, support_m, toplevel
|
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
|
-
|
120
|
+
case @tag_format
|
121
|
+
when :ewkt
|
119
122
|
tag << "M" if support_m && !support_z
|
120
123
|
tag = "SRID=#{obj.srid};#{tag}" if toplevel && @emit_ewkt_srid
|
121
|
-
|
124
|
+
when :wkt12
|
122
125
|
if support_z
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
126
|
+
tag <<
|
127
|
+
if support_m
|
128
|
+
" ZM"
|
129
|
+
else
|
130
|
+
" Z"
|
131
|
+
end
|
128
132
|
elsif support_m
|
129
133
|
tag << " M"
|
130
134
|
end
|
@@ -163,7 +167,8 @@ module RGeo
|
|
163
167
|
if obj.empty?
|
164
168
|
"EMPTY"
|
165
169
|
else
|
166
|
-
|
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
|
|
@@ -171,7 +176,9 @@ module RGeo
|
|
171
176
|
if obj.empty?
|
172
177
|
"EMPTY"
|
173
178
|
else
|
174
|
-
|
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
|
|