rgeo-dschee 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +29 -0
  3. data/ext/geos_c_impl/coordinates.c +65 -0
  4. data/ext/geos_c_impl/coordinates.h +2 -0
  5. data/ext/geos_c_impl/extconf.rb +43 -0
  6. data/ext/geos_c_impl/factory.c +995 -0
  7. data/ext/geos_c_impl/factory.h +238 -0
  8. data/ext/geos_c_impl/geometry.c +1093 -0
  9. data/ext/geos_c_impl/geometry.h +23 -0
  10. data/ext/geos_c_impl/geometry_collection.c +757 -0
  11. data/ext/geos_c_impl/geometry_collection.h +46 -0
  12. data/ext/geos_c_impl/line_string.c +675 -0
  13. data/ext/geos_c_impl/line_string.h +32 -0
  14. data/ext/geos_c_impl/main.c +40 -0
  15. data/ext/geos_c_impl/point.c +236 -0
  16. data/ext/geos_c_impl/point.h +30 -0
  17. data/ext/geos_c_impl/polygon.c +359 -0
  18. data/ext/geos_c_impl/polygon.h +43 -0
  19. data/ext/geos_c_impl/preface.h +38 -0
  20. data/ext/proj4_c_impl/extconf.rb +62 -0
  21. data/ext/proj4_c_impl/main.c +315 -0
  22. data/lib/rgeo.rb +89 -0
  23. data/lib/rgeo/cartesian.rb +25 -0
  24. data/lib/rgeo/cartesian/analysis.rb +77 -0
  25. data/lib/rgeo/cartesian/bounding_box.rb +398 -0
  26. data/lib/rgeo/cartesian/calculations.rb +113 -0
  27. data/lib/rgeo/cartesian/factory.rb +347 -0
  28. data/lib/rgeo/cartesian/feature_classes.rb +100 -0
  29. data/lib/rgeo/cartesian/feature_methods.rb +88 -0
  30. data/lib/rgeo/cartesian/interface.rb +135 -0
  31. data/lib/rgeo/coord_sys.rb +43 -0
  32. data/lib/rgeo/coord_sys/cs/entities.rb +1315 -0
  33. data/lib/rgeo/coord_sys/cs/factories.rb +148 -0
  34. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +272 -0
  35. data/lib/rgeo/coord_sys/proj4.rb +293 -0
  36. data/lib/rgeo/coord_sys/srs_database/interface.rb +115 -0
  37. data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +140 -0
  38. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +62 -0
  39. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +63 -0
  40. data/lib/rgeo/error.rb +27 -0
  41. data/lib/rgeo/feature.rb +54 -0
  42. data/lib/rgeo/feature/curve.rb +111 -0
  43. data/lib/rgeo/feature/factory.rb +278 -0
  44. data/lib/rgeo/feature/factory_generator.rb +96 -0
  45. data/lib/rgeo/feature/geometry.rb +624 -0
  46. data/lib/rgeo/feature/geometry_collection.rb +95 -0
  47. data/lib/rgeo/feature/line.rb +26 -0
  48. data/lib/rgeo/feature/line_string.rb +60 -0
  49. data/lib/rgeo/feature/linear_ring.rb +26 -0
  50. data/lib/rgeo/feature/mixins.rb +143 -0
  51. data/lib/rgeo/feature/multi_curve.rb +71 -0
  52. data/lib/rgeo/feature/multi_line_string.rb +26 -0
  53. data/lib/rgeo/feature/multi_point.rb +33 -0
  54. data/lib/rgeo/feature/multi_polygon.rb +57 -0
  55. data/lib/rgeo/feature/multi_surface.rb +73 -0
  56. data/lib/rgeo/feature/point.rb +84 -0
  57. data/lib/rgeo/feature/polygon.rb +97 -0
  58. data/lib/rgeo/feature/surface.rb +79 -0
  59. data/lib/rgeo/feature/types.rb +284 -0
  60. data/lib/rgeo/geographic.rb +40 -0
  61. data/lib/rgeo/geographic/factory.rb +450 -0
  62. data/lib/rgeo/geographic/interface.rb +489 -0
  63. data/lib/rgeo/geographic/proj4_projector.rb +58 -0
  64. data/lib/rgeo/geographic/projected_feature_classes.rb +107 -0
  65. data/lib/rgeo/geographic/projected_feature_methods.rb +212 -0
  66. data/lib/rgeo/geographic/projected_window.rb +383 -0
  67. data/lib/rgeo/geographic/simple_mercator_projector.rb +110 -0
  68. data/lib/rgeo/geographic/spherical_feature_classes.rb +100 -0
  69. data/lib/rgeo/geographic/spherical_feature_methods.rb +134 -0
  70. data/lib/rgeo/geographic/spherical_math.rb +188 -0
  71. data/lib/rgeo/geos.rb +89 -0
  72. data/lib/rgeo/geos/capi_factory.rb +470 -0
  73. data/lib/rgeo/geos/capi_feature_classes.rb +129 -0
  74. data/lib/rgeo/geos/ffi_factory.rb +592 -0
  75. data/lib/rgeo/geos/ffi_feature_classes.rb +83 -0
  76. data/lib/rgeo/geos/ffi_feature_methods.rb +574 -0
  77. data/lib/rgeo/geos/interface.rb +202 -0
  78. data/lib/rgeo/geos/utils.rb +74 -0
  79. data/lib/rgeo/geos/zm_factory.rb +405 -0
  80. data/lib/rgeo/geos/zm_feature_classes.rb +80 -0
  81. data/lib/rgeo/geos/zm_feature_methods.rb +344 -0
  82. data/lib/rgeo/impl_helper.rb +19 -0
  83. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +185 -0
  84. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +61 -0
  85. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +146 -0
  86. data/lib/rgeo/impl_helper/basic_point_methods.rb +104 -0
  87. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +87 -0
  88. data/lib/rgeo/impl_helper/math.rb +14 -0
  89. data/lib/rgeo/impl_helper/utils.rb +29 -0
  90. data/lib/rgeo/version.rb +3 -0
  91. data/lib/rgeo/wkrep.rb +37 -0
  92. data/lib/rgeo/wkrep/wkb_generator.rb +201 -0
  93. data/lib/rgeo/wkrep/wkb_parser.rb +251 -0
  94. data/lib/rgeo/wkrep/wkt_generator.rb +207 -0
  95. data/lib/rgeo/wkrep/wkt_parser.rb +415 -0
  96. data/lib/rgeo/yaml.rb +23 -0
  97. data/test/cartesian_analysis_test.rb +65 -0
  98. data/test/cartesian_bbox_test.rb +123 -0
  99. data/test/common/factory_tests.rb +78 -0
  100. data/test/common/geometry_collection_tests.rb +237 -0
  101. data/test/common/line_string_tests.rb +330 -0
  102. data/test/common/multi_line_string_tests.rb +182 -0
  103. data/test/common/multi_point_tests.rb +200 -0
  104. data/test/common/multi_polygon_tests.rb +191 -0
  105. data/test/common/point_tests.rb +370 -0
  106. data/test/common/polygon_tests.rb +261 -0
  107. data/test/coord_sys/ogc_cs_test.rb +342 -0
  108. data/test/coord_sys/proj4_srs_data_test.rb +41 -0
  109. data/test/coord_sys/proj4_test.rb +150 -0
  110. data/test/coord_sys/sr_org_test.rb +32 -0
  111. data/test/coord_sys/url_reader_test.rb +42 -0
  112. data/test/geos_capi/factory_test.rb +31 -0
  113. data/test/geos_capi/geometry_collection_test.rb +24 -0
  114. data/test/geos_capi/line_string_test.rb +24 -0
  115. data/test/geos_capi/misc_test.rb +116 -0
  116. data/test/geos_capi/multi_line_string_test.rb +24 -0
  117. data/test/geos_capi/multi_point_test.rb +24 -0
  118. data/test/geos_capi/multi_polygon_test.rb +39 -0
  119. data/test/geos_capi/parsing_unparsing_test.rb +40 -0
  120. data/test/geos_capi/point_test.rb +72 -0
  121. data/test/geos_capi/polygon_test.rb +154 -0
  122. data/test/geos_capi/zmfactory_test.rb +57 -0
  123. data/test/geos_ffi/factory_test.rb +31 -0
  124. data/test/geos_ffi/geometry_collection_test.rb +24 -0
  125. data/test/geos_ffi/line_string_test.rb +24 -0
  126. data/test/geos_ffi/misc_test.rb +63 -0
  127. data/test/geos_ffi/multi_line_string_test.rb +24 -0
  128. data/test/geos_ffi/multi_point_test.rb +24 -0
  129. data/test/geos_ffi/multi_polygon_test.rb +33 -0
  130. data/test/geos_ffi/parsing_unparsing_test.rb +41 -0
  131. data/test/geos_ffi/point_test.rb +77 -0
  132. data/test/geos_ffi/polygon_test.rb +46 -0
  133. data/test/geos_ffi/zmfactory_test.rb +58 -0
  134. data/test/mixins_test.rb +141 -0
  135. data/test/oneoff_test.rb +26 -0
  136. data/test/projected_geographic/factory_test.rb +25 -0
  137. data/test/projected_geographic/geometry_collection_test.rb +24 -0
  138. data/test/projected_geographic/line_string_test.rb +24 -0
  139. data/test/projected_geographic/multi_line_string_test.rb +26 -0
  140. data/test/projected_geographic/multi_point_test.rb +30 -0
  141. data/test/projected_geographic/multi_polygon_test.rb +25 -0
  142. data/test/projected_geographic/point_test.rb +51 -0
  143. data/test/projected_geographic/polygon_test.rb +24 -0
  144. data/test/simple_cartesian/calculations_test.rb +99 -0
  145. data/test/simple_cartesian/factory_test.rb +27 -0
  146. data/test/simple_cartesian/geometry_collection_test.rb +30 -0
  147. data/test/simple_cartesian/line_string_test.rb +31 -0
  148. data/test/simple_cartesian/multi_line_string_test.rb +28 -0
  149. data/test/simple_cartesian/multi_point_test.rb +31 -0
  150. data/test/simple_cartesian/multi_polygon_test.rb +31 -0
  151. data/test/simple_cartesian/point_test.rb +50 -0
  152. data/test/simple_cartesian/polygon_test.rb +28 -0
  153. data/test/simple_mercator/factory_test.rb +25 -0
  154. data/test/simple_mercator/geometry_collection_test.rb +24 -0
  155. data/test/simple_mercator/line_string_test.rb +24 -0
  156. data/test/simple_mercator/multi_line_string_test.rb +26 -0
  157. data/test/simple_mercator/multi_point_test.rb +29 -0
  158. data/test/simple_mercator/multi_polygon_test.rb +25 -0
  159. data/test/simple_mercator/point_test.rb +55 -0
  160. data/test/simple_mercator/polygon_test.rb +24 -0
  161. data/test/simple_mercator/window_test.rb +173 -0
  162. data/test/spherical_geographic/calculations_test.rb +167 -0
  163. data/test/spherical_geographic/factory_test.rb +27 -0
  164. data/test/spherical_geographic/geometry_collection_test.rb +31 -0
  165. data/test/spherical_geographic/line_string_test.rb +31 -0
  166. data/test/spherical_geographic/multi_line_string_test.rb +29 -0
  167. data/test/spherical_geographic/multi_point_test.rb +31 -0
  168. data/test/spherical_geographic/multi_polygon_test.rb +31 -0
  169. data/test/spherical_geographic/point_test.rb +78 -0
  170. data/test/spherical_geographic/polygon_test.rb +28 -0
  171. data/test/types_test.rb +42 -0
  172. data/test/wkrep/wkb_generator_test.rb +185 -0
  173. data/test/wkrep/wkb_parser_test.rb +293 -0
  174. data/test/wkrep/wkt_generator_test.rb +294 -0
  175. data/test/wkrep/wkt_parser_test.rb +412 -0
  176. metadata +386 -0
@@ -0,0 +1,3 @@
1
+ module RGeo
2
+ VERSION = "0.5.4".freeze
3
+ end
data/lib/rgeo/wkrep.rb ADDED
@@ -0,0 +1,37 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known representation for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ module RGeo
8
+ # This module contains implementations of the OpenGIS well-known
9
+ # representations: the WKT (well-known text representation) and the
10
+ # WKB (well-known binary representation), as defined in the Simple
11
+ # Features Specification, version 1.1. Facilities are provided to
12
+ # serialize any geometry into one of these formats, and to parse a
13
+ # serialized string back into a geometry. Support is also provided for
14
+ # the common extensions to these formats-- notably, the EWKT and EWKB
15
+ # formats used by PostGIS.
16
+ #
17
+ # To serialize a geometry into WKT (well-known text) format, use
18
+ # the WKRep::WKTGenerator class.
19
+ #
20
+ # To serialize a geometry into WKB (well-known binary) format, use
21
+ # the WKRep::WKBGenerator class.
22
+ #
23
+ # To parse a string in WKT (well-known text) format back into a
24
+ # geometry object, use the WKRep::WKTParser class.
25
+ #
26
+ # To parse a byte string in WKB (well-known binary) format back into a
27
+ # geometry object, use the WKRep::WKBParser class.
28
+
29
+ module WKRep
30
+ end
31
+ end
32
+
33
+ # Implementation files
34
+ require "rgeo/wkrep/wkt_parser"
35
+ require "rgeo/wkrep/wkt_generator"
36
+ require "rgeo/wkrep/wkb_parser"
37
+ require "rgeo/wkrep/wkb_generator"
@@ -0,0 +1,201 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known binary generator for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ module RGeo
8
+ module WKRep
9
+ # This class provides the functionality of serializing a geometry as
10
+ # WKB (well-known binary) format. You may also customize the
11
+ # serializer to generate PostGIS EWKB extensions to the output, or to
12
+ # follow the Simple Features Specification 1.2 extensions for Z and M
13
+ # coordinates.
14
+ #
15
+ # To use this class, create an instance with the desired settings and
16
+ # customizations, and call the generate method.
17
+ #
18
+ # === Configuration options
19
+ #
20
+ # The following options are recognized. These can be passed to the
21
+ # constructor, or set on the object afterwards.
22
+ #
23
+ # [<tt>:type_format</tt>]
24
+ # The format for type codes. Possible values are <tt>:wkb11</tt>,
25
+ # indicating SFS 1.1 WKB (i.e. no Z or M values); <tt>:ewkb</tt>,
26
+ # indicating the PostGIS EWKB extensions (i.e. Z and M presence
27
+ # flagged by the two high bits of the type code, and support for
28
+ # embedded SRID); or <tt>:wkb12</tt> (indicating SFS 1.2 WKB
29
+ # (i.e. Z and M presence flagged by adding 1000 and/or 2000 to
30
+ # the type code.) Default is <tt>:wkb11</tt>.
31
+ # [<tt>:emit_ewkb_srid</tt>]
32
+ # If true, embed the SRID in the toplevel geometry. Available only
33
+ # if <tt>:type_format</tt> is <tt>:ewkb</tt>. Default is false.
34
+ # [<tt>:hex_format</tt>]
35
+ # If true, output a hex string instead of a byte string.
36
+ # Default is false.
37
+ # [<tt>:little_endian</tt>]
38
+ # If true, output little endian (NDR) byte order. If false, output
39
+ # big endian (XDR), or network byte order. Default is false.
40
+
41
+ class WKBGenerator
42
+ # :stopdoc:
43
+ TYPE_CODES = {
44
+ Feature::Point => 1,
45
+ Feature::LineString => 2,
46
+ Feature::LinearRing => 2,
47
+ Feature::Line => 2,
48
+ Feature::Polygon => 3,
49
+ Feature::MultiPoint => 4,
50
+ Feature::MultiLineString => 5,
51
+ Feature::MultiPolygon => 6,
52
+ Feature::GeometryCollection => 7
53
+ }.freeze
54
+ # :startdoc:
55
+
56
+ # Create and configure a WKB generator. See the WKBGenerator
57
+ # documentation for the options that can be passed.
58
+
59
+ def initialize(opts_ = {})
60
+ @type_format = opts_[:type_format] || :wkb11
61
+ @emit_ewkb_srid = @type_format == :ewkb ?
62
+ (opts_[:emit_ewkb_srid] ? true : false) : nil
63
+ @hex_format = opts_[:hex_format] ? true : false
64
+ @little_endian = opts_[:little_endian] ? true : false
65
+ end
66
+
67
+ # Returns the format for type codes. See WKBGenerator for details.
68
+ attr_reader :type_format
69
+
70
+ # Returns whether SRID is embedded. See WKBGenerator for details.
71
+ def emit_ewkb_srid?
72
+ @emit_ewkb_srid
73
+ end
74
+
75
+ # Returns whether output is converted to hex.
76
+ # See WKBGenerator for details.
77
+ def hex_format?
78
+ @hex_format
79
+ end
80
+
81
+ # Returns whether output is little-endian (NDR).
82
+ # See WKBGenerator for details.
83
+ def little_endian?
84
+ @little_endian
85
+ end
86
+
87
+ def _properties # :nodoc:
88
+ {
89
+ "type_format" => @type_format.to_s,
90
+ "emit_ewkb_srid" => @emit_ewkb_srid,
91
+ "hex_format" => @hex_format,
92
+ "little_endian" => @little_endian
93
+ }
94
+ end
95
+
96
+ # Generate and return the WKB format for the given geometry object,
97
+ # according to the current settings.
98
+
99
+ def generate(obj_)
100
+ factory_ = obj_.factory
101
+ if @type_format == :ewkb || @type_format == :wkb12
102
+ @cur_has_z = factory_.property(:has_z_coordinate)
103
+ @cur_has_m = factory_.property(:has_m_coordinate)
104
+ else
105
+ @cur_has_z = nil
106
+ @cur_has_m = nil
107
+ end
108
+ @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
109
+ _start_emitter
110
+ _generate_feature(obj_, true)
111
+ _finish_emitter
112
+ end
113
+
114
+ def _generate_feature(obj_, toplevel_ = false) # :nodoc:
115
+ _emit_byte(@little_endian ? 1 : 0)
116
+ type_ = obj_.geometry_type
117
+ type_code_ = TYPE_CODES[type_]
118
+ unless type_code_
119
+ raise Error::ParseError, "Unrecognized Geometry Type: #{type_}"
120
+ end
121
+ emit_srid_ = false
122
+ if @type_format == :ewkb
123
+ type_code_ |= 0x80000000 if @cur_has_z
124
+ type_code_ |= 0x40000000 if @cur_has_m
125
+ if @emit_ewkb_srid && toplevel_
126
+ type_code_ |= 0x20000000
127
+ emit_srid_ = true
128
+ end
129
+ elsif @type_format == :wkb12
130
+ type_code_ += 1000 if @cur_has_z
131
+ type_code_ += 2000 if @cur_has_m
132
+ end
133
+ _emit_integer(type_code_)
134
+ _emit_integer(obj_.srid) if emit_srid_
135
+ if type_ == Feature::Point
136
+ _emit_doubles(_point_coords(obj_))
137
+ elsif type_.subtype_of?(Feature::LineString)
138
+ _emit_line_string_coords(obj_)
139
+ elsif type_ == Feature::Polygon
140
+ exterior_ring_ = obj_.exterior_ring
141
+ if exterior_ring_.is_empty?
142
+ _emit_integer(0)
143
+ else
144
+ _emit_integer(1 + obj_.num_interior_rings)
145
+ _emit_line_string_coords(exterior_ring_)
146
+ obj_.interior_rings.each { |r_| _emit_line_string_coords(r_) }
147
+ end
148
+ elsif type_ == Feature::GeometryCollection
149
+ _emit_integer(obj_.num_geometries)
150
+ obj_.each { |g_| _generate_feature(g_) }
151
+ elsif type_ == Feature::MultiPoint
152
+ _emit_integer(obj_.num_geometries)
153
+ obj_.each { |g_| _generate_feature(g_) }
154
+ elsif type_ == Feature::MultiLineString
155
+ _emit_integer(obj_.num_geometries)
156
+ obj_.each { |g_| _generate_feature(g_) }
157
+ elsif type_ == Feature::MultiPolygon
158
+ _emit_integer(obj_.num_geometries)
159
+ obj_.each { |g_| _generate_feature(g_) }
160
+ end
161
+ end
162
+
163
+ def _point_coords(obj_, array_ = []) # :nodoc:
164
+ array_ << obj_.x
165
+ array_ << obj_.y
166
+ array_ << obj_.z if @cur_has_z
167
+ array_ << obj_.m if @cur_has_m
168
+ array_
169
+ end
170
+
171
+ def _emit_line_string_coords(obj_) # :nodoc:
172
+ array_ = []
173
+ obj_.points.each { |p_| _point_coords(p_, array_) }
174
+ _emit_integer(obj_.num_points)
175
+ _emit_doubles(array_)
176
+ end
177
+
178
+ def _start_emitter # :nodoc:
179
+ @cur_array = []
180
+ end
181
+
182
+ def _emit_byte(value_) # :nodoc:
183
+ @cur_array << [value_].pack("C")
184
+ end
185
+
186
+ def _emit_integer(value_) # :nodoc:
187
+ @cur_array << [value_].pack(@little_endian ? "V" : "N")
188
+ end
189
+
190
+ def _emit_doubles(array_) # :nodoc:
191
+ @cur_array << array_.pack(@little_endian ? "E*" : "G*")
192
+ end
193
+
194
+ def _finish_emitter # :nodoc:
195
+ str_ = @cur_array.join
196
+ @cur_array = nil
197
+ @hex_format ? str_.unpack("H*")[0] : str_
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,251 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Well-known binary parser for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ module RGeo
8
+ module WKRep
9
+ # This class provides the functionality of parsing a geometry from
10
+ # WKB (well-known binary) format. You may also customize the parser
11
+ # to recognize PostGIS EWKB extensions to the input, or Simple
12
+ # Features Specification 1.2 extensions for Z and M coordinates.
13
+ #
14
+ # To use this class, create an instance with the desired settings and
15
+ # customizations, and call the parse method.
16
+ #
17
+ # === Configuration options
18
+ #
19
+ # You must provide each parser with an RGeo::Feature::FactoryGenerator.
20
+ # It should understand the configuration options <tt>:srid</tt>,
21
+ # <tt>:has_z_coordinate</tt>, and <tt>:has_m_coordinate</tt>.
22
+ # You may also pass a specific RGeo::Feature::Factory, or nil to
23
+ # specify the default Cartesian FactoryGenerator.
24
+ #
25
+ # The following additional options are recognized. These can be passed
26
+ # to the constructor, or set on the object afterwards.
27
+ #
28
+ # [<tt>:support_ewkb</tt>]
29
+ # Activate support for PostGIS EWKB type codes, which use high
30
+ # order bits in the type code to signal the presence of Z, M, and
31
+ # SRID values in the data. Default is false.
32
+ # [<tt>:support_wkb12</tt>]
33
+ # Activate support for SFS 1.2 extensions to the type codes, which
34
+ # use values greater than 1000 to signal the presence of Z and M
35
+ # values in the data. SFS 1.2 types such as triangle, tin, and
36
+ # polyhedralsurface are NOT yet supported. Default is false.
37
+ # [<tt>:ignore_extra_bytes</tt>]
38
+ # If true, extra bytes at the end of the data are ignored. If
39
+ # false (the default), extra bytes will trigger a parse error.
40
+ # [<tt>:default_srid</tt>]
41
+ # A SRID to pass to the factory generator if no SRID is present in
42
+ # the input. Defaults to nil (i.e. don't specify a SRID).
43
+
44
+ class WKBParser
45
+ # Create and configure a WKB parser. See the WKBParser
46
+ # documentation for the options that can be passed.
47
+
48
+ def initialize(factory_generator_ = nil, opts_ = {})
49
+ if factory_generator_.is_a?(Feature::Factory::Instance)
50
+ @factory_generator = Feature::FactoryGenerator.single(factory_generator_)
51
+ @exact_factory = factory_generator_
52
+ elsif factory_generator_.respond_to?(:call)
53
+ @factory_generator = factory_generator_
54
+ @exact_factory = nil
55
+ else
56
+ @factory_generator = Cartesian.method(:preferred_factory)
57
+ @exact_factory = nil
58
+ end
59
+ @support_ewkb = opts_[:support_ewkb] ? true : false
60
+ @support_wkb12 = opts_[:support_wkb12] ? true : false
61
+ @ignore_extra_bytes = opts_[:ignore_extra_bytes] ? true : false
62
+ @default_srid = opts_[:default_srid]
63
+ end
64
+
65
+ # Returns the factory generator. See WKBParser for details.
66
+ attr_reader :factory_generator
67
+
68
+ # If this parser was given an exact factory, returns it; otherwise
69
+ # returns nil.
70
+ attr_reader :exact_factory
71
+
72
+ # Returns true if this parser supports EWKB.
73
+ # See WKBParser for details.
74
+ def support_ewkb?
75
+ @support_ewkb
76
+ end
77
+
78
+ # Returns true if this parser supports SFS 1.2 extensions.
79
+ # See WKBParser for details.
80
+ def support_wkb12?
81
+ @support_wkb12
82
+ end
83
+
84
+ # Returns true if this parser ignores extra bytes.
85
+ # See WKBParser for details.
86
+ def ignore_extra_bytes?
87
+ @ignore_extra_bytes
88
+ end
89
+
90
+ def _properties # :nodoc:
91
+ {
92
+ "support_ewkb" => @support_ewkb,
93
+ "support_wkb12" => @support_wkb12,
94
+ "ignore_extra_bytes" => @ignore_extra_bytes,
95
+ "default_srid" => @default_srid
96
+ }
97
+ end
98
+
99
+ # Parse the given binary data or hexadecimal string, and return a
100
+ # geometry object.
101
+ #
102
+ # The #parse_hex method is a synonym, present for historical
103
+ # reasons but deprecated. Use #parse instead.
104
+
105
+ def parse(data_)
106
+ data_ = [data_].pack("H*") if data_[0, 1] =~ /[0-9a-fA-F]/
107
+ @cur_has_z = nil
108
+ @cur_has_m = nil
109
+ @cur_srid = nil
110
+ @cur_dims = 2
111
+ @cur_factory = nil
112
+ begin
113
+ _start_scanner(data_)
114
+ obj_ = _parse_object(false)
115
+ unless @ignore_extra_bytes
116
+ bytes_ = _bytes_remaining
117
+ if bytes_ > 0
118
+ raise Error::ParseError, "Found #{bytes_} extra bytes at the end of the stream."
119
+ end
120
+ end
121
+ ensure
122
+ _clean_scanner
123
+ end
124
+ obj_
125
+ end
126
+ alias_method :parse_hex, :parse
127
+
128
+ def _parse_object(contained_) # :nodoc:
129
+ endian_value_ = _get_byte
130
+ case endian_value_
131
+ when 0
132
+ little_endian_ = false
133
+ when 1
134
+ little_endian_ = true
135
+ else
136
+ raise Error::ParseError, "Bad endian byte value: #{endian_value_}"
137
+ end
138
+ type_code_ = _get_integer(little_endian_)
139
+ has_z_ = false
140
+ has_m_ = false
141
+ srid_ = contained_ ? nil : @default_srid
142
+ if @support_ewkb
143
+ has_z_ ||= type_code_ & 0x80000000 != 0
144
+ has_m_ ||= type_code_ & 0x40000000 != 0
145
+ srid_ = _get_integer(little_endian_) if type_code_ & 0x20000000 != 0
146
+ type_code_ &= 0x0fffffff
147
+ end
148
+ if @support_wkb12
149
+ has_z_ ||= (type_code_ / 1000) & 1 != 0
150
+ has_m_ ||= (type_code_ / 1000) & 2 != 0
151
+ type_code_ %= 1000
152
+ end
153
+ if contained_
154
+ if contained_ != true && contained_ != type_code_
155
+ raise Error::ParseError, "Enclosed type=#{type_code_} is different from container constraint #{contained_}"
156
+ end
157
+ if has_z_ != @cur_has_z
158
+ raise Error::ParseError, "Enclosed hasZ=#{has_z_} is different from toplevel hasZ=#{@cur_has_z}"
159
+ end
160
+ if has_m_ != @cur_has_m
161
+ raise Error::ParseError, "Enclosed hasM=#{has_m_} is different from toplevel hasM=#{@cur_has_m}"
162
+ end
163
+ if srid_ && srid_ != @cur_srid
164
+ raise Error::ParseError, "Enclosed SRID #{srid_} is different from toplevel srid #{@cur_srid || '(unspecified)'}"
165
+ end
166
+ else
167
+ @cur_has_z = has_z_
168
+ @cur_has_m = has_m_
169
+ @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0)
170
+ @cur_srid = srid_
171
+ @cur_factory = @factory_generator.call(srid: @cur_srid, has_z_coordinate: has_z_, has_m_coordinate: has_m_)
172
+ if @cur_has_z && !@cur_factory.property(:has_z_coordinate)
173
+ raise Error::ParseError, "Data has Z coordinates but the factory doesn't have Z coordinates"
174
+ end
175
+ if @cur_has_m && !@cur_factory.property(:has_m_coordinate)
176
+ raise Error::ParseError, "Data has M coordinates but the factory doesn't have M coordinates"
177
+ end
178
+ end
179
+ case type_code_
180
+ when 1
181
+ coords_ = _get_doubles(little_endian_, @cur_dims)
182
+ @cur_factory.point(*coords_)
183
+ when 2
184
+ _parse_line_string(little_endian_)
185
+ when 3
186
+ interior_rings_ = (1.._get_integer(little_endian_)).map { _parse_line_string(little_endian_) }
187
+ exterior_ring_ = interior_rings_.shift || @cur_factory.linear_ring([])
188
+ @cur_factory.polygon(exterior_ring_, interior_rings_)
189
+ when 4
190
+ @cur_factory.multi_point((1.._get_integer(little_endian_)).map { _parse_object(1) })
191
+ when 5
192
+ @cur_factory.multi_line_string((1.._get_integer(little_endian_)).map { _parse_object(2) })
193
+ when 6
194
+ @cur_factory.multi_polygon((1.._get_integer(little_endian_)).map { _parse_object(3) })
195
+ when 7
196
+ @cur_factory.collection((1.._get_integer(little_endian_)).map { _parse_object(true) })
197
+ else
198
+ raise Error::ParseError, "Unknown type value: #{type_code_}."
199
+ end
200
+ end
201
+
202
+ def _parse_line_string(little_endian_) # :nodoc:
203
+ count_ = _get_integer(little_endian_)
204
+ coords_ = _get_doubles(little_endian_, @cur_dims * count_)
205
+ @cur_factory.line_string((0...count_).map { |i_| @cur_factory.point(*coords_[@cur_dims * i_, @cur_dims]) })
206
+ end
207
+
208
+ def _start_scanner(data_) # :nodoc:
209
+ @_data = data_
210
+ @_len = data_.length
211
+ @_pos = 0
212
+ end
213
+
214
+ def _clean_scanner # :nodoc:
215
+ @_data = nil
216
+ end
217
+
218
+ def _bytes_remaining # :nodoc:
219
+ @_len - @_pos
220
+ end
221
+
222
+ def _get_byte # :nodoc:
223
+ if @_pos + 1 > @_len
224
+ raise Error::ParseError, "Not enough bytes left to fulfill 1 byte"
225
+ end
226
+ str_ = @_data[@_pos, 1]
227
+ @_pos += 1
228
+ str_.unpack("C").first
229
+ end
230
+
231
+ def _get_integer(little_endian_) # :nodoc:
232
+ if @_pos + 4 > @_len
233
+ raise Error::ParseError, "Not enough bytes left to fulfill 1 integer"
234
+ end
235
+ str_ = @_data[@_pos, 4]
236
+ @_pos += 4
237
+ str_.unpack("#{little_endian_ ? 'V' : 'N'}").first
238
+ end
239
+
240
+ def _get_doubles(little_endian_, count_) # :nodoc:
241
+ len_ = 8 * count_
242
+ if @_pos + len_ > @_len
243
+ raise Error::ParseError, "Not enough bytes left to fulfill #{count_} doubles"
244
+ end
245
+ str_ = @_data[@_pos, len_]
246
+ @_pos += len_
247
+ str_.unpack("#{little_endian_ ? 'E' : 'G'}*")
248
+ end
249
+ end
250
+ end
251
+ end