rgeo 3.0.0.pre.rc.3 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|