Class: ActiveRecord::ConnectionAdapters::SpatiaLiteAdapter::NativeFormatParser

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb

Overview

A utility class that parses the native (internal) SpatiaLite format. This is used to read and return an attribute value as an RGeo object.

Instance Method Summary collapse

Constructor Details

#initialize(factory_) ⇒ NativeFormatParser

Create a parser that generates features using the given factory.



53
54
55
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 53

def initialize(factory_)
  @factory = factory_
end

Instance Method Details

#_clean_scannerObject

:nodoc:



119
120
121
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 119

def _clean_scanner  # :nodoc:
  @_data = nil
end

#_get_byte(expect_ = nil) ⇒ Object

:nodoc:



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 124

def _get_byte(expect_=nil)  # :nodoc:
  if @_pos + 1 > @_len
    raise ::RGeo::Error::ParseError, "Not enough bytes left to fulfill 1 byte"
  end
  str_ = @_data[@_pos, 1]
  @_pos += 1
  val_ = str_.unpack("C").first
  if expect_ && expect_ != val_
    raise ::RGeo::Error::ParseError, "Expected byte 0x#{expect_.to_s(16)} but got 0x#{val_.to_s(16)}"
  end
  val_
end

#_get_doubles(count_) ⇒ Object

:nodoc:



148
149
150
151
152
153
154
155
156
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 148

def _get_doubles(count_)  # :nodoc:
  len_ = 8 * count_
  if @_pos + len_ > @_len
    raise ::RGeo::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_integerObject

:nodoc:



138
139
140
141
142
143
144
145
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 138

def _get_integer  # :nodoc:
  if @_pos + 4 > @_len
    raise ::RGeo::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_stringObject

:nodoc:



105
106
107
108
109
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 105

def _parse_line_string  # :nodoc:
  count_ = _get_integer
  coords_ = _get_doubles(2 * count_)
  @factory.line_string((0...count_).map{ |i_| @factory.point(*coords_[2*i_,2]) })
end

#_parse_object(contained_) ⇒ Object

:nodoc:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 78

def _parse_object(contained_)  # :nodoc:
  _get_byte(contained_ ? 0x69 : 0x7c)
  type_code_ = _get_integer
  case type_code_
  when 1
    coords_ = _get_doubles(2)
    @factory.point(*coords_)
  when 2
    _parse_line_string
  when 3
    interior_rings_ = (1.._get_integer).map{ _parse_line_string }
    exterior_ring_ = interior_rings_.shift || @factory.linear_ring([])
    @factory.polygon(exterior_ring_, interior_rings_)
  when 4
    @factory.multi_point((1.._get_integer).map{ _parse_object(1) })
  when 5
    @factory.multi_line_string((1.._get_integer).map{ _parse_object(2) })
  when 6
    @factory.multi_polygon((1.._get_integer).map{ _parse_object(3) })
  when 7
    @factory.collection((1.._get_integer).map{ _parse_object(true) })
  else
    raise ::RGeo::Error::ParseError, "Unknown type value: #{type_code_}."
  end
end

#_start_scanner(data_) ⇒ Object

:nodoc:



112
113
114
115
116
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 112

def _start_scanner(data_)  # :nodoc:
  @_data = data_
  @_len = data_.length
  @_pos = 38
end

#parse(data_) ⇒ Object

Parse the given binary data and return an object. Raises ::RGeo::Error::ParseError on failure.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/active_record/connection_adapters/spatialite_adapter/native_format_parser.rb', line 61

def parse(data_)
  if data_[0,1] =~ /[0-9a-fA-F]/
    data_ = [data_].pack('H*')
  end
  @little_endian = data_[1,1] == "\x01"
  srid_ = data_[2,4].unpack(@little_endian ? 'V' : 'N').first
  begin
    _start_scanner(data_)
    obj_ = _parse_object(false)
    _get_byte(0xfe)
  ensure
    _clean_scanner
  end
  obj_
end