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
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Well-known text generator for RGeo
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
module WKRep
|
|
9
|
+
# This class provides the functionality of serializing a geometry as
|
|
10
|
+
# WKT (well-known text) format. You may also customize the serializer
|
|
11
|
+
# to generate PostGIS EWKT extensions to the output, or to follow the
|
|
12
|
+
# Simple Features Specification 1.2 extensions for Z and M.
|
|
13
|
+
#
|
|
14
|
+
# To use this class, create an instance with the desired settings and
|
|
15
|
+
# customizations, and call the generate method.
|
|
16
|
+
#
|
|
17
|
+
# === Configuration options
|
|
18
|
+
#
|
|
19
|
+
# The following options are recognized. These can be passed to the
|
|
20
|
+
# constructor, or set on the object afterwards.
|
|
21
|
+
#
|
|
22
|
+
# [<tt>:tag_format</tt>]
|
|
23
|
+
# The format for tags. Possible values are <tt>:wkt11</tt>,
|
|
24
|
+
# indicating SFS 1.1 WKT (i.e. no Z or M markers in the tags) but
|
|
25
|
+
# with Z and/or M values added in if they are present;
|
|
26
|
+
# <tt>:wkt11_strict</tt>, indicating SFS 1.1 WKT with Z and M
|
|
27
|
+
# dropped from the output (since WKT strictly does not support
|
|
28
|
+
# the Z or M dimensions); <tt>:ewkt</tt>, indicating the PostGIS
|
|
29
|
+
# EWKT extensions (i.e. "M" appended to tag names if M but not
|
|
30
|
+
# Z is present); or <tt>:wkt12</tt>, indicating SFS 1.2 WKT
|
|
31
|
+
# tags that indicate the presence of Z and M in a separate token.
|
|
32
|
+
# Default is <tt>:wkt11</tt>.
|
|
33
|
+
# This option can also be specified as <tt>:type_format</tt>.
|
|
34
|
+
# [<tt>:emit_ewkt_srid</tt>]
|
|
35
|
+
# If true, embed the SRID of the toplevel geometry. Available only
|
|
36
|
+
# if <tt>:tag_format</tt> is <tt>:ewkt</tt>. Default is false.
|
|
37
|
+
# [<tt>:square_brackets</tt>]
|
|
38
|
+
# If true, uses square brackets rather than parentheses.
|
|
39
|
+
# Default is false.
|
|
40
|
+
# [<tt>:convert_case</tt>]
|
|
41
|
+
# Possible values are <tt>:upper</tt>, which changes all letters
|
|
42
|
+
# in the output to ALL CAPS; <tt>:lower</tt>, which changes all
|
|
43
|
+
# letters to lower case; or nil, indicating no case changes from
|
|
44
|
+
# the default (which is not specified exactly, but is chosen by the
|
|
45
|
+
# generator to emphasize readability.) Default is nil.
|
|
46
|
+
|
|
47
|
+
class WKTGenerator
|
|
48
|
+
# Create and configure a WKT generator. See the WKTGenerator
|
|
49
|
+
# documentation for the options that can be passed.
|
|
50
|
+
|
|
51
|
+
def initialize(opts_ = {})
|
|
52
|
+
@tag_format = opts_[:tag_format] || opts_[:type_format] || :wkt11
|
|
53
|
+
@emit_ewkt_srid = @tag_format == :ewkt ?
|
|
54
|
+
(opts_[:emit_ewkt_srid] ? true : false) : nil
|
|
55
|
+
@square_brackets = opts_[:square_brackets] ? true : false
|
|
56
|
+
@convert_case = opts_[:convert_case]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the format for type tags. See WKTGenerator for details.
|
|
60
|
+
attr_reader :tag_format
|
|
61
|
+
alias_method :type_format, :tag_format
|
|
62
|
+
|
|
63
|
+
# Returns whether SRID is embedded. See WKTGenerator for details.
|
|
64
|
+
def emit_ewkt_srid?
|
|
65
|
+
@emit_ewkt_srid
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns whether square brackets rather than parens are output.
|
|
69
|
+
# See WKTGenerator for details.
|
|
70
|
+
def square_brackets?
|
|
71
|
+
@square_brackets
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns the case for output. See WKTGenerator for details.
|
|
75
|
+
attr_reader :convert_case
|
|
76
|
+
|
|
77
|
+
def _properties # :nodoc:
|
|
78
|
+
{
|
|
79
|
+
"tag_format" => @tag_format.to_s,
|
|
80
|
+
"emit_ewkt_srid" => @emit_ewkt_srid,
|
|
81
|
+
"square_brackets" => @square_brackets,
|
|
82
|
+
"convert_case" => @convert_case ? @convert_case.to_s : nil
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Generate and return the WKT format for the given geometry object,
|
|
87
|
+
# according to the current settings.
|
|
88
|
+
|
|
89
|
+
def generate(obj_)
|
|
90
|
+
@begin_bracket = @square_brackets ? "[" : "("
|
|
91
|
+
@end_bracket = @square_brackets ? "]" : ")"
|
|
92
|
+
factory_ = obj_.factory
|
|
93
|
+
if @tag_format == :wkt11_strict
|
|
94
|
+
@cur_support_z = nil
|
|
95
|
+
@cur_support_m = nil
|
|
96
|
+
else
|
|
97
|
+
@cur_support_z = factory_.property(:has_z_coordinate)
|
|
98
|
+
@cur_support_m = factory_.property(:has_m_coordinate)
|
|
99
|
+
end
|
|
100
|
+
str_ = _generate_feature(obj_, true)
|
|
101
|
+
if @convert_case == :upper
|
|
102
|
+
str_.upcase
|
|
103
|
+
elsif @convert_case == :lower
|
|
104
|
+
str_.downcase
|
|
105
|
+
else
|
|
106
|
+
str_
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def _generate_feature(obj_, toplevel_ = false) # :nodoc:
|
|
111
|
+
type_ = obj_.geometry_type
|
|
112
|
+
type_ = Feature::LineString if type_.subtype_of?(Feature::LineString)
|
|
113
|
+
tag_ = type_.type_name
|
|
114
|
+
if @tag_format == :ewkt
|
|
115
|
+
tag_ << "M" if @cur_support_m && !@cur_support_z
|
|
116
|
+
tag_ = "SRID=#{obj_.srid};#{tag_}" if toplevel_ && @emit_ewkt_srid
|
|
117
|
+
elsif @tag_format == :wkt12
|
|
118
|
+
if @cur_support_z
|
|
119
|
+
if @cur_support_m
|
|
120
|
+
tag_ << " ZM"
|
|
121
|
+
else
|
|
122
|
+
tag_ << " Z"
|
|
123
|
+
end
|
|
124
|
+
elsif @cur_support_m
|
|
125
|
+
tag_ << " M"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
if type_ == Feature::Point
|
|
129
|
+
"#{tag_} #{_generate_point(obj_)}"
|
|
130
|
+
elsif type_ == Feature::LineString
|
|
131
|
+
"#{tag_} #{_generate_line_string(obj_)}"
|
|
132
|
+
elsif type_ == Feature::Polygon
|
|
133
|
+
"#{tag_} #{_generate_polygon(obj_)}"
|
|
134
|
+
elsif type_ == Feature::GeometryCollection
|
|
135
|
+
"#{tag_} #{_generate_geometry_collection(obj_)}"
|
|
136
|
+
elsif type_ == Feature::MultiPoint
|
|
137
|
+
"#{tag_} #{_generate_multi_point(obj_)}"
|
|
138
|
+
elsif type_ == Feature::MultiLineString
|
|
139
|
+
"#{tag_} #{_generate_multi_line_string(obj_)}"
|
|
140
|
+
elsif type_ == Feature::MultiPolygon
|
|
141
|
+
"#{tag_} #{_generate_multi_polygon(obj_)}"
|
|
142
|
+
else
|
|
143
|
+
raise Error::ParseError, "Unrecognized geometry type: #{type_}"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def _generate_coords(obj_) # :nodoc:
|
|
148
|
+
str_ = "#{obj_.x} #{obj_.y}"
|
|
149
|
+
str_ << " #{obj_.z}" if @cur_support_z
|
|
150
|
+
str_ << " #{obj_.m}" if @cur_support_m
|
|
151
|
+
str_
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def _generate_point(obj_) # :nodoc:
|
|
155
|
+
"#{@begin_bracket}#{_generate_coords(obj_)}#{@end_bracket}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def _generate_line_string(obj_) # :nodoc:
|
|
159
|
+
if obj_.is_empty?
|
|
160
|
+
"EMPTY"
|
|
161
|
+
else
|
|
162
|
+
"#{@begin_bracket}#{obj_.points.map { |p_| _generate_coords(p_) }.join(', ')}#{@end_bracket}"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def _generate_polygon(obj_) # :nodoc:
|
|
167
|
+
if obj_.is_empty?
|
|
168
|
+
"EMPTY"
|
|
169
|
+
else
|
|
170
|
+
"#{@begin_bracket}#{([_generate_line_string(obj_.exterior_ring)] + obj_.interior_rings.map { |r_| _generate_line_string(r_) }).join(', ')}#{@end_bracket}"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def _generate_geometry_collection(obj_) # :nodoc:
|
|
175
|
+
if obj_.is_empty?
|
|
176
|
+
"EMPTY"
|
|
177
|
+
else
|
|
178
|
+
"#{@begin_bracket}#{obj_.map { |f_| _generate_feature(f_) }.join(', ')}#{@end_bracket}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def _generate_multi_point(obj_) # :nodoc:
|
|
183
|
+
if obj_.is_empty?
|
|
184
|
+
"EMPTY"
|
|
185
|
+
else
|
|
186
|
+
"#{@begin_bracket}#{obj_.map { |f_| _generate_point(f_) }.join(', ')}#{@end_bracket}"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def _generate_multi_line_string(obj_) # :nodoc:
|
|
191
|
+
if obj_.is_empty?
|
|
192
|
+
"EMPTY"
|
|
193
|
+
else
|
|
194
|
+
"#{@begin_bracket}#{obj_.map { |f_| _generate_line_string(f_) }.join(', ')}#{@end_bracket}"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def _generate_multi_polygon(obj_) # :nodoc:
|
|
199
|
+
if obj_.is_empty?
|
|
200
|
+
"EMPTY"
|
|
201
|
+
else
|
|
202
|
+
"#{@begin_bracket}#{obj_.map { |f_| _generate_polygon(f_) }.join(', ')}#{@end_bracket}"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Well-known text parser for RGeo
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
require "strscan"
|
|
8
|
+
|
|
9
|
+
module RGeo
|
|
10
|
+
module WKRep
|
|
11
|
+
# This class provides the functionality of parsing a geometry from
|
|
12
|
+
# WKT (well-known text) format. You may also customize the parser
|
|
13
|
+
# to recognize PostGIS EWKT extensions to the input, or Simple
|
|
14
|
+
# Features Specification 1.2 extensions for Z and M coordinates.
|
|
15
|
+
#
|
|
16
|
+
# To use this class, create an instance with the desired settings and
|
|
17
|
+
# customizations, and call the parse method.
|
|
18
|
+
#
|
|
19
|
+
# === Configuration options
|
|
20
|
+
#
|
|
21
|
+
# You must provide each parser with an RGeo::Feature::FactoryGenerator.
|
|
22
|
+
# It should understand the configuration options <tt>:srid</tt>,
|
|
23
|
+
# <tt>:has_z_coordinate</tt>, and <tt>:has_m_coordinate</tt>.
|
|
24
|
+
# You may also pass a specific RGeo::Feature::Factory, or nil to
|
|
25
|
+
# specify the default Cartesian FactoryGenerator.
|
|
26
|
+
#
|
|
27
|
+
# The following additional options are recognized. These can be passed
|
|
28
|
+
# to the constructor, or set on the object afterwards.
|
|
29
|
+
#
|
|
30
|
+
# [<tt>:support_ewkt</tt>]
|
|
31
|
+
# Activate support for PostGIS EWKT type tags, which appends an "M"
|
|
32
|
+
# to tags to indicate the presence of M but not Z, and also
|
|
33
|
+
# recognizes the SRID prefix. Default is false.
|
|
34
|
+
# [<tt>:support_wkt12</tt>]
|
|
35
|
+
# Activate support for SFS 1.2 extensions to the type codes, which
|
|
36
|
+
# use a "M", "Z", or "ZM" token to signal the presence of Z and M
|
|
37
|
+
# values in the data. SFS 1.2 types such as triangle, tin, and
|
|
38
|
+
# polyhedralsurface are NOT yet supported. Default is false.
|
|
39
|
+
# [<tt>:strict_wkt11</tt>]
|
|
40
|
+
# If true, parsing will proceed in SFS 1.1 strict mode, which
|
|
41
|
+
# disallows any values other than X or Y. This has no effect if
|
|
42
|
+
# support_ewkt or support_wkt12 are active. Default is false.
|
|
43
|
+
# [<tt>:ignore_extra_tokens</tt>]
|
|
44
|
+
# If true, extra tokens at the end of the data are ignored. If
|
|
45
|
+
# false (the default), extra tokens will trigger a parse error.
|
|
46
|
+
# [<tt>:default_srid</tt>]
|
|
47
|
+
# A SRID to pass to the factory generator if no SRID is present in
|
|
48
|
+
# the input. Defaults to nil (i.e. don't specify a SRID).
|
|
49
|
+
|
|
50
|
+
class WKTParser
|
|
51
|
+
# Create and configure a WKT parser. See the WKTParser
|
|
52
|
+
# documentation for the options that can be passed.
|
|
53
|
+
|
|
54
|
+
def initialize(factory_generator_ = nil, opts_ = {})
|
|
55
|
+
if factory_generator_.is_a?(Feature::Factory::Instance)
|
|
56
|
+
@factory_generator = Feature::FactoryGenerator.single(factory_generator_)
|
|
57
|
+
@exact_factory = factory_generator_
|
|
58
|
+
elsif factory_generator_.respond_to?(:call)
|
|
59
|
+
@factory_generator = factory_generator_
|
|
60
|
+
@exact_factory = nil
|
|
61
|
+
else
|
|
62
|
+
@factory_generator = Cartesian.method(:preferred_factory)
|
|
63
|
+
@exact_factory = nil
|
|
64
|
+
end
|
|
65
|
+
@support_ewkt = opts_[:support_ewkt] ? true : false
|
|
66
|
+
@support_wkt12 = opts_[:support_wkt12] ? true : false
|
|
67
|
+
@strict_wkt11 = @support_ewkt || @support_wkt12 ? false : opts_[:strict_wkt11] ? true : false
|
|
68
|
+
@ignore_extra_tokens = opts_[:ignore_extra_tokens] ? true : false
|
|
69
|
+
@default_srid = opts_[:default_srid]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns the factory generator. See WKTParser for details.
|
|
73
|
+
attr_reader :factory_generator
|
|
74
|
+
|
|
75
|
+
# If this parser was given an exact factory, returns it; otherwise
|
|
76
|
+
# returns nil.
|
|
77
|
+
attr_reader :exact_factory
|
|
78
|
+
|
|
79
|
+
# Returns true if this parser supports EWKT.
|
|
80
|
+
# See WKTParser for details.
|
|
81
|
+
def support_ewkt?
|
|
82
|
+
@support_ewkt
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns true if this parser supports SFS 1.2 extensions.
|
|
86
|
+
# See WKTParser for details.
|
|
87
|
+
def support_wkt12?
|
|
88
|
+
@support_wkt12
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns true if this parser strictly adheres to WKT 1.1.
|
|
92
|
+
# See WKTParser for details.
|
|
93
|
+
def strict_wkt11?
|
|
94
|
+
@strict_wkt11
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Returns true if this parser ignores extra tokens.
|
|
98
|
+
# See WKTParser for details.
|
|
99
|
+
def ignore_extra_tokens?
|
|
100
|
+
@ignore_extra_tokens
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def _properties # :nodoc:
|
|
104
|
+
{
|
|
105
|
+
"support_ewkt" => @support_ewkt,
|
|
106
|
+
"support_wkt12" => @support_wkt12,
|
|
107
|
+
"strict_wkt11" => @strict_wkt11,
|
|
108
|
+
"ignore_extra_tokens" => @ignore_extra_tokens,
|
|
109
|
+
"default_srid" => @default_srid
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Parse the given string, and return a geometry object.
|
|
114
|
+
|
|
115
|
+
def parse(str_)
|
|
116
|
+
str_ = str_.downcase
|
|
117
|
+
@cur_factory = @exact_factory
|
|
118
|
+
if @cur_factory
|
|
119
|
+
@cur_factory_support_z = @cur_factory.property(:has_z_coordinate) ? true : false
|
|
120
|
+
@cur_factory_support_m = @cur_factory.property(:has_m_coordinate) ? true : false
|
|
121
|
+
end
|
|
122
|
+
@cur_expect_z = nil
|
|
123
|
+
@cur_expect_m = nil
|
|
124
|
+
@cur_srid = @default_srid
|
|
125
|
+
if @support_ewkt && str_ =~ /^srid=(\d+);/i
|
|
126
|
+
str_ = $'
|
|
127
|
+
@cur_srid = Regexp.last_match(1).to_i
|
|
128
|
+
end
|
|
129
|
+
begin
|
|
130
|
+
_start_scanner(str_)
|
|
131
|
+
obj_ = _parse_type_tag(false)
|
|
132
|
+
if @cur_token && !@ignore_extra_tokens
|
|
133
|
+
raise Error::ParseError, "Extra tokens beginning with #{@cur_token.inspect}."
|
|
134
|
+
end
|
|
135
|
+
ensure
|
|
136
|
+
_clean_scanner
|
|
137
|
+
end
|
|
138
|
+
obj_
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def _check_factory_support # :nodoc:
|
|
142
|
+
if @cur_expect_z && !@cur_factory_support_z
|
|
143
|
+
raise Error::ParseError, "Geometry calls for Z coordinate but factory doesn't support it."
|
|
144
|
+
end
|
|
145
|
+
if @cur_expect_m && !@cur_factory_support_m
|
|
146
|
+
raise Error::ParseError, "Geometry calls for M coordinate but factory doesn't support it."
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def _ensure_factory # :nodoc:
|
|
151
|
+
unless @cur_factory
|
|
152
|
+
@cur_factory = @factory_generator.call(srid: @cur_srid, has_z_coordinate: @cur_expect_z, has_m_coordinate: @cur_expect_m)
|
|
153
|
+
@cur_factory_support_z = @cur_factory.property(:has_z_coordinate) ? true : false
|
|
154
|
+
@cur_factory_support_m = @cur_factory.property(:has_m_coordinate) ? true : false
|
|
155
|
+
_check_factory_support unless @cur_expect_z.nil?
|
|
156
|
+
end
|
|
157
|
+
@cur_factory
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def _parse_type_tag(_contained_) # :nodoc:
|
|
161
|
+
_expect_token_type(::String)
|
|
162
|
+
if @support_ewkt && @cur_token =~ /^(.+)(m)$/
|
|
163
|
+
type_ = Regexp.last_match(1)
|
|
164
|
+
zm_ = Regexp.last_match(2)
|
|
165
|
+
else
|
|
166
|
+
type_ = @cur_token
|
|
167
|
+
zm_ = ""
|
|
168
|
+
end
|
|
169
|
+
_next_token
|
|
170
|
+
if zm_.length == 0 && @support_wkt12 && @cur_token.is_a?(::String) && @cur_token =~ /^z?m?$/
|
|
171
|
+
zm_ = @cur_token
|
|
172
|
+
_next_token
|
|
173
|
+
end
|
|
174
|
+
if zm_.length > 0 || @strict_wkt11
|
|
175
|
+
creating_expectation_ = @cur_expect_z.nil?
|
|
176
|
+
expect_z_ = zm_[0, 1] == "z" ? true : false
|
|
177
|
+
if @cur_expect_z.nil?
|
|
178
|
+
@cur_expect_z = expect_z_
|
|
179
|
+
elsif expect_z_ != @cur_expect_z
|
|
180
|
+
raise Error::ParseError, "Surrounding collection has Z but contained geometry doesn't."
|
|
181
|
+
end
|
|
182
|
+
expect_m_ = zm_[-1, 1] == "m" ? true : false
|
|
183
|
+
if @cur_expect_m.nil?
|
|
184
|
+
@cur_expect_m = expect_m_
|
|
185
|
+
elsif expect_m_ != @cur_expect_m
|
|
186
|
+
raise Error::ParseError, "Surrounding collection has M but contained geometry doesn't."
|
|
187
|
+
end
|
|
188
|
+
if creating_expectation_
|
|
189
|
+
if @cur_factory
|
|
190
|
+
_check_factory_support
|
|
191
|
+
else
|
|
192
|
+
_ensure_factory
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
case type_
|
|
197
|
+
when "point"
|
|
198
|
+
_parse_point(true)
|
|
199
|
+
when "linestring"
|
|
200
|
+
_parse_line_string
|
|
201
|
+
when "polygon"
|
|
202
|
+
_parse_polygon
|
|
203
|
+
when "geometrycollection"
|
|
204
|
+
_parse_geometry_collection
|
|
205
|
+
when "multipoint"
|
|
206
|
+
_parse_multi_point
|
|
207
|
+
when "multilinestring"
|
|
208
|
+
_parse_multi_line_string
|
|
209
|
+
when "multipolygon"
|
|
210
|
+
_parse_multi_polygon
|
|
211
|
+
else
|
|
212
|
+
raise Error::ParseError, "Unknown type tag: #{type_.inspect}."
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def _parse_coords # :nodoc:
|
|
217
|
+
_expect_token_type(::Numeric)
|
|
218
|
+
x_ = @cur_token
|
|
219
|
+
_next_token
|
|
220
|
+
_expect_token_type(::Numeric)
|
|
221
|
+
y_ = @cur_token
|
|
222
|
+
_next_token
|
|
223
|
+
extra_ = []
|
|
224
|
+
if @cur_expect_z.nil?
|
|
225
|
+
while ::Numeric === @cur_token
|
|
226
|
+
extra_ << @cur_token
|
|
227
|
+
_next_token
|
|
228
|
+
end
|
|
229
|
+
num_extras_ = extra_.size
|
|
230
|
+
@cur_expect_z = num_extras_ > 0 && (!@cur_factory || @cur_factory_support_z) ? true : false
|
|
231
|
+
num_extras_ -= 1 if @cur_expect_z
|
|
232
|
+
@cur_expect_m = num_extras_ > 0 && (!@cur_factory || @cur_factory_support_m) ? true : false
|
|
233
|
+
num_extras_ -= 1 if @cur_expect_m
|
|
234
|
+
if num_extras_ > 0
|
|
235
|
+
raise Error::ParseError, "Found #{extra_.size + 2} coordinates, which is too many for this factory."
|
|
236
|
+
end
|
|
237
|
+
_ensure_factory
|
|
238
|
+
else
|
|
239
|
+
val_ = 0
|
|
240
|
+
if @cur_expect_z
|
|
241
|
+
_expect_token_type(::Numeric)
|
|
242
|
+
val_ = @cur_token
|
|
243
|
+
_next_token
|
|
244
|
+
end
|
|
245
|
+
extra_ << val_ if @cur_factory_support_z
|
|
246
|
+
val_ = 0
|
|
247
|
+
if @cur_expect_m
|
|
248
|
+
_expect_token_type(::Numeric)
|
|
249
|
+
val_ = @cur_token
|
|
250
|
+
_next_token
|
|
251
|
+
end
|
|
252
|
+
extra_ << val_ if @cur_factory_support_m
|
|
253
|
+
end
|
|
254
|
+
@cur_factory.point(x_, y_, *extra_)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def _parse_point(convert_empty_ = false) # :nodoc:
|
|
258
|
+
if convert_empty_ && @cur_token == "empty"
|
|
259
|
+
point_ = _ensure_factory.multi_point([])
|
|
260
|
+
else
|
|
261
|
+
_expect_token_type(:begin)
|
|
262
|
+
_next_token
|
|
263
|
+
point_ = _parse_coords
|
|
264
|
+
_expect_token_type(:end)
|
|
265
|
+
end
|
|
266
|
+
_next_token
|
|
267
|
+
point_
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def _parse_line_string # :nodoc:
|
|
271
|
+
points_ = []
|
|
272
|
+
if @cur_token != "empty"
|
|
273
|
+
_expect_token_type(:begin)
|
|
274
|
+
_next_token
|
|
275
|
+
loop do
|
|
276
|
+
points_ << _parse_coords
|
|
277
|
+
break if @cur_token == :end
|
|
278
|
+
_expect_token_type(:comma)
|
|
279
|
+
_next_token
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
_next_token
|
|
283
|
+
_ensure_factory.line_string(points_)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def _parse_polygon # :nodoc:
|
|
287
|
+
inner_rings_ = []
|
|
288
|
+
if @cur_token == "empty"
|
|
289
|
+
outer_ring_ = _ensure_factory.linear_ring([])
|
|
290
|
+
else
|
|
291
|
+
_expect_token_type(:begin)
|
|
292
|
+
_next_token
|
|
293
|
+
outer_ring_ = _parse_line_string
|
|
294
|
+
loop do
|
|
295
|
+
break if @cur_token == :end
|
|
296
|
+
_expect_token_type(:comma)
|
|
297
|
+
_next_token
|
|
298
|
+
inner_rings_ << _parse_line_string
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
_next_token
|
|
302
|
+
_ensure_factory.polygon(outer_ring_, inner_rings_)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def _parse_geometry_collection # :nodoc:
|
|
306
|
+
geometries_ = []
|
|
307
|
+
if @cur_token != "empty"
|
|
308
|
+
_expect_token_type(:begin)
|
|
309
|
+
_next_token
|
|
310
|
+
loop do
|
|
311
|
+
geometries_ << _parse_type_tag(true)
|
|
312
|
+
break if @cur_token == :end
|
|
313
|
+
_expect_token_type(:comma)
|
|
314
|
+
_next_token
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
_next_token
|
|
318
|
+
_ensure_factory.collection(geometries_)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def _parse_multi_point # :nodoc:
|
|
322
|
+
points_ = []
|
|
323
|
+
if @cur_token != "empty"
|
|
324
|
+
_expect_token_type(:begin)
|
|
325
|
+
_next_token
|
|
326
|
+
loop do
|
|
327
|
+
uses_paren_ = @cur_token == :begin
|
|
328
|
+
_next_token if uses_paren_
|
|
329
|
+
points_ << _parse_coords
|
|
330
|
+
if uses_paren_
|
|
331
|
+
_expect_token_type(:end)
|
|
332
|
+
_next_token
|
|
333
|
+
end
|
|
334
|
+
break if @cur_token == :end
|
|
335
|
+
_expect_token_type(:comma)
|
|
336
|
+
_next_token
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
_next_token
|
|
340
|
+
_ensure_factory.multi_point(points_)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def _parse_multi_line_string # :nodoc:
|
|
344
|
+
line_strings_ = []
|
|
345
|
+
if @cur_token != "empty"
|
|
346
|
+
_expect_token_type(:begin)
|
|
347
|
+
_next_token
|
|
348
|
+
loop do
|
|
349
|
+
line_strings_ << _parse_line_string
|
|
350
|
+
break if @cur_token == :end
|
|
351
|
+
_expect_token_type(:comma)
|
|
352
|
+
_next_token
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
_next_token
|
|
356
|
+
_ensure_factory.multi_line_string(line_strings_)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def _parse_multi_polygon # :nodoc:
|
|
360
|
+
polygons_ = []
|
|
361
|
+
if @cur_token != "empty"
|
|
362
|
+
_expect_token_type(:begin)
|
|
363
|
+
_next_token
|
|
364
|
+
loop do
|
|
365
|
+
polygons_ << _parse_polygon
|
|
366
|
+
break if @cur_token == :end
|
|
367
|
+
_expect_token_type(:comma)
|
|
368
|
+
_next_token
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
_next_token
|
|
372
|
+
_ensure_factory.multi_polygon(polygons_)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def _start_scanner(str_) # :nodoc:
|
|
376
|
+
@_scanner = ::StringScanner.new(str_)
|
|
377
|
+
_next_token
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def _clean_scanner # :nodoc:
|
|
381
|
+
@_scanner = nil
|
|
382
|
+
@cur_token = nil
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def _expect_token_type(type_) # :nodoc:
|
|
386
|
+
unless type_ === @cur_token
|
|
387
|
+
raise Error::ParseError, "#{type_.inspect} expected but #{@cur_token.inspect} found."
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def _next_token # :nodoc:
|
|
392
|
+
if @_scanner.scan_until(/\(|\)|\[|\]|,|[^\s\(\)\[\],]+/)
|
|
393
|
+
token_ = @_scanner.matched
|
|
394
|
+
case token_
|
|
395
|
+
when /^[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?$/
|
|
396
|
+
@cur_token = token_.to_f
|
|
397
|
+
when /^[a-z]+$/
|
|
398
|
+
@cur_token = token_
|
|
399
|
+
when ","
|
|
400
|
+
@cur_token = :comma
|
|
401
|
+
when "(", "["
|
|
402
|
+
@cur_token = :begin
|
|
403
|
+
when "]", ")"
|
|
404
|
+
@cur_token = :end
|
|
405
|
+
else
|
|
406
|
+
raise Error::ParseError, "Bad token: #{token_.inspect}"
|
|
407
|
+
end
|
|
408
|
+
else
|
|
409
|
+
@cur_token = nil
|
|
410
|
+
end
|
|
411
|
+
@cur_token
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|