schleyfox-rgeo 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
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