rgeo 0.1.13 → 0.1.14

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 (41) hide show
  1. data/History.rdoc +11 -0
  2. data/Version +1 -1
  3. data/ext/geos_c_impl/factory.c +35 -40
  4. data/ext/geos_c_impl/factory.h +4 -1
  5. data/ext/geos_c_impl/geometry_collection.c +5 -5
  6. data/ext/geos_c_impl/geometry_collection.h +1 -1
  7. data/ext/geos_c_impl/line_string.c +129 -116
  8. data/ext/geos_c_impl/point.c +22 -33
  9. data/ext/geos_c_impl/point.h +1 -6
  10. data/ext/geos_c_impl/polygon.c +4 -4
  11. data/ext/geos_c_impl/polygon.h +1 -1
  12. data/lib/rgeo.rb +1 -0
  13. data/lib/rgeo/cartesian/simple_factory.rb +20 -4
  14. data/lib/rgeo/errors.rb +8 -0
  15. data/lib/rgeo/features/factory.rb +35 -1
  16. data/lib/rgeo/features/point.rb +22 -0
  17. data/lib/rgeo/features/polygon.rb +1 -2
  18. data/lib/rgeo/features/types.rb +70 -16
  19. data/lib/rgeo/geography/factories.rb +40 -3
  20. data/lib/rgeo/geography/factory.rb +25 -5
  21. data/lib/rgeo/geography/simple_mercator/feature_methods.rb +2 -6
  22. data/lib/rgeo/geography/simple_mercator/projector.rb +1 -1
  23. data/lib/rgeo/geos/factory.rb +40 -46
  24. data/lib/rgeo/geos/interface.rb +10 -0
  25. data/lib/rgeo/impl_helpers.rb +0 -1
  26. data/lib/rgeo/impl_helpers/basic_geometry_methods.rb +2 -2
  27. data/lib/rgeo/impl_helpers/basic_point_methods.rb +12 -14
  28. data/lib/rgeo/wkrep.rb +59 -0
  29. data/lib/rgeo/wkrep/wkb_generator.rb +181 -0
  30. data/lib/rgeo/wkrep/wkb_parser.rb +205 -0
  31. data/lib/rgeo/wkrep/wkt_generator.rb +187 -0
  32. data/lib/rgeo/wkrep/wkt_parser.rb +321 -0
  33. data/tests/common/line_string_tests.rb +26 -2
  34. data/tests/common/point_tests.rb +26 -0
  35. data/tests/geos/tc_point.rb +1 -20
  36. data/tests/simple_cartesian/tc_point.rb +1 -0
  37. data/tests/simple_mercator/tc_point.rb +1 -0
  38. data/tests/simple_spherical/tc_point.rb +1 -0
  39. data/tests/tc_oneoff.rb +3 -2
  40. metadata +8 -4
  41. data/lib/rgeo/impl_helpers/serialization.rb +0 -130
@@ -71,9 +71,36 @@ module RGeo
71
71
  # may generate slightly different results from geodesic calculations
72
72
  # than those generated by, e.g., PostGIS (unless you direct PostGIS
73
73
  # to use spherical geodesics).
74
+ #
75
+ # Options include:
76
+ #
77
+ # <tt>:lenient_multi_polygon_assertions</tt>::
78
+ # If set to true, assertion checking on MultiPolygon is disabled.
79
+ # This may speed up creation of MultiPolygon objects, at the
80
+ # expense of not doing the proper checking for OGC MultiPolygon
81
+ # compliance. See RGeo::Features::MultiPolygon for details on
82
+ # the MultiPolygon assertions. Default is false.
83
+ # <tt>:buffer_resolution</tt>::
84
+ # The resolution of buffers around geometries created by this
85
+ # factory. This controls the number of line segments used to
86
+ # approximate curves. The default is 1, which causes, for
87
+ # example, the buffer around a point to be approximated by a
88
+ # 4-sided polygon. A resolution of 2 would cause that buffer
89
+ # to be approximated by an 8-sided polygon. The exact behavior
90
+ # for different kinds of buffers is defined by GEOS.
91
+ # <tt>:support_z_coordinate</tt>::
92
+ # Support <tt>z_coordinate</tt>. Default is false.
93
+ # Note that simple_mercator factories cannot support both
94
+ # <tt>z_coordinate</tt> and <tt>m_coordinate</tt>. They may at
95
+ # most support one or the other.
96
+ # <tt>:support_m_coordinate</tt>::
97
+ # Support <tt>m_coordinate</tt>. Default is false.
98
+ # Note that simple_mercator factories cannot support both
99
+ # <tt>z_coordinate</tt> and <tt>m_coordinate</tt>. They may at
100
+ # most support one or the other.
74
101
 
75
102
  def simple_spherical(opts_={})
76
- Geography::Factory.new(Geography::SimpleSpherical)
103
+ Geography::Factory.new(Geography::SimpleSpherical, :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
77
104
  end
78
105
 
79
106
 
@@ -121,7 +148,7 @@ module RGeo
121
148
  # visualization APIs, so we've decided to fudge on this in the
122
149
  # interest of being true to our expected application use cases.
123
150
  #
124
- # Supported options include:
151
+ # Options include:
125
152
  #
126
153
  # <tt>:lenient_multi_polygon_assertions</tt>::
127
154
  # If set to true, assertion checking on MultiPolygon is disabled.
@@ -137,9 +164,19 @@ module RGeo
137
164
  # 4-sided polygon. A resolution of 2 would cause that buffer
138
165
  # to be approximated by an 8-sided polygon. The exact behavior
139
166
  # for different kinds of buffers is defined by GEOS.
167
+ # <tt>:support_z_coordinate</tt>::
168
+ # Support <tt>z_coordinate</tt>. Default is false.
169
+ # Note that simple_mercator factories cannot support both
170
+ # <tt>z_coordinate</tt> and <tt>m_coordinate</tt>. They may at
171
+ # most support one or the other.
172
+ # <tt>:support_m_coordinate</tt>::
173
+ # Support <tt>m_coordinate</tt>. Default is false.
174
+ # Note that simple_mercator factories cannot support both
175
+ # <tt>z_coordinate</tt> and <tt>m_coordinate</tt>. They may at
176
+ # most support one or the other.
140
177
 
141
178
  def simple_mercator(opts_={})
142
- Geography::Factory.new(Geography::SimpleMercator, :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions])
179
+ Geography::Factory.new(Geography::SimpleMercator, :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
143
180
  end
144
181
 
145
182
 
@@ -51,7 +51,13 @@ module RGeo
51
51
  def initialize(namespace_, opts_={}) # :nodoc:
52
52
  @namespace = namespace_
53
53
  @opts = opts_.dup
54
- @projector = @namespace.const_get(:Projector).new(self, opts_) rescue nil
54
+ if @namespace.const_defined?(:Projector)
55
+ @projector = @namespace.const_get(:Projector).new(self, opts_)
56
+ else
57
+ @projector = nil
58
+ end
59
+ @support_z = opts_[:support_z_coordinate] ? true : false
60
+ @support_m = opts_[:support_m_coordinate] ? true : false
55
61
  end
56
62
 
57
63
 
@@ -126,24 +132,38 @@ module RGeo
126
132
  end
127
133
 
128
134
 
135
+ # See ::RGeo::Features::Factory#has_capability?
136
+
137
+ def has_capability?(name_)
138
+ case name_
139
+ when :z_coordinate
140
+ @support_z
141
+ when :m_coordinate
142
+ @support_m
143
+ else
144
+ nil
145
+ end
146
+ end
147
+
148
+
129
149
  # See ::RGeo::Features::Factory#parse_wkt
130
150
 
131
151
  def parse_wkt(str_)
132
- ImplHelpers::Serialization.parse_wkt(str_, self)
152
+ WKRep::WKTParser.new(self, :support_higher_dimensions => true).parse(str_)
133
153
  end
134
154
 
135
155
 
136
156
  # See ::RGeo::Features::Factory#parse_wkb
137
157
 
138
158
  def parse_wkb(str_)
139
- ImplHelpers::Serialization.parse_wkb(str_, self)
159
+ WKRep::WKBParser.new(self).parse(str_)
140
160
  end
141
161
 
142
162
 
143
163
  # See ::RGeo::Features::Factory#point
144
164
 
145
- def point(x_, y_)
146
- @namespace.const_get(:PointImpl).new(self, x_, y_) rescue nil
165
+ def point(x_, y_, *extra_)
166
+ @namespace.const_get(:PointImpl).new(self, x_, y_, *extra_) rescue nil
147
167
  end
148
168
 
149
169
 
@@ -50,13 +50,9 @@ module RGeo
50
50
 
51
51
 
52
52
  def projection
53
- unless @projection
53
+ if @projection.nil?
54
54
  projection_factory_ = @factory.projection_factory
55
- if projection_factory_
56
- @projection = _make_projection(projection_factory_)
57
- else
58
- @projection = nil
59
- end
55
+ @projection = projection_factory_ ? _make_projection(projection_factory_) : nil
60
56
  @projection = false unless @projection
61
57
  end
62
58
  @projection ? @projection : nil
@@ -48,7 +48,7 @@ module RGeo
48
48
 
49
49
  def initialize(geography_factory_, opts_={})
50
50
  @geography_factory = geography_factory_
51
- @projection_factory = Cartesian.preferred_factory(:srid => 3857, :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions])
51
+ @projection_factory = Cartesian.preferred_factory(:srid => 3857, :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
52
52
  end
53
53
 
54
54
 
@@ -52,30 +52,17 @@ module RGeo
52
52
  # Create a new factory. Returns nil if the GEOS implementation is
53
53
  # not supported.
54
54
  #
55
- # Options include:
56
- #
57
- # <tt>:lenient_multi_polygon_assertions</tt>::
58
- # If set to true, assertion checking on MultiPolygon is disabled.
59
- # This may speed up creation of MultiPolygon objects, at the
60
- # expense of not doing the proper checking for OGC MultiPolygon
61
- # compliance. See RGeo::Features::MultiPolygon for details on
62
- # the MultiPolygon assertions. Default is false.
63
- # <tt>:buffer_resolution</tt>::
64
- # The resolution of buffers around geometries created by this
65
- # factory. This controls the number of line segments used to
66
- # approximate curves. The default is 1, which causes, for
67
- # example, the buffer around a point to be approximated by a
68
- # 4-sided polygon. A resolution of 2 would cause that buffer
69
- # to be approximated by an 8-sided polygon. The exact behavior
70
- # for different kinds of buffers is defined by GEOS.
71
- # <tt>:srid</tt>::
72
- # Set the SRID returned by geometries created by this factory.
73
- # Default is 0.
55
+ # See ::RGeo::Geos::factory for a list of supported options.
74
56
 
75
57
  def create(opts_={})
76
58
  return nil unless respond_to?(:_create)
77
59
  flags_ = 0
78
60
  flags_ |= 1 if opts_[:lenient_multi_polygon_assertions]
61
+ flags_ |= 2 if opts_[:support_z_coordinate]
62
+ flags_ |= 4 if opts_[:support_m_coordinate]
63
+ if flags_ & 6 == 6
64
+ raise Errors::UnsupportedCapability, "GEOS cannot support both Z and M coordinates at the same time."
65
+ end
79
66
  buffer_resolution_ = opts_[:buffer_resolution].to_i
80
67
  buffer_resolution_ = 1 if buffer_resolution_ < 1
81
68
  _create(flags_, opts_[:srid].to_i, buffer_resolution_)
@@ -116,6 +103,20 @@ module RGeo
116
103
  alias_method :==, :eql?
117
104
 
118
105
 
106
+ # See ::RGeo::Features::Factory#has_capability?
107
+
108
+ def has_capability?(name_)
109
+ case name_
110
+ when :z_coordinate
111
+ _flags & 0x2 != 0
112
+ when :m_coordinate
113
+ _flags & 0x4 != 0
114
+ else
115
+ nil
116
+ end
117
+ end
118
+
119
+
119
120
  # See ::RGeo::Features::Factory#parse_wkt
120
121
 
121
122
  def parse_wkt(str_)
@@ -132,8 +133,12 @@ module RGeo
132
133
 
133
134
  # See ::RGeo::Features::Factory#point
134
135
 
135
- def point(x_, y_)
136
- PointImpl.create(self, x_, y_) rescue nil
136
+ def point(x_, y_, *extra_)
137
+ if extra_.length > (_flags & 6 == 0 ? 0 : 1)
138
+ nil
139
+ else
140
+ PointImpl.create(self, x_, y_, extra_[0].to_f) rescue nil
141
+ end
137
142
  end
138
143
 
139
144
 
@@ -204,41 +209,30 @@ module RGeo
204
209
 
205
210
  def override_cast(original_, ntype_, keep_subtype_, force_new_)
206
211
  return nil unless Geos.supported?
207
- type_ = original_.geometry_type
208
- ntype_ = type_ if keep_subtype_ && type_.include?(ntype_)
209
- if original_.factory != self && ntype_ == type_
210
- if GeometryImpl === original_
212
+ if GeometryImpl === original_
213
+ type_ = original_.geometry_type
214
+ ntype_ = type_ if keep_subtype_ && type_.include?(ntype_)
215
+ if original_.factory != self && ntype_ == type_
211
216
  result_ = original_.dup
212
217
  result_._set_factory(self)
213
218
  return result_
214
219
  end
215
- if type_ == Features::Point && original_.respond_to?(:z)
216
- z_ = original_.z
217
- return PointImpl.create(self, original_.x, original_.y, z_) if z_
218
- end
219
- end
220
- if GeometryImpl === original_ && (original_.factory != self || ntype_ != type_) &&
221
- (type_ == Features::LineString || type_ == Features::Line || type_ == Features::LinearRing)
222
- then
223
- if ntype_ == Features::LineString
224
- return LineStringImpl._copy_from(self, original_)
225
- elsif ntype_ == Features::Line
226
- return LineImpl._copy_from(self, original_)
227
- elsif ntype_ == Features::LinearRing
228
- return LinearRingImpl._copy_from(self, original_)
220
+ if (original_.factory != self || ntype_ != type_) &&
221
+ (type_ == Features::LineString || type_.include?(Features::LineString))
222
+ then
223
+ if ntype_ == Features::LineString
224
+ return LineStringImpl._copy_from(self, original_)
225
+ elsif ntype_ == Features::Line
226
+ return LineImpl._copy_from(self, original_)
227
+ elsif ntype_ == Features::LinearRing
228
+ return LinearRingImpl._copy_from(self, original_)
229
+ end
229
230
  end
230
231
  end
231
232
  false
232
233
  end
233
234
 
234
235
 
235
- # A GEOS extension that creates a 3-D point with a Z coordinate.
236
-
237
- def point3d(x_, y_, z_)
238
- PointImpl.create3d(self, x_, y_, z_) rescue nil
239
- end
240
-
241
-
242
236
  end
243
237
 
244
238
 
@@ -81,6 +81,16 @@ module RGeo
81
81
  # <tt>:srid</tt>::
82
82
  # Set the SRID returned by geometries created by this factory.
83
83
  # Default is 0.
84
+ # <tt>:support_z_coordinate</tt>::
85
+ # Support <tt>z_coordinate</tt>. Default is false.
86
+ # Note that GEOS factories cannot support both
87
+ # <tt>z_coordinate</tt> and <tt>m_coordinate</tt>. They may at
88
+ # most support one or the other.
89
+ # <tt>:support_m_coordinate</tt>::
90
+ # Support <tt>m_coordinate</tt>. Default is false.
91
+ # Note that GEOS factories cannot support both
92
+ # <tt>z_coordinate</tt> and <tt>m_coordinate</tt>. They may at
93
+ # most support one or the other.
84
94
 
85
95
  def factory(opts_={})
86
96
  supported? ? Factory.create(opts_) : nil
@@ -51,6 +51,5 @@ paths_ = [
51
51
  'impl_helpers/basic_point_methods',
52
52
  'impl_helpers/basic_line_string_methods',
53
53
  'impl_helpers/basic_polygon_methods',
54
- 'impl_helpers/serialization',
55
54
  ]
56
55
  paths_.each{ |path_| require "rgeo/#{path_}" }
@@ -73,12 +73,12 @@ module RGeo
73
73
 
74
74
 
75
75
  def as_text
76
- Serialization.unparse_wkt(self)
76
+ WKRep::WKTGenerator.new.generate(self)
77
77
  end
78
78
 
79
79
 
80
80
  def as_binary
81
- Serialization.unparse_wkb(self)
81
+ WKRep::WKBGenerator.new.generate(self)
82
82
  end
83
83
 
84
84
 
@@ -42,10 +42,12 @@ module RGeo
42
42
  module BasicPointMethods # :nodoc:
43
43
 
44
44
 
45
- def initialize(factory_, x_, y_)
45
+ def initialize(factory_, x_, y_, *extra_)
46
46
  _set_factory(factory_)
47
47
  @x = x_.to_f
48
48
  @y = y_.to_f
49
+ @z = factory_.has_capability?(:z_coordinate) ? extra_.shift.to_f : nil
50
+ @m = factory_.has_capability?(:m_coordinate) ? extra_.shift.to_f : nil
49
51
  _validate_geometry
50
52
  end
51
53
 
@@ -60,22 +62,18 @@ module RGeo
60
62
  end
61
63
 
62
64
 
63
- def eql?(rhs_)
64
- rhs_.is_a?(self.class) && rhs_.factory.eql?(@factory) && @x == rhs_.x && @y == rhs_.y
65
+ def z
66
+ @z
65
67
  end
66
68
 
67
69
 
68
- def cast(type_)
69
- case type_
70
- when Features::Point
71
- self
72
- when Features::GeometryCollection
73
- factory.collection([self]) rescue nil
74
- when Features::MultiPoint
75
- factory.multi_point([self]) rescue nil
76
- else
77
- super
78
- end
70
+ def m
71
+ @m
72
+ end
73
+
74
+
75
+ def eql?(rhs_)
76
+ rhs_.is_a?(self.class) && rhs_.factory.eql?(@factory) && @x == rhs_.x && @y == rhs_.y && @z == rhs_.z && @m == rhs_.m
79
77
  end
80
78
 
81
79
 
data/lib/rgeo/wkrep.rb ADDED
@@ -0,0 +1,59 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known representation 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
+
40
+ # Implementations of the OpenGIS well-known representation
41
+ # (i.e. the WKT/WKB) and its variants (e.g. the EWKT/EWKB used by
42
+ # PostGIS). Provides generation and parsing facilities.
43
+
44
+ module WKRep
45
+ end
46
+
47
+
48
+ end
49
+
50
+
51
+ # Dependency source files.
52
+ paths_ = [
53
+ 'features',
54
+ 'wkrep/wkt_parser',
55
+ 'wkrep/wkt_generator',
56
+ 'wkrep/wkb_parser',
57
+ 'wkrep/wkb_generator',
58
+ ]
59
+ paths_.each{ |path_| require "rgeo/#{path_}" }
@@ -0,0 +1,181 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known binary 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
+ class WKBGenerator
43
+
44
+
45
+ TYPE_CODES = {
46
+ Features::Point => 1,
47
+ Features::LineString => 2,
48
+ Features::LinearRing => 2,
49
+ Features::Line => 2,
50
+ Features::Polygon => 3,
51
+ Features::MultiPoint => 4,
52
+ Features::MultiLineString => 5,
53
+ Features::MultiPolygon => 6,
54
+ Features::GeometryCollection => 7,
55
+ }
56
+
57
+
58
+ def initialize(opts_={})
59
+ @type_format = opts_[:type_format]
60
+ @emit_ewkb_srid = opts_[:emit_ewkb_srid] if @type_format == :ewkb
61
+ @hex_format = opts_[:hex_format]
62
+ @little_endian = opts_[:little_endian]
63
+ end
64
+
65
+
66
+ def generate(obj_)
67
+ factory_ = obj_.factory
68
+ if @type_format == :ewkb || @type_format == :wkb12
69
+ @cur_has_z = factory_.has_capability?(:z_coordinate)
70
+ @cur_has_m = factory_.has_capability?(:m_coordinate)
71
+ else
72
+ @cur_has_z = nil
73
+ @cur_has_m = nil
74
+ end
75
+ @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
76
+ _start_emitter
77
+ _generate_feature(obj_, true)
78
+ _finish_emitter
79
+ end
80
+
81
+
82
+ def _generate_feature(obj_, toplevel_=false) # :nodoc:
83
+ _emit_byte(@little_endian ? 1 : 0)
84
+ type_ = obj_.geometry_type
85
+ type_code_ = TYPE_CODES[type_]
86
+ unless type_code_
87
+ raise Errors::ParseError, "Unrecognized Geometry Type: #{type_}"
88
+ end
89
+ emit_srid_ = false
90
+ if @emit_ewkb_srid && toplevel_
91
+ type_code |= 0x20000000
92
+ emit_srid_ = true
93
+ end
94
+ if @type_format == :ewkb
95
+ type_code_ |= 0x80000000 if @cur_has_z
96
+ type_code_ |= 0x40000000 if @cur_has_m
97
+ elsif @type_format == :wkb12
98
+ type_code_ += 1000 if @cur_has_z
99
+ type_code_ += 2000 if @cur_has_m
100
+ end
101
+ _emit_integer(type_code_)
102
+ _emit_integer(obj_.srid) if emit_srid_
103
+ if type_ == Features::Point
104
+ _emit_doubles(_point_coords(obj_))
105
+ elsif type_.subtype_of?(Features::LineString)
106
+ _emit_line_string_coords(obj_)
107
+ elsif type_ == Features::Polygon
108
+ exterior_ring_ = obj_.exterior_ring
109
+ if exterior_ring_.is_empty?
110
+ _emit_integer(0)
111
+ else
112
+ _emit_integer(1 + obj_.num_interior_rings)
113
+ _emit_line_string_coords(exterior_ring_)
114
+ obj_.interior_rings.each{ |r_| _emit_line_string_coords(r_) }
115
+ end
116
+ elsif type_ == Features::GeometryCollection
117
+ _emit_integer(obj_.num_geometries)
118
+ obj_.each{ |g_| _generate_feature(g_) }
119
+ elsif type_ == Features::MultiPoint
120
+ _emit_integer(obj_.num_geometries)
121
+ obj_.each{ |g_| _generate_feature(g_) }
122
+ elsif type_ == Features::MultiLineString
123
+ _emit_integer(obj_.num_geometries)
124
+ obj_.each{ |g_| _generate_feature(g_) }
125
+ elsif type_ == Features::MultiPolygon
126
+ _emit_integer(obj_.num_geometries)
127
+ obj_.each{ |g_| _generate_feature(g_) }
128
+ end
129
+ end
130
+
131
+
132
+ def _point_coords(obj_, array_=[]) # :nodoc:
133
+ array_ << obj_.x
134
+ array_ << obj_.y
135
+ array_ << obj_.z if @cur_has_z
136
+ array_ << obj_.m if @cur_has_m
137
+ array_
138
+ end
139
+
140
+
141
+ def _emit_line_string_coords(obj_) # :nodoc:
142
+ array_ = []
143
+ obj_.points.each{ |p_| _point_coords(p_, array_) }
144
+ _emit_integer(obj_.num_points)
145
+ _emit_doubles(array_)
146
+ end
147
+
148
+
149
+ def _start_emitter # :nodoc:
150
+ @cur_array = []
151
+ end
152
+
153
+
154
+ def _emit_byte(value_) # :nodoc:
155
+ @cur_array << [value_].pack("C")
156
+ end
157
+
158
+
159
+ def _emit_integer(value_) # :nodoc:
160
+ @cur_array << [value_].pack(@little_endian ? 'V' : 'N')
161
+ end
162
+
163
+
164
+ def _emit_doubles(array_) # :nodoc:
165
+ @cur_array << array_.pack(@little_endian ? 'E*' : 'G*')
166
+ end
167
+
168
+
169
+ def _finish_emitter # :nodoc:
170
+ str_ = @cur_array.join
171
+ @cur_array = nil
172
+ str_
173
+ end
174
+
175
+
176
+ end
177
+
178
+
179
+ end
180
+
181
+ end