Class: RGeo::WKRep::WKBParser
- Inherits:
-
Object
- Object
- RGeo::WKRep::WKBParser
- Defined in:
- lib/rgeo/wkrep/wkb_parser.rb
Overview
This class provides the functionality of parsing a geometry from WKB (well-known binary) format. You may also customize the parser to recognize PostGIS EWKB extensions to the input, or Simple Features Specification 1.2 extensions for Z and M coordinates.
To use this class, create an instance with the desired settings and customizations, and call the parse method.
Configuration options
You must provide each parser with an RGeo::Feature::FactoryGenerator. It should understand the configuration options :srid
, :has_z_coordinate
, and :has_m_coordinate
. You may also pass a specific RGeo::Feature::Factory, or nil to specify the default Cartesian FactoryGenerator.
The following additional options are recognized. These can be passed to the constructor, or set on the object afterwards.
:support_ewkb
-
Activate support for PostGIS EWKB type codes, which use high order bits in the type code to signal the presence of Z, M, and SRID values in the data. Default is false.
:support_wkb12
-
Activate support for SFS 1.2 extensions to the type codes, which use values greater than 1000 to signal the presence of Z and M values in the data. SFS 1.2 types such as triangle, tin, and polyhedralsurface are NOT yet supported. Default is false.
:ignore_extra_bytes
-
If true, extra bytes at the end of the data are ignored. If false (the default), extra bytes will trigger a parse error.
:default_srid
-
A SRID to pass to the factory generator if no SRID is present in the input. Defaults to nil (i.e. don’t specify a SRID).
Instance Method Summary collapse
-
#_bytes_remaining ⇒ Object
:nodoc:.
-
#_clean_scanner ⇒ Object
:nodoc:.
-
#_get_byte ⇒ Object
:nodoc:.
-
#_get_doubles(little_endian_, count_) ⇒ Object
:nodoc:.
-
#_get_integer(little_endian_) ⇒ Object
:nodoc:.
-
#_parse_line_string(little_endian_) ⇒ Object
:nodoc:.
-
#_parse_object(contained_) ⇒ Object
:nodoc:.
-
#_start_scanner(data_) ⇒ Object
:nodoc:.
-
#exact_factory ⇒ Object
If this parser was given an exact factory, returns it; otherwise returns nil.
-
#factory_generator ⇒ Object
Returns the factory generator.
-
#factory_generator=(value_) ⇒ Object
Sets the factory_generator.
-
#ignore_extra_bytes=(value_) ⇒ Object
Sets the the ignore_extra_bytes flag.
-
#ignore_extra_bytes? ⇒ Boolean
Returns true if this parser ignores extra bytes.
-
#initialize(factory_generator_ = nil, opts_ = {}) ⇒ WKBParser
constructor
Create and configure a WKB parser.
-
#parse(data_) ⇒ Object
Parse the given binary data, and return a geometry object.
-
#parse_hex(str_) ⇒ Object
Parse the given hex string, and return a geometry object.
-
#support_ewkb=(value_) ⇒ Object
Sets the the support_ewkb flag.
-
#support_ewkb? ⇒ Boolean
Returns true if this parser supports EWKB.
-
#support_wkb12=(value_) ⇒ Object
Sets the the support_wkb12 flag.
-
#support_wkb12? ⇒ Boolean
Returns true if this parser supports SFS 1.2 extensions.
-
#to_generate_factory(&block_) ⇒ Object
Sets the factory_generator to the given block.
Constructor Details
#initialize(factory_generator_ = nil, opts_ = {}) ⇒ WKBParser
Create and configure a WKB parser. See the WKBParser documentation for the options that can be passed.
83 84 85 86 87 88 89 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 83 def initialize(factory_generator_=nil, opts_={}) self.factory_generator = factory_generator_ @support_ewkb = opts_[:support_ewkb] ? true : false @support_wkb12 = opts_[:support_wkb12] ? true : false @ignore_extra_bytes = opts_[:ignore_extra_bytes] ? true : false @default_srid = opts_[:default_srid] end |
Instance Method Details
#_bytes_remaining ⇒ Object
:nodoc:
274 275 276 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 274 def _bytes_remaining # :nodoc: @_len - @_pos end |
#_clean_scanner ⇒ Object
:nodoc:
269 270 271 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 269 def _clean_scanner # :nodoc: @_data = nil end |
#_get_byte ⇒ Object
:nodoc:
279 280 281 282 283 284 285 286 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 279 def _get_byte # :nodoc: if @_pos + 1 > @_len raise Error::ParseError, "Not enough bytes left to fulfill 1 byte" end str_ = @_data[@_pos, 1] @_pos += 1 str_.unpack("C").first end |
#_get_doubles(little_endian_, count_) ⇒ Object
:nodoc:
299 300 301 302 303 304 305 306 307 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 299 def _get_doubles(little_endian_, count_) # :nodoc: len_ = 8 * count_ if @_pos + len_ > @_len raise Error::ParseError, "Not enough bytes left to fulfill #{count_} doubles" end str_ = @_data[@_pos, len_] @_pos += len_ str_.unpack("#{little_endian_ ? 'E' : 'G'}*") end |
#_get_integer(little_endian_) ⇒ Object
:nodoc:
289 290 291 292 293 294 295 296 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 289 def _get_integer(little_endian_) # :nodoc: if @_pos + 4 > @_len raise Error::ParseError, "Not enough bytes left to fulfill 1 integer" end str_ = @_data[@_pos, 4] @_pos += 4 str_.unpack("#{little_endian_ ? 'V' : 'N'}").first end |
#_parse_line_string(little_endian_) ⇒ Object
:nodoc:
255 256 257 258 259 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 255 def _parse_line_string(little_endian_) # :nodoc: count_ = _get_integer(little_endian_) coords_ = _get_doubles(little_endian_, @cur_dims * count_) @cur_factory.line_string((0...count_).map{ |i_| @cur_factory.point(*coords_[@cur_dims*i_,@cur_dims]) }) end |
#_parse_object(contained_) ⇒ Object
:nodoc:
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 188 def _parse_object(contained_) # :nodoc: little_endian_ = _get_byte == 1 type_code_ = _get_integer(little_endian_) has_z_ = false has_m_ = false srid_ = contained_ ? nil : @default_srid if @support_ewkb has_z_ ||= type_code_ & 0x80000000 != 0 has_m_ ||= type_code_ & 0x40000000 != 0 srid_ = _get_integer(little_endian_) if type_code_ & 0x20000000 != 0 type_code_ &= 0x0fffffff end if @support_wkb12 has_z_ ||= (type_code_ / 1000) & 1 != 0 has_m_ ||= (type_code_ / 1000) & 2 != 0 type_code_ %= 1000 end if contained_ if contained_ != true && contained_ != type_code_ raise Error::ParseError, "Enclosed type=#{type_code_} is different from container constraint #{contained_}" end if has_z_ != @cur_has_z raise Error::ParseError, "Enclosed hasZ=#{has_z_} is different from toplevel hasZ=#{@cur_has_z}" end if has_m_ != @cur_has_m raise Error::ParseError, "Enclosed hasM=#{has_m_} is different from toplevel hasM=#{@cur_has_m}" end if srid_ && srid_ != @cur_srid raise Error::ParseError, "Enclosed SRID #{srid_} is different from toplevel srid #{@cur_srid || '(unspecified)'}" end else @cur_has_z = has_z_ @cur_has_m = has_m_ @cur_dims = 2 + (@cur_has_z ? 1 : 0) + (@cur_has_m ? 1 : 0) @cur_srid = srid_ @cur_factory = @factory_generator.call(:srid => @cur_srid, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_) if @cur_has_z && !@cur_factory.property(:has_z_coordinate) raise Error::ParseError, "Data has Z coordinates but the factory doesn't have Z coordinates" end if @cur_has_m && !@cur_factory.property(:has_m_coordinate) raise Error::ParseError, "Data has M coordinates but the factory doesn't have M coordinates" end end case type_code_ when 1 coords_ = _get_doubles(little_endian_, @cur_dims) @cur_factory.point(*coords_) when 2 _parse_line_string(little_endian_) when 3 interior_rings_ = (1.._get_integer(little_endian_)).map{ _parse_line_string(little_endian_) } exterior_ring_ = interior_rings_.shift || @cur_factory.linear_ring([]) @cur_factory.polygon(exterior_ring_, interior_rings_) when 4 @cur_factory.multi_point((1.._get_integer(little_endian_)).map{ _parse_object(1) }) when 5 @cur_factory.multi_line_string((1.._get_integer(little_endian_)).map{ _parse_object(2) }) when 6 @cur_factory.multi_polygon((1.._get_integer(little_endian_)).map{ _parse_object(3) }) when 7 @cur_factory.collection((1.._get_integer(little_endian_)).map{ _parse_object(true) }) else raise Error::ParseError, "Unknown type value: #{type_code_}." end end |
#_start_scanner(data_) ⇒ Object
:nodoc:
262 263 264 265 266 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 262 def _start_scanner(data_) # :nodoc: @_data = data_ @_len = data_.length @_pos = 0 end |
#exact_factory ⇒ Object
If this parser was given an exact factory, returns it; otherwise returns nil.
99 100 101 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 99 def exact_factory @exact_factory end |
#factory_generator ⇒ Object
Returns the factory generator. See WKBParser for details.
93 94 95 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 93 def factory_generator @factory_generator end |
#factory_generator=(value_) ⇒ Object
Sets the factory_generator. See WKBParser for details.
104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 104 def factory_generator=(value_) if value_.kind_of?(Feature::Factory::Instance) @factory_generator = Feature::FactoryGenerator.single(value_) @exact_factory = value_ elsif value_.respond_to?(:call) @factory_generator = value_ @exact_factory = nil else @factory_generator = Cartesian.method(:preferred_factory) @exact_factory = nil end end |
#ignore_extra_bytes=(value_) ⇒ Object
Sets the the ignore_extra_bytes flag. See WKBParser for details.
152 153 154 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 152 def ignore_extra_bytes=(value_) @ignore_extra_bytes = value_ ? true : false end |
#ignore_extra_bytes? ⇒ Boolean
Returns true if this parser ignores extra bytes. See WKBParser for details.
147 148 149 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 147 def ignore_extra_bytes? @ignore_extra_bytes end |
#parse(data_) ⇒ Object
Parse the given binary data, and return a geometry object.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 166 def parse(data_) @cur_has_z = nil @cur_has_m = nil @cur_srid = nil @cur_dims = 2 @cur_factory = nil begin _start_scanner(data_) obj_ = _parse_object(false) unless @ignore_extra_bytes bytes_ = _bytes_remaining if bytes_ > 0 raise Error::ParseError, "Found #{bytes_} extra bytes at the end of the stream." end end ensure _clean_scanner end obj_ end |
#parse_hex(str_) ⇒ Object
Parse the given hex string, and return a geometry object.
159 160 161 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 159 def parse_hex(str_) parse([str_].pack('H*')) end |
#support_ewkb=(value_) ⇒ Object
Sets the the support_ewkb flag. See WKBParser for details.
130 131 132 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 130 def support_ewkb=(value_) @support_ewkb = value_ ? true : false end |
#support_ewkb? ⇒ Boolean
Returns true if this parser supports EWKB. See WKBParser for details.
125 126 127 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 125 def support_ewkb? @support_ewkb end |
#support_wkb12=(value_) ⇒ Object
Sets the the support_wkb12 flag. See WKBParser for details.
141 142 143 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 141 def support_wkb12=(value_) @support_wkb12 = value_ ? true : false end |
#support_wkb12? ⇒ Boolean
Returns true if this parser supports SFS 1.2 extensions. See WKBParser for details.
136 137 138 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 136 def support_wkb12? @support_wkb12 end |
#to_generate_factory(&block_) ⇒ Object
Sets the factory_generator to the given block. See WKBParser for details.
119 120 121 |
# File 'lib/rgeo/wkrep/wkb_parser.rb', line 119 def to_generate_factory(&block_) @factory_generator = block_ end |