ruby-mapsource 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="ruby-1.9.3-p0@mapsource_gdb"
8
+
9
+ #
10
+ # Uncomment following line if you want options to be set only for given project.
11
+ #
12
+ # PROJECT_JRUBY_OPTS=( --1.9 )
13
+ #
14
+ # The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
15
+ #
16
+ # chmod +x ${rvm_path}/hooks/after_use_jruby_opts
17
+ #
18
+
19
+ #
20
+ # First we attempt to load the desired environment directly from the environment
21
+ # file. This is very fast and efficient compared to running through the entire
22
+ # CLI and selector. If you want feedback on which environment was used then
23
+ # insert the word 'use' after --create as this triggers verbose mode.
24
+ #
25
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
26
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
27
+ then
28
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
29
+
30
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
31
+ then
32
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
33
+ fi
34
+ else
35
+ # If the environment file has not yet been created, use the RVM CLI to select.
36
+ if ! rvm --create use "$environment_id"
37
+ then
38
+ echo "Failed to create RVM environment '${environment_id}'."
39
+ return 1
40
+ fi
41
+ fi
42
+
43
+ #
44
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
45
+ # it be automatically loaded. Uncomment the following and adjust the filename if
46
+ # necessary.
47
+ #
48
+ # filename=".gems"
49
+ # if [[ -s "$filename" ]]
50
+ # then
51
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
52
+ # fi
53
+
54
+ # If you use bundler, this might be useful to you:
55
+ # if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
56
+ # then
57
+ # printf "The rubygem 'bundler' is not installed. Installing it now.\n"
58
+ # gem install bundler
59
+ # fi
60
+ # if [[ -s Gemfile ]] && command -v bundle
61
+ # then
62
+ # bundle install
63
+ # fi
64
+
65
+ if [[ $- == *i* ]] # check for interactive shells
66
+ then
67
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
68
+ else
69
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
70
+ fi
71
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'guard-minitest', '~> 0.4'
7
+ gem 'rb-fsevent', '~> 0.4'
8
+ end
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'minitest' do
5
+ # with Minitest::Spec
6
+ watch(%r|^spec/(.*)_spec\.rb|)
7
+ watch(%r|^lib/mapsource/(.*)\.rb|) { |m| "spec/integration/#{m[1]}_spec.rb" }
8
+ watch(%r|^lib/mapsource/(.*)\.rb|) { |m| "spec/unit/#{m[1]}_spec.rb" }
9
+ watch(%r|^spec/spec_helper\.rb|) { "spec" }
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Vitor Peres Capela Pereira
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ ruby-mapsource is a library that allows ruby programs to read files created by Garmin's MapSource and BaseCamp.
2
+
3
+ # Usage
4
+
5
+ TODO
6
+
7
+ # Tools
8
+
9
+ TODO:
10
+
11
+ - gdbtokml - makes a kml file from a gdb
12
+ - gdbtocsv - creates a csv file from a gdb
13
+
14
+ # Thanks
15
+
16
+ GPSBabel
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push 'spec'
6
+ t.pattern = 'spec/**/*_spec.rb'
7
+ end
@@ -0,0 +1,44 @@
1
+ module MapSource
2
+ # Public: A Color attributed to a Track, Route or Waypoint.
3
+ class Color
4
+ attr_reader :name, :r, :g, :b
5
+
6
+ def initialize(name, r=-1, g=-1, b=-1)
7
+ @name = name
8
+ @r = @r
9
+ @g = @g
10
+ @b = @b
11
+ end
12
+
13
+ # Public: Converts GDB color index to color object.
14
+ #
15
+ # idx - GDB index
16
+ #
17
+ # Returns Color corresponding to index.
18
+ def self.from_index(idx)
19
+ COLORS[idx] || COLORS.first
20
+ end
21
+ end
22
+
23
+ # Public: A set of default colors.
24
+ COLORS = [
25
+ Color.new('Unknown'),
26
+ Color.new('Black', 0, 0, 0),
27
+ Color.new('DarkRed', 139, 0, 0),
28
+ Color.new('DarkGreen', 0, 100, 0),
29
+ Color.new('DarkYellow', 139, 139, 0),
30
+ Color.new('DarkBlue', 0, 0, 139),
31
+ Color.new('DarkMagenta', 139, 0, 139),
32
+ Color.new('DarkCyan', 0, 139, 139),
33
+ Color.new('LightGray', 211, 211, 211),
34
+ Color.new('DarkGray', 169, 169, 169),
35
+ Color.new('Red', 255, 0, 0),
36
+ Color.new('Green', 0, 255, 0),
37
+ Color.new('Yellow', 255, 255, 0),
38
+ Color.new('Blue', 0, 0, 255),
39
+ Color.new('Magenta', 255, 0, 255),
40
+ Color.new('Cyan', 0, 255, 255),
41
+ Color.new('White', 255, 255, 255),
42
+ Color.new('Transparent')
43
+ ]
44
+ end
@@ -0,0 +1,248 @@
1
+ module MapSource
2
+ class InvalidFormatError < StandardError; end
3
+ class UnsupportedVersionError < StandardError; end
4
+
5
+ # Public: Parses GDB files and extracts waypoints, tracks and routes.
6
+ #
7
+ # Examples:
8
+ #
9
+ # reader = MapSource::Reader.new(open('around_the_world.gdb'))
10
+ # reader.waypoints
11
+ # # => [MapSource::Waypoint<...>, ...]
12
+ class Reader
13
+ # Public: Range of format versions supported.
14
+ SUPPORTED_VERSIONS = (1..3)
15
+
16
+ attr_reader :header, :waypoints
17
+
18
+ # Public: Creates a Reader.
19
+ #
20
+ # gdb - An IO object pointing to a GDB.
21
+ def initialize(gdb)
22
+ @gdb = gdb
23
+ @header = read_header
24
+
25
+ @parsed = false
26
+ end
27
+
28
+ # Public: Read waypoints from file.
29
+ #
30
+ # Returns a list of waypoints.
31
+ def waypoints
32
+ read_data
33
+ @waypoints
34
+ end
35
+
36
+ def tracks
37
+ read_data
38
+ @tracks
39
+ end
40
+
41
+ private
42
+ # Internal: Reads data from the GDB file.
43
+ #
44
+ # Returns list of waypoints, list of tracks, list of routes.
45
+ def read_data
46
+ return if @parsed
47
+
48
+ @waypoints = []
49
+ @tracks = []
50
+ @routes = []
51
+
52
+ while true
53
+ len = @gdb.read(4).unpack('l').shift
54
+ record = @gdb.read(len + 1)
55
+
56
+ case record
57
+ when /^W/
58
+ @waypoints << read_waypoint(record)
59
+ when /^T/
60
+ @tracks << read_track(record)
61
+ when /^V/
62
+ break
63
+ else
64
+ end
65
+ end
66
+
67
+ @parsed = true
68
+ end
69
+
70
+ # Internal: Converts coordinates in semicircles to degrees.
71
+ #
72
+ # v - coordinate as semicircle
73
+ #
74
+ # Returns coordinate in degrees.
75
+ def semicircle_to_degrees(v)
76
+ (v.to_f / (1 << 31)) * 180.0
77
+ end
78
+
79
+ # Internal: Reads a waypoint record from the GDB.
80
+ #
81
+ # record - a binary string containing waypoint data.
82
+ #
83
+ # Returns waypoint.
84
+ def read_waypoint(record)
85
+ io = StringIO.new(record)
86
+
87
+ read_char io
88
+ shortname = read_string(io)
89
+ wptclass = read_int(io)
90
+
91
+ read_string(io)
92
+ io.read 22 # skip 22 bytes
93
+
94
+ lat = semicircle_to_degrees(read_int(io))
95
+ lon = semicircle_to_degrees(read_int(io))
96
+
97
+ wpt = Waypoint.new(lat, lon)
98
+ wpt.shortname = shortname
99
+
100
+ if read_char(io) == 1
101
+ alt = read_double(io)
102
+
103
+ wpt.altitude = alt if alt < 1.0e24
104
+ end
105
+
106
+ wpt.notes = read_string(io)
107
+ wpt.proximity = read_double(io) if read_char(io) == 1
108
+
109
+ read_int io # display
110
+ read_int io # color, not implemented
111
+
112
+ wpt.icon = read_int(io)
113
+ wpt.city = read_string(io)
114
+ wpt.state = read_string(io)
115
+ wpt.facility = read_string(io)
116
+
117
+ wpt.depth = read_double(io) if read_char(io) == 1
118
+
119
+ wpt
120
+ end
121
+
122
+ def read_track(record)
123
+ header = record.unpack('AZ*all')
124
+ _, name, _, color, npoints = *header
125
+ contents = record.sub(/^#{Regexp.quote(header.pack('AZ*all'))}/, '')
126
+
127
+ track = Track.new(name, Color::from_index(color))
128
+ io = StringIO.new(contents)
129
+
130
+ 0.upto(npoints - 1) do
131
+ lat = semicircle_to_degrees(read_int(io))
132
+ lon = semicircle_to_degrees(read_int(io))
133
+
134
+ wpt = Waypoint.new(lat, lon)
135
+
136
+ if read_char(io) == 1
137
+ alt = read_double(io)
138
+ wpt.altitude = alt if alt < 1.0e24
139
+ end
140
+
141
+ wpt.creation_time = read_int(io) if read_char(io) == 1
142
+ wpt.depth = read_double(io) if read_char(io) == 1
143
+ wpt.temperature = read_double(io) if read_char(io) == 1
144
+
145
+ track.add_waypoint wpt
146
+ end
147
+
148
+ track
149
+ end
150
+
151
+ # Internal: Reads a string from an IO object.
152
+ #
153
+ # io - an IO object
154
+ # chars - number of chars to read. If not specified, read_string stops at
155
+ # the null string terminator.
156
+ #
157
+ # Returns a string.
158
+ def read_string(io, chars=nil)
159
+ if chars
160
+ io.read chars
161
+ else
162
+ str = ''
163
+ while c = io.read(1)
164
+ break if c == "\x00"
165
+ str += c
166
+ end
167
+
168
+ str
169
+ end
170
+ end
171
+
172
+ # Internal: Reads an Integer from an IO object, unpacking it appropriately.
173
+ #
174
+ # io - an IO object.
175
+ #
176
+ # Returns an Integer.
177
+ def read_int(io)
178
+ io.read(4).unpack('l').shift
179
+ end
180
+
181
+ # Internal: Reads a Double from an IO object, unpacking it appropriately.
182
+ #
183
+ # io - an IO object.
184
+ #
185
+ # Returns an Double.
186
+ def read_double(io)
187
+ io.read(8).unpack('E').shift
188
+ end
189
+
190
+ # Internal: Reads a single character from an IO object, unpacking it
191
+ # appropriately.
192
+ #
193
+ # io - an IO object.
194
+ #
195
+ # Returns a single character.
196
+ def read_char(io)
197
+ io.read(1).unpack('c').shift
198
+ end
199
+
200
+ # Internal: Reads a GDB's header to determine the version being parsed, its creator
201
+ # and signer.
202
+ #
203
+ # Returns a properly filled header.
204
+ # Raises MapSource::InvalidFormatError if it's not a GDB file.
205
+ # Raises MapSource::InvalidFormatError if GDB is malformed.
206
+ # Raises MapSource::UnsupportedVersionError if file format version is not supported.
207
+ def read_header
208
+ header = Header.new
209
+
210
+ mscrf = @gdb.read(6).unpack('A*').shift
211
+
212
+ raise InvalidFormatError, "Invalid gdb file" if mscrf != 'MsRcf'
213
+
214
+ record_length = @gdb.read(4).unpack('l').shift
215
+ buffer = @gdb.read(record_length + 1)
216
+
217
+ raise InvalidFormatError, "Invalid gdb file" if buffer[0] != ?D
218
+ gdb_version = buffer[1].getbyte(0) - ?k.getbyte(0) + 1
219
+
220
+ raise UnsupportedVersionError, "Unsupported version: #{gdb_version}. Supported versions are #{SUPPORTED_VERSIONS.to_a.join(', ')}" if !SUPPORTED_VERSIONS.member?(gdb_version)
221
+
222
+ header.version = gdb_version
223
+
224
+ record_length = @gdb.read(4).unpack('l').shift
225
+ buffer = @gdb.read(record_length + 1)
226
+ creator = buffer.unpack('Z*').shift
227
+
228
+ header.created_by = if creator =~ /SQA$/
229
+ 'MapSource'
230
+ elsif creator =~ /neaderhi$/
231
+ 'MapSource BETA'
232
+ end
233
+
234
+ signer = @gdb.read(10)
235
+ signer += @gdb.read(1) until signer =~ /\x00$/
236
+
237
+ signer = signer.unpack('Z*').shift
238
+
239
+ if signer !~ /MapSource|BaseCamp/
240
+ raise InvalidFormatError, "Unknown file signature: #{signer}"
241
+ end
242
+
243
+ header.signed_by = signer
244
+
245
+ header
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,45 @@
1
+ # Defines the structure
2
+ module MapSource
3
+ # Public: GDB header. Contains name of creator software, signature and
4
+ # format version.
5
+ class Header
6
+ attr_accessor :created_by, :signed_by, :version
7
+ end
8
+
9
+ # Public: A Waypoint.
10
+ class Waypoint
11
+ attr_accessor :shortname, :latitude, :longitude, :altitude, :temperature, :depth, :notes, :creation_time, :proximity, :icon, :city, :state, :facility
12
+
13
+ def initialize(latitude, longitude)
14
+ @latitude = latitude
15
+ @longitude = longitude
16
+ end
17
+ end
18
+
19
+ # Public: A Track.
20
+ class Track
21
+ attr_reader :name, :color
22
+
23
+ def initialize(name, color)
24
+ @name = name
25
+ @color = color
26
+ @waypoints = []
27
+ end
28
+
29
+ def waypoints
30
+ @waypoints.dup
31
+ end
32
+
33
+ def add_waypoint(wpt)
34
+ @waypoints << wpt
35
+ end
36
+
37
+ def size
38
+ @waypoints.size
39
+ end
40
+
41
+ def each
42
+ @waypoints.each { |wpt| yield wpt if block_given? }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module MapSource
2
+ VERSION = "0.0.1"
3
+ end
data/lib/mapsource.rb ADDED
@@ -0,0 +1,30 @@
1
+ #--
2
+ # Copyright (C) 2012 Vitor Peres Capela Pereira
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ # of the Software, and to permit persons to whom the Software is furnished to do
9
+ # so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #++
22
+
23
+ mapsource_path = File.dirname(__FILE__)
24
+ $:.unshift(mapsource_path) if File.directory?(mapsource_path) && !$:.include?(mapsource_path)
25
+
26
+ require 'stringio'
27
+
28
+ require 'mapsource/structure'
29
+ require 'mapsource/defs'
30
+ require 'mapsource/reader'
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mapsource/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ruby-mapsource"
7
+ s.version = MapSource::VERSION
8
+ s.authors = ["Vitor Capela"]
9
+ s.email = ["dodecaphonic@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{A Ruby library for reading MapSource/BaseCamp-created GDB files}
12
+ s.description = %q{A Ruby library for reading MapSource/BaseCamp-created GDB files}
13
+
14
+ s.rubyforge_project = "ruby-mapsource"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "minitest", "~> 2.10"
22
+ s.add_development_dependency "mocha", "~> 0.10"
23
+ end
Binary file
Binary file
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe MapSource::Reader do
4
+ before :each do
5
+ @gdb_file = open(File.dirname(__FILE__) + '/../assets/sample.gdb')
6
+ @reader = MapSource::Reader.new(@gdb_file)
7
+ end
8
+
9
+ after :each do
10
+ @gdb_file.close
11
+ end
12
+
13
+ it "parses the header correctly" do
14
+ @reader.header.created_by.must_equal 'MapSource'
15
+ @reader.header.signed_by.must_equal 'MapSource'
16
+ end
17
+
18
+ it "parses all waypoints" do
19
+ @reader.waypoints.size.must_equal 312
20
+ end
21
+
22
+ it "parses every track" do
23
+ tracks = @reader.tracks
24
+ al = tracks.find { |t| t.name == 'ACTIVE LOG 009' }
25
+ al.wont_be_nil
26
+ al.size.must_equal 296
27
+
28
+ wpt = al.waypoints[9]
29
+ wpt.latitude.must_be_close_to -23.17172
30
+ wpt.longitude.must_be_close_to -44.83632
31
+ wpt.altitude.must_be_close_to 1471, 0.05
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ $: << File.dirname(__FILE__) + '/../lib'
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+ require 'mocha'
6
+
7
+ require 'mapsource'
8
+
9
+ module MapSource::Spec
10
+ # Internal: Creates a basic mock object containing expectations for a part of
11
+ # (or all of) the header. How much is created is determined by the _options_
12
+ # hash.
13
+ #
14
+ # options - a hash of options. Currently supports ":before" and the following
15
+ # states:
16
+ # - :start - before expecting on the first string in the file
17
+ # - :version - before expectations regarding the version
18
+ # - :creator - before expectations regarding the creator of the file
19
+ # - :signer - before expectations regarding the signer of the file
20
+ #
21
+ # Returns an array with a mocha mock object and a mocha sequence object.
22
+ def create_basic_valid_state(options={ before: nil })
23
+ gdb_file = mock('gdb')
24
+ header = sequence('parsing')
25
+
26
+ unless options[:before] == :start
27
+ gdb_file.expects(:read).in_sequence(header).with(6).returns "MsRcf\x00"
28
+ else
29
+ return [gdb_file, header]
30
+ end
31
+
32
+ unless options[:before] == :version
33
+ reclen = mock('reclen')
34
+ reclen.expects(:unpack).with('l').returns [2]
35
+ gdb_file.expects(:read).in_sequence(header).with(4).returns reclen
36
+
37
+ gdb_file.expects(:read).in_sequence(header).with(3).returns "Dm\x00"
38
+ else
39
+ return [gdb_file, header]
40
+ end
41
+
42
+ unless options[:before] == :creator
43
+ reclen = mock('reclen 2')
44
+ reclen.expects(:unpack).with('l').returns [27]
45
+ gdb_file.expects(:read).in_sequence(header).with(4).returns reclen
46
+
47
+ creator = mock('creator')
48
+ creator.expects(:unpack).with('Z*').returns ["Ae\u0002SQA"]
49
+ gdb_file.expects(:read).in_sequence(header).with(28).returns creator
50
+ else
51
+ return [gdb_file, header]
52
+ end
53
+
54
+ unless options[:before] == :signer
55
+ gdb_file.expects(:read).in_sequence(header).with(10).returns "MapSource\x00"
56
+ else
57
+ return [gdb_file, header]
58
+ end
59
+
60
+ [gdb_file, header]
61
+ end
62
+
63
+ SAMPLE_TRACK = File.dirname(__FILE__) + '/assets/track.bin'
64
+ end
65
+
66
+ include MapSource::Spec
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe MapSource::Reader do
4
+ describe "when reading the header" do
5
+ it "fails when first portion of the file doesn't pass validation" do
6
+ gdb_file, sequence = *create_basic_valid_state(before: :start)
7
+
8
+ bogus = mock('bogus')
9
+ bogus.expects(:unpack).with('A*').returns ['Bogus']
10
+ gdb_file.expects(:read).with(6).returns bogus
11
+
12
+ lambda {
13
+ MapSource::Reader.new gdb_file
14
+ }.must_raise MapSource::InvalidFormatError
15
+ end
16
+
17
+ it "fails when header is malformed" do
18
+ gdb_file, sequence = *create_basic_valid_state(before: :version)
19
+
20
+ reclen = mock('reclen')
21
+ reclen.expects(:unpack).with('l').returns [2]
22
+ gdb_file.expects(:read).in_sequence(sequence).with(4).returns reclen
23
+
24
+ gdb_file.expects(:read).in_sequence(sequence).with(3).returns "Am\x00"
25
+
26
+ lambda {
27
+ MapSource::Reader.new gdb_file
28
+ }.must_raise MapSource::InvalidFormatError
29
+ end
30
+
31
+ it "fails when version is unsupported" do
32
+ gdb_file, sequence = *create_basic_valid_state(before: :version)
33
+
34
+ reclen = mock('reclen')
35
+ reclen.expects(:unpack).with('l').returns [2]
36
+ gdb_file.expects(:read).in_sequence(sequence).with(4).returns reclen
37
+
38
+ gdb_file.expects(:read).in_sequence(sequence).with(3).returns "Dn\x00"
39
+
40
+ lambda {
41
+ MapSource::Reader.new gdb_file
42
+ }.must_raise MapSource::UnsupportedVersionError
43
+ end
44
+
45
+ it "determines which version of the format it has" do
46
+ gdb_file, _ = *create_basic_valid_state
47
+
48
+ reader = MapSource::Reader.new(gdb_file)
49
+ reader.header.version.must_equal 3
50
+ end
51
+
52
+ it "determines which software created the file" do
53
+ gdb_file, _ = *create_basic_valid_state
54
+
55
+ reader = MapSource::Reader.new(gdb_file)
56
+ reader.header.created_by.must_equal 'MapSource'
57
+ end
58
+
59
+ it "determines which software signed the file" do
60
+ gdb_file, _ = *create_basic_valid_state
61
+
62
+ reader = MapSource::Reader.new(gdb_file)
63
+ reader.header.signed_by.must_equal 'MapSource'
64
+ end
65
+
66
+ it "fails when creator is not recognized" do
67
+ gdb_file, sequence = *create_basic_valid_state(before: :signer)
68
+
69
+ gdb_file.expects(:read).in_sequence(sequence).with(10).returns 'BogusSoftw'
70
+ gdb_file.expects(:read).in_sequence(sequence).with(1).times(3).returns 'x'
71
+ gdb_file.expects(:read).in_sequence(sequence).with(1).returns "\x00"
72
+
73
+ lambda {
74
+ MapSource::Reader.new gdb_file
75
+ }.must_raise MapSource::InvalidFormatError
76
+ end
77
+ end
78
+
79
+ describe "when reading the content" do
80
+ it "parses waypoints" do
81
+ gdb_file, seq = *create_basic_valid_state
82
+
83
+ gdb_file.expects(:read).in_sequence(seq).with(4).returns "\l\x00\x00\x00"
84
+ gdb_file.expects(:read).in_sequence(seq).with(109).returns "W001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xE7\xB7\x85\xEF\xDE\xCB\x1D\xE0\x01\x00\x00\x00\x80\xF9X\x97@15-DEZ-11 12:06:43PM\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
85
+ gdb_file.expects(:read).in_sequence(seq).with(4).returns "\x01\x00\x00\x00"
86
+ gdb_file.expects(:read).in_sequence(seq).with(2).returns "V\x00"
87
+
88
+ reader = MapSource::Reader.new(gdb_file)
89
+
90
+ reader.waypoints.size.must_equal 1
91
+ wpt = reader.waypoints.first
92
+ wpt.shortname.must_equal "001"
93
+ wpt.latitude.must_be_close_to -23.17171306349337
94
+ wpt.longitude.must_be_close_to -44.836323726922274
95
+ wpt.notes.must_equal "15-DEZ-11 12:06:43PM"
96
+ wpt.altitude.floor.must_be_close_to 1494
97
+ end
98
+
99
+ it "parses tracks" do
100
+ track = open(SAMPLE_TRACK, 'rb').read
101
+ gdb_file, seq = *create_basic_valid_state
102
+ gdb_file.expects(:read).in_sequence(seq).with(4).returns "\xC0r\x00\x00"
103
+ gdb_file.expects(:read).in_sequence(seq).with(29377).returns track
104
+ gdb_file.expects(:read).in_sequence(seq).with(4).returns "\x01\x00\x00\x00"
105
+ gdb_file.expects(:read).in_sequence(seq).with(2).returns "V\x00"
106
+
107
+ reader = MapSource::Reader.new(gdb_file)
108
+ reader.tracks.size.must_equal 1
109
+
110
+ track = reader.tracks.first
111
+ track.name.must_equal "ACTIVE LOG"
112
+ track.color.name.must_equal "Red"
113
+ track.size.must_equal 1223
114
+
115
+ wpt = track.waypoints.first
116
+ wpt.latitude.must_be_close_to -22.17333
117
+ wpt.longitude.must_be_close_to -42.41256
118
+ wpt.altitude.must_be_close_to 675.0, 0.5
119
+
120
+ wpt = track.waypoints[1]
121
+ wpt.latitude.must_be_close_to -22.17332
122
+ wpt.longitude.must_be_close_to -42.41264
123
+ wpt.altitude.must_be_close_to 673.0, 0.5
124
+ end
125
+ end
126
+ end
127
+
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-mapsource
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Vitor Capela
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: &70324053963740 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.10'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70324053963740
25
+ - !ruby/object:Gem::Dependency
26
+ name: mocha
27
+ requirement: &70324053976040 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '0.10'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70324053976040
36
+ description: A Ruby library for reading MapSource/BaseCamp-created GDB files
37
+ email:
38
+ - dodecaphonic@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .rvmrc
45
+ - Gemfile
46
+ - Guardfile
47
+ - LICENSE
48
+ - README.md
49
+ - Rakefile
50
+ - lib/mapsource.rb
51
+ - lib/mapsource/defs.rb
52
+ - lib/mapsource/reader.rb
53
+ - lib/mapsource/structure.rb
54
+ - lib/mapsource/version.rb
55
+ - ruby-mapsource.gemspec
56
+ - spec/assets/sample.gdb
57
+ - spec/assets/track.bin
58
+ - spec/integration/reader_spec.rb
59
+ - spec/spec_helper.rb
60
+ - spec/unit/reader_spec.rb
61
+ homepage: ''
62
+ licenses: []
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project: ruby-mapsource
81
+ rubygems_version: 1.8.10
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: A Ruby library for reading MapSource/BaseCamp-created GDB files
85
+ test_files:
86
+ - spec/assets/sample.gdb
87
+ - spec/assets/track.bin
88
+ - spec/integration/reader_spec.rb
89
+ - spec/spec_helper.rb
90
+ - spec/unit/reader_spec.rb