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.
Files changed (176) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +29 -0
  3. data/ext/geos_c_impl/coordinates.c +65 -0
  4. data/ext/geos_c_impl/coordinates.h +2 -0
  5. data/ext/geos_c_impl/extconf.rb +43 -0
  6. data/ext/geos_c_impl/factory.c +995 -0
  7. data/ext/geos_c_impl/factory.h +238 -0
  8. data/ext/geos_c_impl/geometry.c +1093 -0
  9. data/ext/geos_c_impl/geometry.h +23 -0
  10. data/ext/geos_c_impl/geometry_collection.c +757 -0
  11. data/ext/geos_c_impl/geometry_collection.h +46 -0
  12. data/ext/geos_c_impl/line_string.c +675 -0
  13. data/ext/geos_c_impl/line_string.h +32 -0
  14. data/ext/geos_c_impl/main.c +40 -0
  15. data/ext/geos_c_impl/point.c +236 -0
  16. data/ext/geos_c_impl/point.h +30 -0
  17. data/ext/geos_c_impl/polygon.c +359 -0
  18. data/ext/geos_c_impl/polygon.h +43 -0
  19. data/ext/geos_c_impl/preface.h +38 -0
  20. data/ext/proj4_c_impl/extconf.rb +62 -0
  21. data/ext/proj4_c_impl/main.c +315 -0
  22. data/lib/rgeo.rb +89 -0
  23. data/lib/rgeo/cartesian.rb +25 -0
  24. data/lib/rgeo/cartesian/analysis.rb +77 -0
  25. data/lib/rgeo/cartesian/bounding_box.rb +398 -0
  26. data/lib/rgeo/cartesian/calculations.rb +113 -0
  27. data/lib/rgeo/cartesian/factory.rb +347 -0
  28. data/lib/rgeo/cartesian/feature_classes.rb +100 -0
  29. data/lib/rgeo/cartesian/feature_methods.rb +88 -0
  30. data/lib/rgeo/cartesian/interface.rb +135 -0
  31. data/lib/rgeo/coord_sys.rb +43 -0
  32. data/lib/rgeo/coord_sys/cs/entities.rb +1315 -0
  33. data/lib/rgeo/coord_sys/cs/factories.rb +148 -0
  34. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +272 -0
  35. data/lib/rgeo/coord_sys/proj4.rb +293 -0
  36. data/lib/rgeo/coord_sys/srs_database/interface.rb +115 -0
  37. data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +140 -0
  38. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +62 -0
  39. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +63 -0
  40. data/lib/rgeo/error.rb +27 -0
  41. data/lib/rgeo/feature.rb +54 -0
  42. data/lib/rgeo/feature/curve.rb +111 -0
  43. data/lib/rgeo/feature/factory.rb +278 -0
  44. data/lib/rgeo/feature/factory_generator.rb +96 -0
  45. data/lib/rgeo/feature/geometry.rb +624 -0
  46. data/lib/rgeo/feature/geometry_collection.rb +95 -0
  47. data/lib/rgeo/feature/line.rb +26 -0
  48. data/lib/rgeo/feature/line_string.rb +60 -0
  49. data/lib/rgeo/feature/linear_ring.rb +26 -0
  50. data/lib/rgeo/feature/mixins.rb +143 -0
  51. data/lib/rgeo/feature/multi_curve.rb +71 -0
  52. data/lib/rgeo/feature/multi_line_string.rb +26 -0
  53. data/lib/rgeo/feature/multi_point.rb +33 -0
  54. data/lib/rgeo/feature/multi_polygon.rb +57 -0
  55. data/lib/rgeo/feature/multi_surface.rb +73 -0
  56. data/lib/rgeo/feature/point.rb +84 -0
  57. data/lib/rgeo/feature/polygon.rb +97 -0
  58. data/lib/rgeo/feature/surface.rb +79 -0
  59. data/lib/rgeo/feature/types.rb +284 -0
  60. data/lib/rgeo/geographic.rb +40 -0
  61. data/lib/rgeo/geographic/factory.rb +450 -0
  62. data/lib/rgeo/geographic/interface.rb +489 -0
  63. data/lib/rgeo/geographic/proj4_projector.rb +58 -0
  64. data/lib/rgeo/geographic/projected_feature_classes.rb +107 -0
  65. data/lib/rgeo/geographic/projected_feature_methods.rb +212 -0
  66. data/lib/rgeo/geographic/projected_window.rb +383 -0
  67. data/lib/rgeo/geographic/simple_mercator_projector.rb +110 -0
  68. data/lib/rgeo/geographic/spherical_feature_classes.rb +100 -0
  69. data/lib/rgeo/geographic/spherical_feature_methods.rb +134 -0
  70. data/lib/rgeo/geographic/spherical_math.rb +188 -0
  71. data/lib/rgeo/geos.rb +89 -0
  72. data/lib/rgeo/geos/capi_factory.rb +470 -0
  73. data/lib/rgeo/geos/capi_feature_classes.rb +129 -0
  74. data/lib/rgeo/geos/ffi_factory.rb +592 -0
  75. data/lib/rgeo/geos/ffi_feature_classes.rb +83 -0
  76. data/lib/rgeo/geos/ffi_feature_methods.rb +574 -0
  77. data/lib/rgeo/geos/interface.rb +202 -0
  78. data/lib/rgeo/geos/utils.rb +74 -0
  79. data/lib/rgeo/geos/zm_factory.rb +405 -0
  80. data/lib/rgeo/geos/zm_feature_classes.rb +80 -0
  81. data/lib/rgeo/geos/zm_feature_methods.rb +344 -0
  82. data/lib/rgeo/impl_helper.rb +19 -0
  83. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +185 -0
  84. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +61 -0
  85. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +146 -0
  86. data/lib/rgeo/impl_helper/basic_point_methods.rb +104 -0
  87. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +87 -0
  88. data/lib/rgeo/impl_helper/math.rb +14 -0
  89. data/lib/rgeo/impl_helper/utils.rb +29 -0
  90. data/lib/rgeo/version.rb +3 -0
  91. data/lib/rgeo/wkrep.rb +37 -0
  92. data/lib/rgeo/wkrep/wkb_generator.rb +201 -0
  93. data/lib/rgeo/wkrep/wkb_parser.rb +251 -0
  94. data/lib/rgeo/wkrep/wkt_generator.rb +207 -0
  95. data/lib/rgeo/wkrep/wkt_parser.rb +415 -0
  96. data/lib/rgeo/yaml.rb +23 -0
  97. data/test/cartesian_analysis_test.rb +65 -0
  98. data/test/cartesian_bbox_test.rb +123 -0
  99. data/test/common/factory_tests.rb +78 -0
  100. data/test/common/geometry_collection_tests.rb +237 -0
  101. data/test/common/line_string_tests.rb +330 -0
  102. data/test/common/multi_line_string_tests.rb +182 -0
  103. data/test/common/multi_point_tests.rb +200 -0
  104. data/test/common/multi_polygon_tests.rb +191 -0
  105. data/test/common/point_tests.rb +370 -0
  106. data/test/common/polygon_tests.rb +261 -0
  107. data/test/coord_sys/ogc_cs_test.rb +342 -0
  108. data/test/coord_sys/proj4_srs_data_test.rb +41 -0
  109. data/test/coord_sys/proj4_test.rb +150 -0
  110. data/test/coord_sys/sr_org_test.rb +32 -0
  111. data/test/coord_sys/url_reader_test.rb +42 -0
  112. data/test/geos_capi/factory_test.rb +31 -0
  113. data/test/geos_capi/geometry_collection_test.rb +24 -0
  114. data/test/geos_capi/line_string_test.rb +24 -0
  115. data/test/geos_capi/misc_test.rb +116 -0
  116. data/test/geos_capi/multi_line_string_test.rb +24 -0
  117. data/test/geos_capi/multi_point_test.rb +24 -0
  118. data/test/geos_capi/multi_polygon_test.rb +39 -0
  119. data/test/geos_capi/parsing_unparsing_test.rb +40 -0
  120. data/test/geos_capi/point_test.rb +72 -0
  121. data/test/geos_capi/polygon_test.rb +154 -0
  122. data/test/geos_capi/zmfactory_test.rb +57 -0
  123. data/test/geos_ffi/factory_test.rb +31 -0
  124. data/test/geos_ffi/geometry_collection_test.rb +24 -0
  125. data/test/geos_ffi/line_string_test.rb +24 -0
  126. data/test/geos_ffi/misc_test.rb +63 -0
  127. data/test/geos_ffi/multi_line_string_test.rb +24 -0
  128. data/test/geos_ffi/multi_point_test.rb +24 -0
  129. data/test/geos_ffi/multi_polygon_test.rb +33 -0
  130. data/test/geos_ffi/parsing_unparsing_test.rb +41 -0
  131. data/test/geos_ffi/point_test.rb +77 -0
  132. data/test/geos_ffi/polygon_test.rb +46 -0
  133. data/test/geos_ffi/zmfactory_test.rb +58 -0
  134. data/test/mixins_test.rb +141 -0
  135. data/test/oneoff_test.rb +26 -0
  136. data/test/projected_geographic/factory_test.rb +25 -0
  137. data/test/projected_geographic/geometry_collection_test.rb +24 -0
  138. data/test/projected_geographic/line_string_test.rb +24 -0
  139. data/test/projected_geographic/multi_line_string_test.rb +26 -0
  140. data/test/projected_geographic/multi_point_test.rb +30 -0
  141. data/test/projected_geographic/multi_polygon_test.rb +25 -0
  142. data/test/projected_geographic/point_test.rb +51 -0
  143. data/test/projected_geographic/polygon_test.rb +24 -0
  144. data/test/simple_cartesian/calculations_test.rb +99 -0
  145. data/test/simple_cartesian/factory_test.rb +27 -0
  146. data/test/simple_cartesian/geometry_collection_test.rb +30 -0
  147. data/test/simple_cartesian/line_string_test.rb +31 -0
  148. data/test/simple_cartesian/multi_line_string_test.rb +28 -0
  149. data/test/simple_cartesian/multi_point_test.rb +31 -0
  150. data/test/simple_cartesian/multi_polygon_test.rb +31 -0
  151. data/test/simple_cartesian/point_test.rb +50 -0
  152. data/test/simple_cartesian/polygon_test.rb +28 -0
  153. data/test/simple_mercator/factory_test.rb +25 -0
  154. data/test/simple_mercator/geometry_collection_test.rb +24 -0
  155. data/test/simple_mercator/line_string_test.rb +24 -0
  156. data/test/simple_mercator/multi_line_string_test.rb +26 -0
  157. data/test/simple_mercator/multi_point_test.rb +29 -0
  158. data/test/simple_mercator/multi_polygon_test.rb +25 -0
  159. data/test/simple_mercator/point_test.rb +55 -0
  160. data/test/simple_mercator/polygon_test.rb +24 -0
  161. data/test/simple_mercator/window_test.rb +173 -0
  162. data/test/spherical_geographic/calculations_test.rb +167 -0
  163. data/test/spherical_geographic/factory_test.rb +27 -0
  164. data/test/spherical_geographic/geometry_collection_test.rb +31 -0
  165. data/test/spherical_geographic/line_string_test.rb +31 -0
  166. data/test/spherical_geographic/multi_line_string_test.rb +29 -0
  167. data/test/spherical_geographic/multi_point_test.rb +31 -0
  168. data/test/spherical_geographic/multi_polygon_test.rb +31 -0
  169. data/test/spherical_geographic/point_test.rb +78 -0
  170. data/test/spherical_geographic/polygon_test.rb +28 -0
  171. data/test/types_test.rb +42 -0
  172. data/test/wkrep/wkb_generator_test.rb +185 -0
  173. data/test/wkrep/wkb_parser_test.rb +293 -0
  174. data/test/wkrep/wkt_generator_test.rb +294 -0
  175. data/test/wkrep/wkt_parser_test.rb +412 -0
  176. 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