schleyfox-rgeo 0.2.5

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 (150) hide show
  1. data/History.rdoc +199 -0
  2. data/README.rdoc +172 -0
  3. data/Spatial_Programming_With_RGeo.rdoc +440 -0
  4. data/Version +1 -0
  5. data/ext/geos_c_impl/extconf.rb +84 -0
  6. data/ext/geos_c_impl/factory.c +468 -0
  7. data/ext/geos_c_impl/factory.h +224 -0
  8. data/ext/geos_c_impl/geometry.c +705 -0
  9. data/ext/geos_c_impl/geometry.h +55 -0
  10. data/ext/geos_c_impl/geometry_collection.c +482 -0
  11. data/ext/geos_c_impl/geometry_collection.h +69 -0
  12. data/ext/geos_c_impl/line_string.c +509 -0
  13. data/ext/geos_c_impl/line_string.h +64 -0
  14. data/ext/geos_c_impl/main.c +70 -0
  15. data/ext/geos_c_impl/point.c +193 -0
  16. data/ext/geos_c_impl/point.h +62 -0
  17. data/ext/geos_c_impl/polygon.c +265 -0
  18. data/ext/geos_c_impl/polygon.h +66 -0
  19. data/ext/geos_c_impl/preface.h +50 -0
  20. data/ext/proj4_c_impl/extconf.rb +88 -0
  21. data/ext/proj4_c_impl/main.c +271 -0
  22. data/lib/rgeo.rb +124 -0
  23. data/lib/rgeo/cartesian.rb +60 -0
  24. data/lib/rgeo/cartesian/analysis.rb +118 -0
  25. data/lib/rgeo/cartesian/bounding_box.rb +337 -0
  26. data/lib/rgeo/cartesian/calculations.rb +161 -0
  27. data/lib/rgeo/cartesian/factory.rb +209 -0
  28. data/lib/rgeo/cartesian/feature_classes.rb +173 -0
  29. data/lib/rgeo/cartesian/feature_methods.rb +106 -0
  30. data/lib/rgeo/cartesian/interface.rb +150 -0
  31. data/lib/rgeo/coord_sys.rb +79 -0
  32. data/lib/rgeo/coord_sys/cs/entities.rb +1524 -0
  33. data/lib/rgeo/coord_sys/cs/factories.rb +208 -0
  34. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +308 -0
  35. data/lib/rgeo/coord_sys/proj4.rb +312 -0
  36. data/lib/rgeo/coord_sys/srs_database/active_record_table.rb +194 -0
  37. data/lib/rgeo/coord_sys/srs_database/interface.rb +165 -0
  38. data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +188 -0
  39. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +108 -0
  40. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +108 -0
  41. data/lib/rgeo/error.rb +63 -0
  42. data/lib/rgeo/feature.rb +88 -0
  43. data/lib/rgeo/feature/curve.rb +156 -0
  44. data/lib/rgeo/feature/factory.rb +332 -0
  45. data/lib/rgeo/feature/factory_generator.rb +138 -0
  46. data/lib/rgeo/feature/geometry.rb +614 -0
  47. data/lib/rgeo/feature/geometry_collection.rb +129 -0
  48. data/lib/rgeo/feature/line.rb +66 -0
  49. data/lib/rgeo/feature/line_string.rb +102 -0
  50. data/lib/rgeo/feature/linear_ring.rb +66 -0
  51. data/lib/rgeo/feature/multi_curve.rb +113 -0
  52. data/lib/rgeo/feature/multi_line_string.rb +66 -0
  53. data/lib/rgeo/feature/multi_point.rb +73 -0
  54. data/lib/rgeo/feature/multi_polygon.rb +97 -0
  55. data/lib/rgeo/feature/multi_surface.rb +116 -0
  56. data/lib/rgeo/feature/point.rb +120 -0
  57. data/lib/rgeo/feature/polygon.rb +141 -0
  58. data/lib/rgeo/feature/surface.rb +122 -0
  59. data/lib/rgeo/feature/types.rb +305 -0
  60. data/lib/rgeo/geographic.rb +75 -0
  61. data/lib/rgeo/geographic/factory.rb +287 -0
  62. data/lib/rgeo/geographic/interface.rb +410 -0
  63. data/lib/rgeo/geographic/proj4_projector.rb +98 -0
  64. data/lib/rgeo/geographic/projected_feature_classes.rb +213 -0
  65. data/lib/rgeo/geographic/projected_feature_methods.rb +228 -0
  66. data/lib/rgeo/geographic/projected_window.rb +467 -0
  67. data/lib/rgeo/geographic/simple_mercator_projector.rb +157 -0
  68. data/lib/rgeo/geographic/spherical_feature_classes.rb +212 -0
  69. data/lib/rgeo/geographic/spherical_feature_methods.rb +97 -0
  70. data/lib/rgeo/geographic/spherical_math.rb +206 -0
  71. data/lib/rgeo/geos.rb +72 -0
  72. data/lib/rgeo/geos/factory.rb +301 -0
  73. data/lib/rgeo/geos/impl_additions.rb +76 -0
  74. data/lib/rgeo/geos/interface.rb +139 -0
  75. data/lib/rgeo/geos/zm_factory.rb +275 -0
  76. data/lib/rgeo/geos/zm_impl.rb +432 -0
  77. data/lib/rgeo/impl_helper.rb +53 -0
  78. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +235 -0
  79. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +85 -0
  80. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +197 -0
  81. data/lib/rgeo/impl_helper/basic_point_methods.rb +138 -0
  82. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +121 -0
  83. data/lib/rgeo/impl_helper/math.rb +50 -0
  84. data/lib/rgeo/version.rb +52 -0
  85. data/lib/rgeo/wkrep.rb +72 -0
  86. data/lib/rgeo/wkrep/wkb_generator.rb +267 -0
  87. data/lib/rgeo/wkrep/wkb_parser.rb +315 -0
  88. data/lib/rgeo/wkrep/wkt_generator.rb +275 -0
  89. data/lib/rgeo/wkrep/wkt_parser.rb +496 -0
  90. data/test/common/geometry_collection_tests.rb +238 -0
  91. data/test/common/line_string_tests.rb +324 -0
  92. data/test/common/multi_line_string_tests.rb +209 -0
  93. data/test/common/multi_point_tests.rb +201 -0
  94. data/test/common/multi_polygon_tests.rb +208 -0
  95. data/test/common/point_tests.rb +331 -0
  96. data/test/common/polygon_tests.rb +232 -0
  97. data/test/coord_sys/tc_active_record_table.rb +102 -0
  98. data/test/coord_sys/tc_ogc_cs.rb +356 -0
  99. data/test/coord_sys/tc_proj4.rb +138 -0
  100. data/test/coord_sys/tc_proj4_srs_data.rb +76 -0
  101. data/test/coord_sys/tc_sr_org.rb +70 -0
  102. data/test/coord_sys/tc_url_reader.rb +82 -0
  103. data/test/geos/tc_factory.rb +91 -0
  104. data/test/geos/tc_geometry_collection.rb +62 -0
  105. data/test/geos/tc_line_string.rb +62 -0
  106. data/test/geos/tc_misc.rb +72 -0
  107. data/test/geos/tc_multi_line_string.rb +62 -0
  108. data/test/geos/tc_multi_point.rb +62 -0
  109. data/test/geos/tc_multi_polygon.rb +63 -0
  110. data/test/geos/tc_point.rb +86 -0
  111. data/test/geos/tc_polygon.rb +86 -0
  112. data/test/geos/tc_zmfactory.rb +85 -0
  113. data/test/projected_geographic/tc_geometry_collection.rb +62 -0
  114. data/test/projected_geographic/tc_line_string.rb +62 -0
  115. data/test/projected_geographic/tc_multi_line_string.rb +62 -0
  116. data/test/projected_geographic/tc_multi_point.rb +62 -0
  117. data/test/projected_geographic/tc_multi_polygon.rb +63 -0
  118. data/test/projected_geographic/tc_point.rb +93 -0
  119. data/test/projected_geographic/tc_polygon.rb +62 -0
  120. data/test/simple_cartesian/tc_calculations.rb +145 -0
  121. data/test/simple_cartesian/tc_geometry_collection.rb +69 -0
  122. data/test/simple_cartesian/tc_line_string.rb +70 -0
  123. data/test/simple_cartesian/tc_multi_line_string.rb +67 -0
  124. data/test/simple_cartesian/tc_multi_point.rb +67 -0
  125. data/test/simple_cartesian/tc_multi_polygon.rb +70 -0
  126. data/test/simple_cartesian/tc_point.rb +91 -0
  127. data/test/simple_cartesian/tc_polygon.rb +67 -0
  128. data/test/simple_mercator/tc_geometry_collection.rb +62 -0
  129. data/test/simple_mercator/tc_line_string.rb +62 -0
  130. data/test/simple_mercator/tc_multi_line_string.rb +62 -0
  131. data/test/simple_mercator/tc_multi_point.rb +62 -0
  132. data/test/simple_mercator/tc_multi_polygon.rb +63 -0
  133. data/test/simple_mercator/tc_point.rb +93 -0
  134. data/test/simple_mercator/tc_polygon.rb +62 -0
  135. data/test/simple_mercator/tc_window.rb +219 -0
  136. data/test/spherical_geographic/tc_calculations.rb +203 -0
  137. data/test/spherical_geographic/tc_geometry_collection.rb +70 -0
  138. data/test/spherical_geographic/tc_line_string.rb +70 -0
  139. data/test/spherical_geographic/tc_multi_line_string.rb +67 -0
  140. data/test/spherical_geographic/tc_multi_point.rb +67 -0
  141. data/test/spherical_geographic/tc_multi_polygon.rb +70 -0
  142. data/test/spherical_geographic/tc_point.rb +100 -0
  143. data/test/spherical_geographic/tc_polygon.rb +67 -0
  144. data/test/tc_cartesian_analysis.rb +107 -0
  145. data/test/tc_oneoff.rb +63 -0
  146. data/test/wkrep/tc_wkb_generator.rb +249 -0
  147. data/test/wkrep/tc_wkb_parser.rb +353 -0
  148. data/test/wkrep/tc_wkt_generator.rb +362 -0
  149. data/test/wkrep/tc_wkt_parser.rb +480 -0
  150. metadata +267 -0
@@ -0,0 +1,315 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known binary parser for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module WKRep
40
+
41
+
42
+ # This class provides the functionality of parsing a geometry from
43
+ # WKB (well-known binary) format. You may also customize the parser
44
+ # to recognize PostGIS EWKB extensions to the input, or Simple
45
+ # Features Specification 1.2 extensions for Z and M coordinates.
46
+ #
47
+ # To use this class, create an instance with the desired settings and
48
+ # customizations, and call the parse method.
49
+ #
50
+ # === Configuration options
51
+ #
52
+ # You must provide each parser with an RGeo::Feature::FactoryGenerator.
53
+ # It should understand the configuration options <tt>:srid</tt>,
54
+ # <tt>:has_z_coordinate</tt>, and <tt>:has_m_coordinate</tt>.
55
+ # You may also pass a specific RGeo::Feature::Factory, or nil to
56
+ # specify the default Cartesian FactoryGenerator.
57
+ #
58
+ # The following additional options are recognized. These can be passed
59
+ # to the constructor, or set on the object afterwards.
60
+ #
61
+ # [<tt>:support_ewkb</tt>]
62
+ # Activate support for PostGIS EWKB type codes, which use high
63
+ # order bits in the type code to signal the presence of Z, M, and
64
+ # SRID values in the data. Default is false.
65
+ # [<tt>:support_wkb12</tt>]
66
+ # Activate support for SFS 1.2 extensions to the type codes, which
67
+ # use values greater than 1000 to signal the presence of Z and M
68
+ # values in the data. SFS 1.2 types such as triangle, tin, and
69
+ # polyhedralsurface are NOT yet supported. Default is false.
70
+ # [<tt>:ignore_extra_bytes</tt>]
71
+ # If true, extra bytes at the end of the data are ignored. If
72
+ # false (the default), extra bytes will trigger a parse error.
73
+ # [<tt>:default_srid</tt>]
74
+ # A SRID to pass to the factory generator if no SRID is present in
75
+ # the input. Defaults to nil (i.e. don't specify a SRID).
76
+
77
+ class WKBParser
78
+
79
+
80
+ # Create and configure a WKB parser. See the WKBParser
81
+ # documentation for the options that can be passed.
82
+
83
+ def initialize(factory_generator_=nil, opts_={})
84
+ self.factory_generator = factory_generator_
85
+ @support_ewkb = opts_[:support_ewkb] ? true : false
86
+ @support_wkb12 = opts_[:support_wkb12] ? true : false
87
+ @ignore_extra_bytes = opts_[:ignore_extra_bytes] ? true : false
88
+ @default_srid = opts_[:default_srid]
89
+ end
90
+
91
+
92
+ # Returns the factory generator. See WKBParser for details.
93
+ def factory_generator
94
+ @factory_generator
95
+ end
96
+
97
+ # If this parser was given an exact factory, returns it; otherwise
98
+ # returns nil.
99
+ def exact_factory
100
+ @exact_factory
101
+ end
102
+
103
+ # Sets the factory_generator. See WKBParser for details.
104
+ def factory_generator=(value_)
105
+ if value_.kind_of?(Feature::Factory::Instance)
106
+ @factory_generator = Feature::FactoryGenerator.single(value_)
107
+ @exact_factory = value_
108
+ elsif value_.respond_to?(:call)
109
+ @factory_generator = value_
110
+ @exact_factory = nil
111
+ else
112
+ @factory_generator = Cartesian.method(:preferred_factory)
113
+ @exact_factory = nil
114
+ end
115
+ end
116
+
117
+ # Sets the factory_generator to the given block.
118
+ # See WKBParser for details.
119
+ def to_generate_factory(&block_)
120
+ @factory_generator = block_
121
+ end
122
+
123
+ # Returns true if this parser supports EWKB.
124
+ # See WKBParser for details.
125
+ def support_ewkb?
126
+ @support_ewkb
127
+ end
128
+
129
+ # Sets the the support_ewkb flag. See WKBParser for details.
130
+ def support_ewkb=(value_)
131
+ @support_ewkb = value_ ? true : false
132
+ end
133
+
134
+ # Returns true if this parser supports SFS 1.2 extensions.
135
+ # See WKBParser for details.
136
+ def support_wkb12?
137
+ @support_wkb12
138
+ end
139
+
140
+ # Sets the the support_wkb12 flag. See WKBParser for details.
141
+ def support_wkb12=(value_)
142
+ @support_wkb12 = value_ ? true : false
143
+ end
144
+
145
+ # Returns true if this parser ignores extra bytes.
146
+ # See WKBParser for details.
147
+ def ignore_extra_bytes?
148
+ @ignore_extra_bytes
149
+ end
150
+
151
+ # Sets the the ignore_extra_bytes flag. See WKBParser for details.
152
+ def ignore_extra_bytes=(value_)
153
+ @ignore_extra_bytes = value_ ? true : false
154
+ end
155
+
156
+
157
+ # Parse the given hex string, and return a geometry object.
158
+
159
+ def parse_hex(str_)
160
+ parse([str_].pack('H*'))
161
+ end
162
+
163
+
164
+ # Parse the given binary data, and return a geometry object.
165
+
166
+ def parse(data_)
167
+ @cur_has_z = nil
168
+ @cur_has_m = nil
169
+ @cur_srid = nil
170
+ @cur_dims = 2
171
+ @cur_factory = nil
172
+ begin
173
+ _start_scanner(data_)
174
+ obj_ = _parse_object(false)
175
+ unless @ignore_extra_bytes
176
+ bytes_ = _bytes_remaining
177
+ if bytes_ > 0
178
+ raise Error::ParseError, "Found #{bytes_} extra bytes at the end of the stream."
179
+ end
180
+ end
181
+ ensure
182
+ _clean_scanner
183
+ end
184
+ obj_
185
+ end
186
+
187
+
188
+ def _parse_object(contained_) # :nodoc:
189
+ little_endian_ = _get_byte == 1
190
+ type_code_ = _get_integer(little_endian_)
191
+ has_z_ = false
192
+ has_m_ = false
193
+ srid_ = contained_ ? nil : @default_srid
194
+ if @support_ewkb
195
+ has_z_ ||= type_code_ & 0x80000000 != 0
196
+ has_m_ ||= type_code_ & 0x40000000 != 0
197
+ srid_ = _get_integer(little_endian_) if type_code_ & 0x20000000 != 0
198
+ type_code_ &= 0x0fffffff
199
+ end
200
+ if @support_wkb12
201
+ has_z_ ||= (type_code_ / 1000) & 1 != 0
202
+ has_m_ ||= (type_code_ / 1000) & 2 != 0
203
+ type_code_ %= 1000
204
+ end
205
+ if contained_
206
+ if contained_ != true && contained_ != type_code_
207
+ raise Error::ParseError, "Enclosed type=#{type_code_} is different from container constraint #{contained_}"
208
+ end
209
+ if has_z_ != @cur_has_z
210
+ raise Error::ParseError, "Enclosed hasZ=#{has_z_} is different from toplevel hasZ=#{@cur_has_z}"
211
+ end
212
+ if has_m_ != @cur_has_m
213
+ raise Error::ParseError, "Enclosed hasM=#{has_m_} is different from toplevel hasM=#{@cur_has_m}"
214
+ end
215
+ if srid_ && srid_ != @cur_srid
216
+ raise Error::ParseError, "Enclosed SRID #{srid_} is different from toplevel srid #{@cur_srid || '(unspecified)'}"
217
+ end
218
+ else
219
+ @cur_has_z = has_z_
220
+ @cur_has_m = has_m_
221
+ @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
222
+ @cur_srid = srid_
223
+ @cur_factory = @factory_generator.call(:srid => @cur_srid, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_)
224
+ if @cur_has_z && !@cur_factory.property(:has_z_coordinate)
225
+ raise Error::ParseError, "Data has Z coordinates but the factory doesn't have Z coordinates"
226
+ end
227
+ if @cur_has_m && !@cur_factory.property(:has_m_coordinate)
228
+ raise Error::ParseError, "Data has M coordinates but the factory doesn't have M coordinates"
229
+ end
230
+ end
231
+ case type_code_
232
+ when 1
233
+ coords_ = _get_doubles(little_endian_, @cur_dims)
234
+ @cur_factory.point(*coords_)
235
+ when 2
236
+ _parse_line_string(little_endian_)
237
+ when 3
238
+ interior_rings_ = (1.._get_integer(little_endian_)).map{ _parse_line_string(little_endian_) }
239
+ exterior_ring_ = interior_rings_.shift || @cur_factory.linear_ring([])
240
+ @cur_factory.polygon(exterior_ring_, interior_rings_)
241
+ when 4
242
+ @cur_factory.multi_point((1.._get_integer(little_endian_)).map{ _parse_object(1) })
243
+ when 5
244
+ @cur_factory.multi_line_string((1.._get_integer(little_endian_)).map{ _parse_object(2) })
245
+ when 6
246
+ @cur_factory.multi_polygon((1.._get_integer(little_endian_)).map{ _parse_object(3) })
247
+ when 7
248
+ @cur_factory.collection((1.._get_integer(little_endian_)).map{ _parse_object(true) })
249
+ else
250
+ raise Error::ParseError, "Unknown type value: #{type_code_}."
251
+ end
252
+ end
253
+
254
+
255
+ def _parse_line_string(little_endian_) # :nodoc:
256
+ count_ = _get_integer(little_endian_)
257
+ coords_ = _get_doubles(little_endian_, @cur_dims * count_)
258
+ @cur_factory.line_string((0...count_).map{ |i_| @cur_factory.point(*coords_[@cur_dims*i_,@cur_dims]) })
259
+ end
260
+
261
+
262
+ def _start_scanner(data_) # :nodoc:
263
+ @_data = data_
264
+ @_len = data_.length
265
+ @_pos = 0
266
+ end
267
+
268
+
269
+ def _clean_scanner # :nodoc:
270
+ @_data = nil
271
+ end
272
+
273
+
274
+ def _bytes_remaining # :nodoc:
275
+ @_len - @_pos
276
+ end
277
+
278
+
279
+ def _get_byte # :nodoc:
280
+ if @_pos + 1 > @_len
281
+ raise Error::ParseError, "Not enough bytes left to fulfill 1 byte"
282
+ end
283
+ str_ = @_data[@_pos, 1]
284
+ @_pos += 1
285
+ str_.unpack("C").first
286
+ end
287
+
288
+
289
+ def _get_integer(little_endian_) # :nodoc:
290
+ if @_pos + 4 > @_len
291
+ raise Error::ParseError, "Not enough bytes left to fulfill 1 integer"
292
+ end
293
+ str_ = @_data[@_pos, 4]
294
+ @_pos += 4
295
+ str_.unpack("#{little_endian_ ? 'V' : 'N'}").first
296
+ end
297
+
298
+
299
+ def _get_doubles(little_endian_, count_) # :nodoc:
300
+ len_ = 8 * count_
301
+ if @_pos + len_ > @_len
302
+ raise Error::ParseError, "Not enough bytes left to fulfill #{count_} doubles"
303
+ end
304
+ str_ = @_data[@_pos, len_]
305
+ @_pos += len_
306
+ str_.unpack("#{little_endian_ ? 'E' : 'G'}*")
307
+ end
308
+
309
+
310
+ end
311
+
312
+
313
+ end
314
+
315
+ end
@@ -0,0 +1,275 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known text generator for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module WKRep
40
+
41
+
42
+ # This class provides the functionality of serializing a geometry as
43
+ # WKT (well-known text) format. You may also customize the serializer
44
+ # to generate PostGIS EWKT extensions to the output, or to follow the
45
+ # Simple Features Specification 1.2 extensions for Z and M.
46
+ #
47
+ # To use this class, create an instance with the desired settings and
48
+ # customizations, and call the generate method.
49
+ #
50
+ # === Configuration options
51
+ #
52
+ # The following options are recognized. These can be passed to the
53
+ # constructor, or set on the object afterwards.
54
+ #
55
+ # [<tt>:tag_format</tt>]
56
+ # The format for tags. Possible values are <tt>:wkt11</tt>,
57
+ # indicating SFS 1.1 WKT (i.e. no Z or M markers in the tags) but
58
+ # with Z and/or M values added in if they are present;
59
+ # <tt>:wkt11_strict</tt>, indicating SFS 1.1 WKT with Z and M
60
+ # dropped from the output (since WKT strictly does not support
61
+ # the Z or M dimensions); <tt>:ewkt</tt>, indicating the PostGIS
62
+ # EWKT extensions (i.e. "M" appended to tag names if M but not
63
+ # Z is present); or <tt>:wkt12</tt>, indicating SFS 1.2 WKT
64
+ # tags that indicate the presence of Z and M in a separate token.
65
+ # Default is <tt>:wkt11</tt>.
66
+ # [<tt>:emit_ewkt_srid</tt>]
67
+ # If true, embed the SRID of the toplevel geometry. Available only
68
+ # if <tt>:type_format</tt> is <tt>:ewkt</tt>. Default is false.
69
+ # [<tt>:square_brackets</tt>]
70
+ # If true, uses square brackets rather than parentheses.
71
+ # Default is false.
72
+ # [<tt>:convert_case</tt>]
73
+ # Possible values are <tt>:upper</tt>, which changes all letters
74
+ # in the output to ALL CAPS; <tt>:lower</tt>, which changes all
75
+ # letters to lower case; or nil, indicating no case changes from
76
+ # the default (which is not specified exactly, but is chosen by the
77
+ # generator to emphasize readability.) Default is nil.
78
+
79
+ class WKTGenerator
80
+
81
+
82
+ # Create and configure a WKT generator. See the WKTGenerator
83
+ # documentation for the options that can be passed.
84
+
85
+ def initialize(opts_={})
86
+ @tag_format = opts_[:tag_format] || :wkt11
87
+ @emit_ewkt_srid = opts_[:emit_ewkt_srid] ? true : false if @tag_format == :ewkt
88
+ @square_brackets = opts_[:square_brackets] ? true : false
89
+ @convert_case = opts_[:convert_case]
90
+ end
91
+
92
+
93
+ # Returns the format for type tags. See WKTGenerator for details.
94
+ def tag_format
95
+ @tag_format
96
+ end
97
+
98
+ # Sets the format for type tags. See WKTGenerator for details.
99
+ def tag_format=(value_)
100
+ @tag_format = value_
101
+ end
102
+
103
+ # Returns whether SRID is embedded. See WKTGenerator for details.
104
+ def emit_ewkt_srid?
105
+ @emit_ewkt_srid
106
+ end
107
+
108
+ # Sets whether SRID is embedded. Available only when the tag_format
109
+ # is <tt>:ewkt</tt>. See WKTGenerator for details.
110
+ def emit_ewkt_srid=(value_)
111
+ @emit_ewkt_srid = @type_format == :ewkt && value_
112
+ end
113
+
114
+ # Returns whether square brackets rather than parens are output.
115
+ # See WKTGenerator for details.
116
+ def square_brackets?
117
+ @square_brackets
118
+ end
119
+
120
+ # Sets whether square brackets rather than parens are output.
121
+ # See WKTGenerator for details.
122
+ def square_brackets=(value_)
123
+ @square_brackets = value_ ? true : false
124
+ end
125
+
126
+ # Returns the case for output. See WKTGenerator for details.
127
+ def convert_case
128
+ @convert_case
129
+ end
130
+
131
+ # Sets the case for output. See WKTGenerator for details.
132
+ def convert_case=(value_)
133
+ @convert_case = value_
134
+ end
135
+
136
+
137
+ # Generate and return the WKT format for the given geometry object,
138
+ # according to the current settings.
139
+
140
+ def generate(obj_)
141
+ @begin_bracket = @square_brackets ? '[' : '('
142
+ @end_bracket = @square_brackets ? ']' : ')'
143
+ factory_ = obj_.factory
144
+ if @tag_format == :wkt11_strict
145
+ @cur_support_z = nil
146
+ @cur_support_m = nil
147
+ else
148
+ @cur_support_z = factory_.property(:has_z_coordinate)
149
+ @cur_support_m = factory_.property(:has_m_coordinate)
150
+ end
151
+ str_ = _generate_feature(obj_, true)
152
+ if @convert_case == :upper
153
+ str_.upcase
154
+ elsif @convert_case == :lower
155
+ str_.downcase
156
+ else
157
+ str_
158
+ end
159
+ end
160
+
161
+
162
+ def _generate_feature(obj_, toplevel_=false) # :nodoc:
163
+ type_ = obj_.geometry_type
164
+ tag_ = type_.type_name
165
+ if @tag_format == :ewkt
166
+ if @cur_support_m && !@cur_support_z
167
+ tag_ << 'M'
168
+ end
169
+ if toplevel_ && @emit_ewkt_srid
170
+ tag_ = "SRID=#{obj_.srid};#{tag_}"
171
+ end
172
+ elsif @tag_format == :wkt12
173
+ if @cur_support_z
174
+ if @cur_support_m
175
+ tag_ << ' ZM'
176
+ else
177
+ tag_ << ' Z'
178
+ end
179
+ elsif @cur_support_m
180
+ tag_ << ' M'
181
+ end
182
+ end
183
+ if type_ == Feature::Point
184
+ tag_ + _generate_point(obj_)
185
+ elsif type_.subtype_of?(Feature::LineString)
186
+ tag_ + _generate_line_string(obj_)
187
+ elsif type_ == Feature::Polygon
188
+ tag_ + _generate_polygon(obj_)
189
+ elsif type_ == Feature::GeometryCollection
190
+ tag_ + _generate_geometry_collection(obj_)
191
+ elsif type_ == Feature::MultiPoint
192
+ tag_ + _generate_multi_point(obj_)
193
+ elsif type_ == Feature::MultiLineString
194
+ tag_ + _generate_multi_line_string(obj_)
195
+ elsif type_ == Feature::MultiPolygon
196
+ tag_ + _generate_multi_polygon(obj_)
197
+ else
198
+ raise Error::ParseError, "Unrecognized geometry type: #{type_}"
199
+ end
200
+ end
201
+
202
+
203
+ def _generate_coords(obj_) # :nodoc:
204
+ str_ = "#{obj_.x.to_s} #{obj_.y.to_s}"
205
+ str_ << " #{obj_.z.to_s}" if @cur_support_z
206
+ str_ << " #{obj_.m.to_s}" if @cur_support_m
207
+ str_
208
+ end
209
+
210
+
211
+ def _generate_point(obj_) # :nodoc:
212
+ "#{@begin_bracket}#{_generate_coords(obj_)}#{@end_bracket}"
213
+ end
214
+
215
+
216
+ def _generate_line_string(obj_, contained_=false) # :nodoc:
217
+ if obj_.is_empty?
218
+ contained_ ? 'EMPTY' : ' EMPTY'
219
+ else
220
+ "#{@begin_bracket}#{obj_.points.map{ |p_| _generate_coords(p_) }.join(',')}#{@end_bracket}"
221
+ end
222
+ end
223
+
224
+
225
+ def _generate_polygon(obj_, contained_=false) # :nodoc:
226
+ if obj_.is_empty?
227
+ contained_ ? 'EMPTY' : ' EMPTY'
228
+ else
229
+ "#{@begin_bracket}#{([_generate_line_string(obj_.exterior_ring, true)] + obj_.interior_rings.map{ |r_| _generate_line_string(r_, true) }).join(',')}#{@end_bracket}"
230
+ end
231
+ end
232
+
233
+
234
+ def _generate_geometry_collection(obj_) # :nodoc:
235
+ if obj_.is_empty?
236
+ ' EMPTY'
237
+ else
238
+ "#{@begin_bracket}#{obj_.map{ |f_| _generate_feature(f_) }.join(',')}#{@end_bracket}"
239
+ end
240
+ end
241
+
242
+
243
+ def _generate_multi_point(obj_) # :nodoc:
244
+ if obj_.is_empty?
245
+ " EMPTY"
246
+ else
247
+ "#{@begin_bracket}#{obj_.map{ |f_| _generate_point(f_) }.join(',')}#{@end_bracket}"
248
+ end
249
+ end
250
+
251
+
252
+ def _generate_multi_line_string(obj_) # :nodoc:
253
+ if obj_.is_empty?
254
+ " EMPTY"
255
+ else
256
+ "#{@begin_bracket}#{obj_.map{ |f_| _generate_line_string(f_, true) }.join(',')}#{@end_bracket}"
257
+ end
258
+ end
259
+
260
+
261
+ def _generate_multi_polygon(obj_) # :nodoc:
262
+ if obj_.is_empty?
263
+ " EMPTY"
264
+ else
265
+ "#{@begin_bracket}#{obj_.map{ |f_| _generate_polygon(f_, true) }.join(',')}#{@end_bracket}"
266
+ end
267
+ end
268
+
269
+
270
+ end
271
+
272
+
273
+ end
274
+
275
+ end