rgeo-dschee 0.5.4
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 +7 -0
- data/LICENSE.txt +29 -0
- data/ext/geos_c_impl/coordinates.c +65 -0
- data/ext/geos_c_impl/coordinates.h +2 -0
- data/ext/geos_c_impl/extconf.rb +43 -0
- data/ext/geos_c_impl/factory.c +995 -0
- data/ext/geos_c_impl/factory.h +238 -0
- data/ext/geos_c_impl/geometry.c +1093 -0
- data/ext/geos_c_impl/geometry.h +23 -0
- data/ext/geos_c_impl/geometry_collection.c +757 -0
- data/ext/geos_c_impl/geometry_collection.h +46 -0
- data/ext/geos_c_impl/line_string.c +675 -0
- data/ext/geos_c_impl/line_string.h +32 -0
- data/ext/geos_c_impl/main.c +40 -0
- data/ext/geos_c_impl/point.c +236 -0
- data/ext/geos_c_impl/point.h +30 -0
- data/ext/geos_c_impl/polygon.c +359 -0
- data/ext/geos_c_impl/polygon.h +43 -0
- data/ext/geos_c_impl/preface.h +38 -0
- data/ext/proj4_c_impl/extconf.rb +62 -0
- data/ext/proj4_c_impl/main.c +315 -0
- data/lib/rgeo.rb +89 -0
- data/lib/rgeo/cartesian.rb +25 -0
- data/lib/rgeo/cartesian/analysis.rb +77 -0
- data/lib/rgeo/cartesian/bounding_box.rb +398 -0
- data/lib/rgeo/cartesian/calculations.rb +113 -0
- data/lib/rgeo/cartesian/factory.rb +347 -0
- data/lib/rgeo/cartesian/feature_classes.rb +100 -0
- data/lib/rgeo/cartesian/feature_methods.rb +88 -0
- data/lib/rgeo/cartesian/interface.rb +135 -0
- data/lib/rgeo/coord_sys.rb +43 -0
- data/lib/rgeo/coord_sys/cs/entities.rb +1315 -0
- data/lib/rgeo/coord_sys/cs/factories.rb +148 -0
- data/lib/rgeo/coord_sys/cs/wkt_parser.rb +272 -0
- data/lib/rgeo/coord_sys/proj4.rb +293 -0
- data/lib/rgeo/coord_sys/srs_database/interface.rb +115 -0
- data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +140 -0
- data/lib/rgeo/coord_sys/srs_database/sr_org.rb +62 -0
- data/lib/rgeo/coord_sys/srs_database/url_reader.rb +63 -0
- data/lib/rgeo/error.rb +27 -0
- data/lib/rgeo/feature.rb +54 -0
- data/lib/rgeo/feature/curve.rb +111 -0
- data/lib/rgeo/feature/factory.rb +278 -0
- data/lib/rgeo/feature/factory_generator.rb +96 -0
- data/lib/rgeo/feature/geometry.rb +624 -0
- data/lib/rgeo/feature/geometry_collection.rb +95 -0
- data/lib/rgeo/feature/line.rb +26 -0
- data/lib/rgeo/feature/line_string.rb +60 -0
- data/lib/rgeo/feature/linear_ring.rb +26 -0
- data/lib/rgeo/feature/mixins.rb +143 -0
- data/lib/rgeo/feature/multi_curve.rb +71 -0
- data/lib/rgeo/feature/multi_line_string.rb +26 -0
- data/lib/rgeo/feature/multi_point.rb +33 -0
- data/lib/rgeo/feature/multi_polygon.rb +57 -0
- data/lib/rgeo/feature/multi_surface.rb +73 -0
- data/lib/rgeo/feature/point.rb +84 -0
- data/lib/rgeo/feature/polygon.rb +97 -0
- data/lib/rgeo/feature/surface.rb +79 -0
- data/lib/rgeo/feature/types.rb +284 -0
- data/lib/rgeo/geographic.rb +40 -0
- data/lib/rgeo/geographic/factory.rb +450 -0
- data/lib/rgeo/geographic/interface.rb +489 -0
- data/lib/rgeo/geographic/proj4_projector.rb +58 -0
- data/lib/rgeo/geographic/projected_feature_classes.rb +107 -0
- data/lib/rgeo/geographic/projected_feature_methods.rb +212 -0
- data/lib/rgeo/geographic/projected_window.rb +383 -0
- data/lib/rgeo/geographic/simple_mercator_projector.rb +110 -0
- data/lib/rgeo/geographic/spherical_feature_classes.rb +100 -0
- data/lib/rgeo/geographic/spherical_feature_methods.rb +134 -0
- data/lib/rgeo/geographic/spherical_math.rb +188 -0
- data/lib/rgeo/geos.rb +89 -0
- data/lib/rgeo/geos/capi_factory.rb +470 -0
- data/lib/rgeo/geos/capi_feature_classes.rb +129 -0
- data/lib/rgeo/geos/ffi_factory.rb +592 -0
- data/lib/rgeo/geos/ffi_feature_classes.rb +83 -0
- data/lib/rgeo/geos/ffi_feature_methods.rb +574 -0
- data/lib/rgeo/geos/interface.rb +202 -0
- data/lib/rgeo/geos/utils.rb +74 -0
- data/lib/rgeo/geos/zm_factory.rb +405 -0
- data/lib/rgeo/geos/zm_feature_classes.rb +80 -0
- data/lib/rgeo/geos/zm_feature_methods.rb +344 -0
- data/lib/rgeo/impl_helper.rb +19 -0
- data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +185 -0
- data/lib/rgeo/impl_helper/basic_geometry_methods.rb +61 -0
- data/lib/rgeo/impl_helper/basic_line_string_methods.rb +146 -0
- data/lib/rgeo/impl_helper/basic_point_methods.rb +104 -0
- data/lib/rgeo/impl_helper/basic_polygon_methods.rb +87 -0
- data/lib/rgeo/impl_helper/math.rb +14 -0
- data/lib/rgeo/impl_helper/utils.rb +29 -0
- data/lib/rgeo/version.rb +3 -0
- data/lib/rgeo/wkrep.rb +37 -0
- data/lib/rgeo/wkrep/wkb_generator.rb +201 -0
- data/lib/rgeo/wkrep/wkb_parser.rb +251 -0
- data/lib/rgeo/wkrep/wkt_generator.rb +207 -0
- data/lib/rgeo/wkrep/wkt_parser.rb +415 -0
- data/lib/rgeo/yaml.rb +23 -0
- data/test/cartesian_analysis_test.rb +65 -0
- data/test/cartesian_bbox_test.rb +123 -0
- data/test/common/factory_tests.rb +78 -0
- data/test/common/geometry_collection_tests.rb +237 -0
- data/test/common/line_string_tests.rb +330 -0
- data/test/common/multi_line_string_tests.rb +182 -0
- data/test/common/multi_point_tests.rb +200 -0
- data/test/common/multi_polygon_tests.rb +191 -0
- data/test/common/point_tests.rb +370 -0
- data/test/common/polygon_tests.rb +261 -0
- data/test/coord_sys/ogc_cs_test.rb +342 -0
- data/test/coord_sys/proj4_srs_data_test.rb +41 -0
- data/test/coord_sys/proj4_test.rb +150 -0
- data/test/coord_sys/sr_org_test.rb +32 -0
- data/test/coord_sys/url_reader_test.rb +42 -0
- data/test/geos_capi/factory_test.rb +31 -0
- data/test/geos_capi/geometry_collection_test.rb +24 -0
- data/test/geos_capi/line_string_test.rb +24 -0
- data/test/geos_capi/misc_test.rb +116 -0
- data/test/geos_capi/multi_line_string_test.rb +24 -0
- data/test/geos_capi/multi_point_test.rb +24 -0
- data/test/geos_capi/multi_polygon_test.rb +39 -0
- data/test/geos_capi/parsing_unparsing_test.rb +40 -0
- data/test/geos_capi/point_test.rb +72 -0
- data/test/geos_capi/polygon_test.rb +154 -0
- data/test/geos_capi/zmfactory_test.rb +57 -0
- data/test/geos_ffi/factory_test.rb +31 -0
- data/test/geos_ffi/geometry_collection_test.rb +24 -0
- data/test/geos_ffi/line_string_test.rb +24 -0
- data/test/geos_ffi/misc_test.rb +63 -0
- data/test/geos_ffi/multi_line_string_test.rb +24 -0
- data/test/geos_ffi/multi_point_test.rb +24 -0
- data/test/geos_ffi/multi_polygon_test.rb +33 -0
- data/test/geos_ffi/parsing_unparsing_test.rb +41 -0
- data/test/geos_ffi/point_test.rb +77 -0
- data/test/geos_ffi/polygon_test.rb +46 -0
- data/test/geos_ffi/zmfactory_test.rb +58 -0
- data/test/mixins_test.rb +141 -0
- data/test/oneoff_test.rb +26 -0
- data/test/projected_geographic/factory_test.rb +25 -0
- data/test/projected_geographic/geometry_collection_test.rb +24 -0
- data/test/projected_geographic/line_string_test.rb +24 -0
- data/test/projected_geographic/multi_line_string_test.rb +26 -0
- data/test/projected_geographic/multi_point_test.rb +30 -0
- data/test/projected_geographic/multi_polygon_test.rb +25 -0
- data/test/projected_geographic/point_test.rb +51 -0
- data/test/projected_geographic/polygon_test.rb +24 -0
- data/test/simple_cartesian/calculations_test.rb +99 -0
- data/test/simple_cartesian/factory_test.rb +27 -0
- data/test/simple_cartesian/geometry_collection_test.rb +30 -0
- data/test/simple_cartesian/line_string_test.rb +31 -0
- data/test/simple_cartesian/multi_line_string_test.rb +28 -0
- data/test/simple_cartesian/multi_point_test.rb +31 -0
- data/test/simple_cartesian/multi_polygon_test.rb +31 -0
- data/test/simple_cartesian/point_test.rb +50 -0
- data/test/simple_cartesian/polygon_test.rb +28 -0
- data/test/simple_mercator/factory_test.rb +25 -0
- data/test/simple_mercator/geometry_collection_test.rb +24 -0
- data/test/simple_mercator/line_string_test.rb +24 -0
- data/test/simple_mercator/multi_line_string_test.rb +26 -0
- data/test/simple_mercator/multi_point_test.rb +29 -0
- data/test/simple_mercator/multi_polygon_test.rb +25 -0
- data/test/simple_mercator/point_test.rb +55 -0
- data/test/simple_mercator/polygon_test.rb +24 -0
- data/test/simple_mercator/window_test.rb +173 -0
- data/test/spherical_geographic/calculations_test.rb +167 -0
- data/test/spherical_geographic/factory_test.rb +27 -0
- data/test/spherical_geographic/geometry_collection_test.rb +31 -0
- data/test/spherical_geographic/line_string_test.rb +31 -0
- data/test/spherical_geographic/multi_line_string_test.rb +29 -0
- data/test/spherical_geographic/multi_point_test.rb +31 -0
- data/test/spherical_geographic/multi_polygon_test.rb +31 -0
- data/test/spherical_geographic/point_test.rb +78 -0
- data/test/spherical_geographic/polygon_test.rb +28 -0
- data/test/types_test.rb +42 -0
- data/test/wkrep/wkb_generator_test.rb +185 -0
- data/test/wkrep/wkb_parser_test.rb +293 -0
- data/test/wkrep/wkt_generator_test.rb +294 -0
- data/test/wkrep/wkt_parser_test.rb +412 -0
- metadata +386 -0
data/lib/rgeo/version.rb
ADDED
data/lib/rgeo/wkrep.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Well-known representation for RGeo
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
# This module contains implementations of the OpenGIS well-known
|
|
9
|
+
# representations: the WKT (well-known text representation) and the
|
|
10
|
+
# WKB (well-known binary representation), as defined in the Simple
|
|
11
|
+
# Features Specification, version 1.1. Facilities are provided to
|
|
12
|
+
# serialize any geometry into one of these formats, and to parse a
|
|
13
|
+
# serialized string back into a geometry. Support is also provided for
|
|
14
|
+
# the common extensions to these formats-- notably, the EWKT and EWKB
|
|
15
|
+
# formats used by PostGIS.
|
|
16
|
+
#
|
|
17
|
+
# To serialize a geometry into WKT (well-known text) format, use
|
|
18
|
+
# the WKRep::WKTGenerator class.
|
|
19
|
+
#
|
|
20
|
+
# To serialize a geometry into WKB (well-known binary) format, use
|
|
21
|
+
# the WKRep::WKBGenerator class.
|
|
22
|
+
#
|
|
23
|
+
# To parse a string in WKT (well-known text) format back into a
|
|
24
|
+
# geometry object, use the WKRep::WKTParser class.
|
|
25
|
+
#
|
|
26
|
+
# To parse a byte string in WKB (well-known binary) format back into a
|
|
27
|
+
# geometry object, use the WKRep::WKBParser class.
|
|
28
|
+
|
|
29
|
+
module WKRep
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Implementation files
|
|
34
|
+
require "rgeo/wkrep/wkt_parser"
|
|
35
|
+
require "rgeo/wkrep/wkt_generator"
|
|
36
|
+
require "rgeo/wkrep/wkb_parser"
|
|
37
|
+
require "rgeo/wkrep/wkb_generator"
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Well-known binary generator for RGeo
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
module WKRep
|
|
9
|
+
# This class provides the functionality of serializing a geometry as
|
|
10
|
+
# WKB (well-known binary) format. You may also customize the
|
|
11
|
+
# serializer to generate PostGIS EWKB extensions to the output, or to
|
|
12
|
+
# follow the Simple Features Specification 1.2 extensions for Z and M
|
|
13
|
+
# coordinates.
|
|
14
|
+
#
|
|
15
|
+
# To use this class, create an instance with the desired settings and
|
|
16
|
+
# customizations, and call the generate method.
|
|
17
|
+
#
|
|
18
|
+
# === Configuration options
|
|
19
|
+
#
|
|
20
|
+
# The following options are recognized. These can be passed to the
|
|
21
|
+
# constructor, or set on the object afterwards.
|
|
22
|
+
#
|
|
23
|
+
# [<tt>:type_format</tt>]
|
|
24
|
+
# The format for type codes. Possible values are <tt>:wkb11</tt>,
|
|
25
|
+
# indicating SFS 1.1 WKB (i.e. no Z or M values); <tt>:ewkb</tt>,
|
|
26
|
+
# indicating the PostGIS EWKB extensions (i.e. Z and M presence
|
|
27
|
+
# flagged by the two high bits of the type code, and support for
|
|
28
|
+
# embedded SRID); or <tt>:wkb12</tt> (indicating SFS 1.2 WKB
|
|
29
|
+
# (i.e. Z and M presence flagged by adding 1000 and/or 2000 to
|
|
30
|
+
# the type code.) Default is <tt>:wkb11</tt>.
|
|
31
|
+
# [<tt>:emit_ewkb_srid</tt>]
|
|
32
|
+
# If true, embed the SRID in the toplevel geometry. Available only
|
|
33
|
+
# if <tt>:type_format</tt> is <tt>:ewkb</tt>. Default is false.
|
|
34
|
+
# [<tt>:hex_format</tt>]
|
|
35
|
+
# If true, output a hex string instead of a byte string.
|
|
36
|
+
# Default is false.
|
|
37
|
+
# [<tt>:little_endian</tt>]
|
|
38
|
+
# If true, output little endian (NDR) byte order. If false, output
|
|
39
|
+
# big endian (XDR), or network byte order. Default is false.
|
|
40
|
+
|
|
41
|
+
class WKBGenerator
|
|
42
|
+
# :stopdoc:
|
|
43
|
+
TYPE_CODES = {
|
|
44
|
+
Feature::Point => 1,
|
|
45
|
+
Feature::LineString => 2,
|
|
46
|
+
Feature::LinearRing => 2,
|
|
47
|
+
Feature::Line => 2,
|
|
48
|
+
Feature::Polygon => 3,
|
|
49
|
+
Feature::MultiPoint => 4,
|
|
50
|
+
Feature::MultiLineString => 5,
|
|
51
|
+
Feature::MultiPolygon => 6,
|
|
52
|
+
Feature::GeometryCollection => 7
|
|
53
|
+
}.freeze
|
|
54
|
+
# :startdoc:
|
|
55
|
+
|
|
56
|
+
# Create and configure a WKB generator. See the WKBGenerator
|
|
57
|
+
# documentation for the options that can be passed.
|
|
58
|
+
|
|
59
|
+
def initialize(opts_ = {})
|
|
60
|
+
@type_format = opts_[:type_format] || :wkb11
|
|
61
|
+
@emit_ewkb_srid = @type_format == :ewkb ?
|
|
62
|
+
(opts_[:emit_ewkb_srid] ? true : false) : nil
|
|
63
|
+
@hex_format = opts_[:hex_format] ? true : false
|
|
64
|
+
@little_endian = opts_[:little_endian] ? true : false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns the format for type codes. See WKBGenerator for details.
|
|
68
|
+
attr_reader :type_format
|
|
69
|
+
|
|
70
|
+
# Returns whether SRID is embedded. See WKBGenerator for details.
|
|
71
|
+
def emit_ewkb_srid?
|
|
72
|
+
@emit_ewkb_srid
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns whether output is converted to hex.
|
|
76
|
+
# See WKBGenerator for details.
|
|
77
|
+
def hex_format?
|
|
78
|
+
@hex_format
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns whether output is little-endian (NDR).
|
|
82
|
+
# See WKBGenerator for details.
|
|
83
|
+
def little_endian?
|
|
84
|
+
@little_endian
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def _properties # :nodoc:
|
|
88
|
+
{
|
|
89
|
+
"type_format" => @type_format.to_s,
|
|
90
|
+
"emit_ewkb_srid" => @emit_ewkb_srid,
|
|
91
|
+
"hex_format" => @hex_format,
|
|
92
|
+
"little_endian" => @little_endian
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Generate and return the WKB format for the given geometry object,
|
|
97
|
+
# according to the current settings.
|
|
98
|
+
|
|
99
|
+
def generate(obj_)
|
|
100
|
+
factory_ = obj_.factory
|
|
101
|
+
if @type_format == :ewkb || @type_format == :wkb12
|
|
102
|
+
@cur_has_z = factory_.property(:has_z_coordinate)
|
|
103
|
+
@cur_has_m = factory_.property(:has_m_coordinate)
|
|
104
|
+
else
|
|
105
|
+
@cur_has_z = nil
|
|
106
|
+
@cur_has_m = nil
|
|
107
|
+
end
|
|
108
|
+
@cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
|
|
109
|
+
_start_emitter
|
|
110
|
+
_generate_feature(obj_, true)
|
|
111
|
+
_finish_emitter
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def _generate_feature(obj_, toplevel_ = false) # :nodoc:
|
|
115
|
+
_emit_byte(@little_endian ? 1 : 0)
|
|
116
|
+
type_ = obj_.geometry_type
|
|
117
|
+
type_code_ = TYPE_CODES[type_]
|
|
118
|
+
unless type_code_
|
|
119
|
+
raise Error::ParseError, "Unrecognized Geometry Type: #{type_}"
|
|
120
|
+
end
|
|
121
|
+
emit_srid_ = false
|
|
122
|
+
if @type_format == :ewkb
|
|
123
|
+
type_code_ |= 0x80000000 if @cur_has_z
|
|
124
|
+
type_code_ |= 0x40000000 if @cur_has_m
|
|
125
|
+
if @emit_ewkb_srid && toplevel_
|
|
126
|
+
type_code_ |= 0x20000000
|
|
127
|
+
emit_srid_ = true
|
|
128
|
+
end
|
|
129
|
+
elsif @type_format == :wkb12
|
|
130
|
+
type_code_ += 1000 if @cur_has_z
|
|
131
|
+
type_code_ += 2000 if @cur_has_m
|
|
132
|
+
end
|
|
133
|
+
_emit_integer(type_code_)
|
|
134
|
+
_emit_integer(obj_.srid) if emit_srid_
|
|
135
|
+
if type_ == Feature::Point
|
|
136
|
+
_emit_doubles(_point_coords(obj_))
|
|
137
|
+
elsif type_.subtype_of?(Feature::LineString)
|
|
138
|
+
_emit_line_string_coords(obj_)
|
|
139
|
+
elsif type_ == Feature::Polygon
|
|
140
|
+
exterior_ring_ = obj_.exterior_ring
|
|
141
|
+
if exterior_ring_.is_empty?
|
|
142
|
+
_emit_integer(0)
|
|
143
|
+
else
|
|
144
|
+
_emit_integer(1 + obj_.num_interior_rings)
|
|
145
|
+
_emit_line_string_coords(exterior_ring_)
|
|
146
|
+
obj_.interior_rings.each { |r_| _emit_line_string_coords(r_) }
|
|
147
|
+
end
|
|
148
|
+
elsif type_ == Feature::GeometryCollection
|
|
149
|
+
_emit_integer(obj_.num_geometries)
|
|
150
|
+
obj_.each { |g_| _generate_feature(g_) }
|
|
151
|
+
elsif type_ == Feature::MultiPoint
|
|
152
|
+
_emit_integer(obj_.num_geometries)
|
|
153
|
+
obj_.each { |g_| _generate_feature(g_) }
|
|
154
|
+
elsif type_ == Feature::MultiLineString
|
|
155
|
+
_emit_integer(obj_.num_geometries)
|
|
156
|
+
obj_.each { |g_| _generate_feature(g_) }
|
|
157
|
+
elsif type_ == Feature::MultiPolygon
|
|
158
|
+
_emit_integer(obj_.num_geometries)
|
|
159
|
+
obj_.each { |g_| _generate_feature(g_) }
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def _point_coords(obj_, array_ = []) # :nodoc:
|
|
164
|
+
array_ << obj_.x
|
|
165
|
+
array_ << obj_.y
|
|
166
|
+
array_ << obj_.z if @cur_has_z
|
|
167
|
+
array_ << obj_.m if @cur_has_m
|
|
168
|
+
array_
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def _emit_line_string_coords(obj_) # :nodoc:
|
|
172
|
+
array_ = []
|
|
173
|
+
obj_.points.each { |p_| _point_coords(p_, array_) }
|
|
174
|
+
_emit_integer(obj_.num_points)
|
|
175
|
+
_emit_doubles(array_)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def _start_emitter # :nodoc:
|
|
179
|
+
@cur_array = []
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def _emit_byte(value_) # :nodoc:
|
|
183
|
+
@cur_array << [value_].pack("C")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def _emit_integer(value_) # :nodoc:
|
|
187
|
+
@cur_array << [value_].pack(@little_endian ? "V" : "N")
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def _emit_doubles(array_) # :nodoc:
|
|
191
|
+
@cur_array << array_.pack(@little_endian ? "E*" : "G*")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def _finish_emitter # :nodoc:
|
|
195
|
+
str_ = @cur_array.join
|
|
196
|
+
@cur_array = nil
|
|
197
|
+
@hex_format ? str_.unpack("H*")[0] : str_
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Well-known binary parser for RGeo
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
module WKRep
|
|
9
|
+
# This class provides the functionality of parsing a geometry from
|
|
10
|
+
# WKB (well-known binary) format. You may also customize the parser
|
|
11
|
+
# to recognize PostGIS EWKB extensions to the input, or Simple
|
|
12
|
+
# Features Specification 1.2 extensions for Z and M coordinates.
|
|
13
|
+
#
|
|
14
|
+
# To use this class, create an instance with the desired settings and
|
|
15
|
+
# customizations, and call the parse method.
|
|
16
|
+
#
|
|
17
|
+
# === Configuration options
|
|
18
|
+
#
|
|
19
|
+
# You must provide each parser with an RGeo::Feature::FactoryGenerator.
|
|
20
|
+
# It should understand the configuration options <tt>:srid</tt>,
|
|
21
|
+
# <tt>:has_z_coordinate</tt>, and <tt>:has_m_coordinate</tt>.
|
|
22
|
+
# You may also pass a specific RGeo::Feature::Factory, or nil to
|
|
23
|
+
# specify the default Cartesian FactoryGenerator.
|
|
24
|
+
#
|
|
25
|
+
# The following additional options are recognized. These can be passed
|
|
26
|
+
# to the constructor, or set on the object afterwards.
|
|
27
|
+
#
|
|
28
|
+
# [<tt>:support_ewkb</tt>]
|
|
29
|
+
# Activate support for PostGIS EWKB type codes, which use high
|
|
30
|
+
# order bits in the type code to signal the presence of Z, M, and
|
|
31
|
+
# SRID values in the data. Default is false.
|
|
32
|
+
# [<tt>:support_wkb12</tt>]
|
|
33
|
+
# Activate support for SFS 1.2 extensions to the type codes, which
|
|
34
|
+
# use values greater than 1000 to signal the presence of Z and M
|
|
35
|
+
# values in the data. SFS 1.2 types such as triangle, tin, and
|
|
36
|
+
# polyhedralsurface are NOT yet supported. Default is false.
|
|
37
|
+
# [<tt>:ignore_extra_bytes</tt>]
|
|
38
|
+
# If true, extra bytes at the end of the data are ignored. If
|
|
39
|
+
# false (the default), extra bytes will trigger a parse error.
|
|
40
|
+
# [<tt>:default_srid</tt>]
|
|
41
|
+
# A SRID to pass to the factory generator if no SRID is present in
|
|
42
|
+
# the input. Defaults to nil (i.e. don't specify a SRID).
|
|
43
|
+
|
|
44
|
+
class WKBParser
|
|
45
|
+
# Create and configure a WKB parser. See the WKBParser
|
|
46
|
+
# documentation for the options that can be passed.
|
|
47
|
+
|
|
48
|
+
def initialize(factory_generator_ = nil, opts_ = {})
|
|
49
|
+
if factory_generator_.is_a?(Feature::Factory::Instance)
|
|
50
|
+
@factory_generator = Feature::FactoryGenerator.single(factory_generator_)
|
|
51
|
+
@exact_factory = factory_generator_
|
|
52
|
+
elsif factory_generator_.respond_to?(:call)
|
|
53
|
+
@factory_generator = factory_generator_
|
|
54
|
+
@exact_factory = nil
|
|
55
|
+
else
|
|
56
|
+
@factory_generator = Cartesian.method(:preferred_factory)
|
|
57
|
+
@exact_factory = nil
|
|
58
|
+
end
|
|
59
|
+
@support_ewkb = opts_[:support_ewkb] ? true : false
|
|
60
|
+
@support_wkb12 = opts_[:support_wkb12] ? true : false
|
|
61
|
+
@ignore_extra_bytes = opts_[:ignore_extra_bytes] ? true : false
|
|
62
|
+
@default_srid = opts_[:default_srid]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the factory generator. See WKBParser for details.
|
|
66
|
+
attr_reader :factory_generator
|
|
67
|
+
|
|
68
|
+
# If this parser was given an exact factory, returns it; otherwise
|
|
69
|
+
# returns nil.
|
|
70
|
+
attr_reader :exact_factory
|
|
71
|
+
|
|
72
|
+
# Returns true if this parser supports EWKB.
|
|
73
|
+
# See WKBParser for details.
|
|
74
|
+
def support_ewkb?
|
|
75
|
+
@support_ewkb
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns true if this parser supports SFS 1.2 extensions.
|
|
79
|
+
# See WKBParser for details.
|
|
80
|
+
def support_wkb12?
|
|
81
|
+
@support_wkb12
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns true if this parser ignores extra bytes.
|
|
85
|
+
# See WKBParser for details.
|
|
86
|
+
def ignore_extra_bytes?
|
|
87
|
+
@ignore_extra_bytes
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def _properties # :nodoc:
|
|
91
|
+
{
|
|
92
|
+
"support_ewkb" => @support_ewkb,
|
|
93
|
+
"support_wkb12" => @support_wkb12,
|
|
94
|
+
"ignore_extra_bytes" => @ignore_extra_bytes,
|
|
95
|
+
"default_srid" => @default_srid
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Parse the given binary data or hexadecimal string, and return a
|
|
100
|
+
# geometry object.
|
|
101
|
+
#
|
|
102
|
+
# The #parse_hex method is a synonym, present for historical
|
|
103
|
+
# reasons but deprecated. Use #parse instead.
|
|
104
|
+
|
|
105
|
+
def parse(data_)
|
|
106
|
+
data_ = [data_].pack("H*") if data_[0, 1] =~ /[0-9a-fA-F]/
|
|
107
|
+
@cur_has_z = nil
|
|
108
|
+
@cur_has_m = nil
|
|
109
|
+
@cur_srid = nil
|
|
110
|
+
@cur_dims = 2
|
|
111
|
+
@cur_factory = nil
|
|
112
|
+
begin
|
|
113
|
+
_start_scanner(data_)
|
|
114
|
+
obj_ = _parse_object(false)
|
|
115
|
+
unless @ignore_extra_bytes
|
|
116
|
+
bytes_ = _bytes_remaining
|
|
117
|
+
if bytes_ > 0
|
|
118
|
+
raise Error::ParseError, "Found #{bytes_} extra bytes at the end of the stream."
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
ensure
|
|
122
|
+
_clean_scanner
|
|
123
|
+
end
|
|
124
|
+
obj_
|
|
125
|
+
end
|
|
126
|
+
alias_method :parse_hex, :parse
|
|
127
|
+
|
|
128
|
+
def _parse_object(contained_) # :nodoc:
|
|
129
|
+
endian_value_ = _get_byte
|
|
130
|
+
case endian_value_
|
|
131
|
+
when 0
|
|
132
|
+
little_endian_ = false
|
|
133
|
+
when 1
|
|
134
|
+
little_endian_ = true
|
|
135
|
+
else
|
|
136
|
+
raise Error::ParseError, "Bad endian byte value: #{endian_value_}"
|
|
137
|
+
end
|
|
138
|
+
type_code_ = _get_integer(little_endian_)
|
|
139
|
+
has_z_ = false
|
|
140
|
+
has_m_ = false
|
|
141
|
+
srid_ = contained_ ? nil : @default_srid
|
|
142
|
+
if @support_ewkb
|
|
143
|
+
has_z_ ||= type_code_ & 0x80000000 != 0
|
|
144
|
+
has_m_ ||= type_code_ & 0x40000000 != 0
|
|
145
|
+
srid_ = _get_integer(little_endian_) if type_code_ & 0x20000000 != 0
|
|
146
|
+
type_code_ &= 0x0fffffff
|
|
147
|
+
end
|
|
148
|
+
if @support_wkb12
|
|
149
|
+
has_z_ ||= (type_code_ / 1000) & 1 != 0
|
|
150
|
+
has_m_ ||= (type_code_ / 1000) & 2 != 0
|
|
151
|
+
type_code_ %= 1000
|
|
152
|
+
end
|
|
153
|
+
if contained_
|
|
154
|
+
if contained_ != true && contained_ != type_code_
|
|
155
|
+
raise Error::ParseError, "Enclosed type=#{type_code_} is different from container constraint #{contained_}"
|
|
156
|
+
end
|
|
157
|
+
if has_z_ != @cur_has_z
|
|
158
|
+
raise Error::ParseError, "Enclosed hasZ=#{has_z_} is different from toplevel hasZ=#{@cur_has_z}"
|
|
159
|
+
end
|
|
160
|
+
if has_m_ != @cur_has_m
|
|
161
|
+
raise Error::ParseError, "Enclosed hasM=#{has_m_} is different from toplevel hasM=#{@cur_has_m}"
|
|
162
|
+
end
|
|
163
|
+
if srid_ && srid_ != @cur_srid
|
|
164
|
+
raise Error::ParseError, "Enclosed SRID #{srid_} is different from toplevel srid #{@cur_srid || '(unspecified)'}"
|
|
165
|
+
end
|
|
166
|
+
else
|
|
167
|
+
@cur_has_z = has_z_
|
|
168
|
+
@cur_has_m = has_m_
|
|
169
|
+
@cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
|
|
170
|
+
@cur_srid = srid_
|
|
171
|
+
@cur_factory = @factory_generator.call(srid: @cur_srid, has_z_coordinate: has_z_, has_m_coordinate: has_m_)
|
|
172
|
+
if @cur_has_z && !@cur_factory.property(:has_z_coordinate)
|
|
173
|
+
raise Error::ParseError, "Data has Z coordinates but the factory doesn't have Z coordinates"
|
|
174
|
+
end
|
|
175
|
+
if @cur_has_m && !@cur_factory.property(:has_m_coordinate)
|
|
176
|
+
raise Error::ParseError, "Data has M coordinates but the factory doesn't have M coordinates"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
case type_code_
|
|
180
|
+
when 1
|
|
181
|
+
coords_ = _get_doubles(little_endian_, @cur_dims)
|
|
182
|
+
@cur_factory.point(*coords_)
|
|
183
|
+
when 2
|
|
184
|
+
_parse_line_string(little_endian_)
|
|
185
|
+
when 3
|
|
186
|
+
interior_rings_ = (1.._get_integer(little_endian_)).map { _parse_line_string(little_endian_) }
|
|
187
|
+
exterior_ring_ = interior_rings_.shift || @cur_factory.linear_ring([])
|
|
188
|
+
@cur_factory.polygon(exterior_ring_, interior_rings_)
|
|
189
|
+
when 4
|
|
190
|
+
@cur_factory.multi_point((1.._get_integer(little_endian_)).map { _parse_object(1) })
|
|
191
|
+
when 5
|
|
192
|
+
@cur_factory.multi_line_string((1.._get_integer(little_endian_)).map { _parse_object(2) })
|
|
193
|
+
when 6
|
|
194
|
+
@cur_factory.multi_polygon((1.._get_integer(little_endian_)).map { _parse_object(3) })
|
|
195
|
+
when 7
|
|
196
|
+
@cur_factory.collection((1.._get_integer(little_endian_)).map { _parse_object(true) })
|
|
197
|
+
else
|
|
198
|
+
raise Error::ParseError, "Unknown type value: #{type_code_}."
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def _parse_line_string(little_endian_) # :nodoc:
|
|
203
|
+
count_ = _get_integer(little_endian_)
|
|
204
|
+
coords_ = _get_doubles(little_endian_, @cur_dims * count_)
|
|
205
|
+
@cur_factory.line_string((0...count_).map { |i_| @cur_factory.point(*coords_[@cur_dims * i_, @cur_dims]) })
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def _start_scanner(data_) # :nodoc:
|
|
209
|
+
@_data = data_
|
|
210
|
+
@_len = data_.length
|
|
211
|
+
@_pos = 0
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def _clean_scanner # :nodoc:
|
|
215
|
+
@_data = nil
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def _bytes_remaining # :nodoc:
|
|
219
|
+
@_len - @_pos
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def _get_byte # :nodoc:
|
|
223
|
+
if @_pos + 1 > @_len
|
|
224
|
+
raise Error::ParseError, "Not enough bytes left to fulfill 1 byte"
|
|
225
|
+
end
|
|
226
|
+
str_ = @_data[@_pos, 1]
|
|
227
|
+
@_pos += 1
|
|
228
|
+
str_.unpack("C").first
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def _get_integer(little_endian_) # :nodoc:
|
|
232
|
+
if @_pos + 4 > @_len
|
|
233
|
+
raise Error::ParseError, "Not enough bytes left to fulfill 1 integer"
|
|
234
|
+
end
|
|
235
|
+
str_ = @_data[@_pos, 4]
|
|
236
|
+
@_pos += 4
|
|
237
|
+
str_.unpack("#{little_endian_ ? 'V' : 'N'}").first
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def _get_doubles(little_endian_, count_) # :nodoc:
|
|
241
|
+
len_ = 8 * count_
|
|
242
|
+
if @_pos + len_ > @_len
|
|
243
|
+
raise Error::ParseError, "Not enough bytes left to fulfill #{count_} doubles"
|
|
244
|
+
end
|
|
245
|
+
str_ = @_data[@_pos, len_]
|
|
246
|
+
@_pos += len_
|
|
247
|
+
str_.unpack("#{little_endian_ ? 'E' : 'G'}*")
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|