rgeo 0.1.13 → 0.1.14

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