rozi 0.0.7 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +255 -0
- data/lib/rozi.rb +6 -3
- data/lib/rozi/file_wrapper_base.rb +52 -0
- data/lib/rozi/module_functions.rb +19 -69
- data/lib/rozi/name_search.rb +176 -0
- data/lib/rozi/{ozi_functions.rb → shared.rb} +36 -9
- data/lib/rozi/tracks.rb +170 -0
- data/lib/rozi/version.rb +1 -1
- data/lib/rozi/waypoints.rb +315 -0
- data/test/rozi/file_wrapper_base_test.rb +29 -0
- data/test/rozi/module_functions_test.rb +11 -72
- data/test/rozi/name_search_test.rb +248 -0
- data/test/rozi/{ozi_functions_test.rb → shared_test.rb} +25 -5
- data/test/rozi/tracks_test.rb +105 -0
- data/test/rozi/waypoints_test.rb +268 -0
- data/test/test_data/expected_output_1.nst +6 -0
- data/{test_data → test/test_data}/expected_output_1.plt +9 -9
- data/{test_data → test/test_data}/expected_output_1.wpt +7 -7
- data/test/test_data/input_1.wpt +6 -0
- metadata +128 -34
- data/README.rdoc +0 -127
- data/lib/rozi/name.rb +0 -23
- data/lib/rozi/name_search_text.rb +0 -43
- data/lib/rozi/name_search_text_writer.rb +0 -71
- data/lib/rozi/track.rb +0 -67
- data/lib/rozi/track_point.rb +0 -39
- data/lib/rozi/track_writer.rb +0 -50
- data/lib/rozi/waypoint.rb +0 -90
- data/lib/rozi/waypoint_writer.rb +0 -38
- data/test/rozi/name_search_text_test.rb +0 -28
- data/test/rozi/name_search_text_writer_test.rb +0 -114
- data/test/rozi/track_point_test.rb +0 -32
- data/test/rozi/track_test.rb +0 -25
- data/test/rozi/track_writer_test.rb +0 -62
- data/test/rozi/waypoint_test.rb +0 -31
- data/test/rozi/waypoint_writer_test.rb +0 -60
@@ -0,0 +1,176 @@
|
|
1
|
+
|
2
|
+
require "rozi/file_wrapper_base"
|
3
|
+
require "rozi/shared"
|
4
|
+
|
5
|
+
module Rozi
|
6
|
+
##
|
7
|
+
# Writes an enumerable of names to a file
|
8
|
+
#
|
9
|
+
# All keyword arguments are used as track properties.
|
10
|
+
#
|
11
|
+
# @param [Enumerable] enumerable
|
12
|
+
# @param [String] file_path
|
13
|
+
#
|
14
|
+
def write_nst(enumerable, file_path, **properties)
|
15
|
+
NameSearchTextFile.open(file_path, "w") { |nst|
|
16
|
+
nst.write_properties NameSearchProperties.new(**properties)
|
17
|
+
|
18
|
+
enumerable.each { |name|
|
19
|
+
nst.write_name name
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# This class represents a name in an Ozi Explorer name database.
|
28
|
+
#
|
29
|
+
# @note The +name+, +latitude+ and +longitude+ fields must be set, or runtime errors will
|
30
|
+
# be raised when attempting to write to file.
|
31
|
+
#
|
32
|
+
class Name < DataStruct
|
33
|
+
PROPERTIES = [:name, :feature_code, :zone, :latitude, :longitude]
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Represents the global properties of a name search text file
|
38
|
+
#
|
39
|
+
class NameSearchProperties < DataStruct
|
40
|
+
PROPERTIES = [
|
41
|
+
:comment, :datum, :latlng, :utm, :utm_zone, :hemisphere
|
42
|
+
]
|
43
|
+
|
44
|
+
include Shared
|
45
|
+
include Shared::DatumSetter
|
46
|
+
|
47
|
+
def initialize(*args, **kwargs)
|
48
|
+
update(
|
49
|
+
comment: "",
|
50
|
+
datum: "WGS 84",
|
51
|
+
latlng: true,
|
52
|
+
utm: false,
|
53
|
+
utm_zone: nil,
|
54
|
+
hemisphere: nil
|
55
|
+
)
|
56
|
+
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# A thin layer above +File+ that handles reading and writing of names to name
|
63
|
+
# search text files
|
64
|
+
#
|
65
|
+
class NameSearchTextFile < FileWrapperBase
|
66
|
+
##
|
67
|
+
# Writes an enumerable of {Rozi::Name} objects to the file
|
68
|
+
#
|
69
|
+
# @param [Enumerable] enumerable
|
70
|
+
# @return [nil]
|
71
|
+
#
|
72
|
+
def write(enumerable)
|
73
|
+
enumerable.each { |name|
|
74
|
+
write_name name
|
75
|
+
}
|
76
|
+
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Writes a {Rozi::Name} to the file
|
82
|
+
#
|
83
|
+
# @note If no properties have been written to the file before this method is
|
84
|
+
# called, a default set of properties will be automatically written to the
|
85
|
+
# file first
|
86
|
+
# @param [Rozi::Name] name
|
87
|
+
# @return [nil]
|
88
|
+
#
|
89
|
+
def write_name(name)
|
90
|
+
ensure_properties
|
91
|
+
|
92
|
+
@file.write serialize_name(name)
|
93
|
+
@file.write "\n"
|
94
|
+
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Writes name search properties to the file
|
100
|
+
#
|
101
|
+
# The file must be empty when this method is called!
|
102
|
+
#
|
103
|
+
# @raise [RuntimeError] if the file isn't empty
|
104
|
+
# @param [NameSearchProperties] properties
|
105
|
+
# @return [nil]
|
106
|
+
#
|
107
|
+
def write_properties(properties)
|
108
|
+
if @file.size > 0
|
109
|
+
raise "Can't write file properties, file is not empty"
|
110
|
+
end
|
111
|
+
|
112
|
+
@file.write serialize_properties(properties)
|
113
|
+
@file.write "\n"
|
114
|
+
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
##
|
121
|
+
# Ensures that properties have been written to the file
|
122
|
+
#
|
123
|
+
def ensure_properties
|
124
|
+
return if @properties_written
|
125
|
+
|
126
|
+
@properties_written = true
|
127
|
+
|
128
|
+
if @file.size == 0
|
129
|
+
write_properties NameSearchProperties.new
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def serialize_name(name)
|
134
|
+
if not name.name or not name.latitude or not name.longitude
|
135
|
+
fail ArgumentError, "name, latitude and longitude must be set!"
|
136
|
+
end
|
137
|
+
|
138
|
+
feature_code = name.feature_code || ""
|
139
|
+
|
140
|
+
if name.name.include?(",") or feature_code.include?(",")
|
141
|
+
fail ArgumentError, "Text cannot contain commas"
|
142
|
+
end
|
143
|
+
|
144
|
+
"%s,%s,%s,%s,%s" % [
|
145
|
+
name.name, name.feature_code, name.zone,
|
146
|
+
name.latitude.round(6), name.longitude.round(6)
|
147
|
+
]
|
148
|
+
end
|
149
|
+
|
150
|
+
def serialize_properties(properties)
|
151
|
+
out = ""
|
152
|
+
|
153
|
+
if properties.comment
|
154
|
+
properties.comment.each_line { |line|
|
155
|
+
out << ";#{line.chomp}\n"
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
out << "#1,"
|
160
|
+
|
161
|
+
if properties.utm
|
162
|
+
out << "UTM,#{properties.utm_zone}"
|
163
|
+
|
164
|
+
if properties.hemisphere
|
165
|
+
out << ",#{properties.hemisphere}"
|
166
|
+
end
|
167
|
+
else
|
168
|
+
out << ","
|
169
|
+
end
|
170
|
+
|
171
|
+
out << "\n#2,#{properties.datum}"
|
172
|
+
|
173
|
+
return out
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -1,24 +1,30 @@
|
|
1
1
|
|
2
2
|
module Rozi
|
3
|
-
|
4
3
|
##
|
5
|
-
# Contains general functions for working with Ozi Explorer file formats
|
4
|
+
# Contains general functions for working with Ozi Explorer file formats
|
6
5
|
#
|
7
|
-
module
|
6
|
+
module Shared
|
8
7
|
##
|
9
|
-
# Escapes commas so the text can be used in Ozi file formats
|
8
|
+
# Escapes commas so the text can be used in Ozi file formats
|
10
9
|
#
|
11
10
|
# @param [String] text
|
12
11
|
# @return [String]
|
13
12
|
#
|
14
13
|
def escape_text(text)
|
15
|
-
text.gsub(/,/,
|
14
|
+
text.gsub(/,/, "Ñ")
|
16
15
|
end
|
17
16
|
|
18
17
|
##
|
19
|
-
#
|
18
|
+
# Undoes the effect of {Rozi::Shared#escape_text}
|
20
19
|
#
|
21
|
-
|
20
|
+
def unescape_text(text)
|
21
|
+
text.gsub(/Ñ/, ",")
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Converts the input to an RGB color represented by an integer
|
26
|
+
#
|
27
|
+
# @param [String, Integer] color Can be a RRGGBB hex string or an integer
|
22
28
|
# @return [Integer]
|
23
29
|
#
|
24
30
|
# @example
|
@@ -36,13 +42,34 @@ module Rozi
|
|
36
42
|
end
|
37
43
|
|
38
44
|
##
|
39
|
-
# Checks if +datum+ is a valid datum according to Ozi Explorer
|
45
|
+
# Checks if +datum+ is a valid datum according to Ozi Explorer
|
40
46
|
#
|
41
47
|
# @return [Boolean] true if +datum+ is valid
|
42
48
|
#
|
43
49
|
def datum_valid?(datum)
|
44
50
|
Rozi::DATUMS.include? datum
|
45
51
|
end
|
46
|
-
end
|
47
52
|
|
53
|
+
##
|
54
|
+
# All data structures with a datum property should include this module
|
55
|
+
#
|
56
|
+
module DatumSetter
|
57
|
+
include Shared
|
58
|
+
|
59
|
+
##
|
60
|
+
# Sets the datum property
|
61
|
+
#
|
62
|
+
# @param [String] datum
|
63
|
+
# @raise [ArgumentError] on invalid datum
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
def datum=(datum)
|
67
|
+
if not datum_valid?(datum)
|
68
|
+
fail ArgumentError, "Invalid datum: #{datum}"
|
69
|
+
end
|
70
|
+
|
71
|
+
super datum
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
48
75
|
end
|
data/lib/rozi/tracks.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
|
2
|
+
require "rozi/file_wrapper_base"
|
3
|
+
require "rozi/shared"
|
4
|
+
|
5
|
+
module Rozi
|
6
|
+
##
|
7
|
+
# Writes an enumerable of track points to a file
|
8
|
+
#
|
9
|
+
# All keyword arguments are used as track properties.
|
10
|
+
#
|
11
|
+
# @param [Enumerable] enumerable
|
12
|
+
# @param [String] file_path
|
13
|
+
#
|
14
|
+
def write_track(enumerable, file_path, **properties)
|
15
|
+
TrackFile.open(file_path, "w") { |track_file|
|
16
|
+
track_file.write_track_properties TrackProperties.new(**properties)
|
17
|
+
track_file.write enumerable
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Represents a point in an Ozi Explorer track
|
23
|
+
#
|
24
|
+
class TrackPoint < DataStruct
|
25
|
+
PROPERTIES = [
|
26
|
+
:latitude, :longitude, :break, :altitude,
|
27
|
+
:date, :date_string, :time_string
|
28
|
+
]
|
29
|
+
|
30
|
+
def initialize(*args, **kwargs)
|
31
|
+
update(
|
32
|
+
break: false,
|
33
|
+
altitude: -777,
|
34
|
+
date: 0,
|
35
|
+
date_string: "",
|
36
|
+
time_string: ""
|
37
|
+
)
|
38
|
+
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
def break
|
43
|
+
super == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def break=(brk)
|
47
|
+
super(brk ? 1 : 0)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Represents the track properties at the top of an Ozi Explorer track file
|
53
|
+
#
|
54
|
+
class TrackProperties < DataStruct
|
55
|
+
PROPERTIES = [
|
56
|
+
:datum, :line_width, :color, :description, :skip_value,
|
57
|
+
:type, :fill_style, :fill_color
|
58
|
+
]
|
59
|
+
|
60
|
+
include Shared
|
61
|
+
include Shared::DatumSetter
|
62
|
+
|
63
|
+
def initialize(*args, **kwargs)
|
64
|
+
update(
|
65
|
+
datum: "WGS 84",
|
66
|
+
line_width: 2,
|
67
|
+
color: 255,
|
68
|
+
description: "",
|
69
|
+
skip_value: 1,
|
70
|
+
type: 0,
|
71
|
+
fill_style: 0,
|
72
|
+
fill_color: 0
|
73
|
+
)
|
74
|
+
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def color=(color)
|
79
|
+
super interpret_color(color)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# A thin wrapper around a file object made for reading and writing tracks
|
85
|
+
#
|
86
|
+
class TrackFile < FileWrapperBase
|
87
|
+
include Shared
|
88
|
+
|
89
|
+
##
|
90
|
+
# Writes track properties to the file
|
91
|
+
#
|
92
|
+
# The file must be empty when this method is called!
|
93
|
+
#
|
94
|
+
# @raise [RuntimeError] if the file isn't empty
|
95
|
+
# @param [TrackProperties] properties
|
96
|
+
# @return [nil]
|
97
|
+
#
|
98
|
+
def write_track_properties(properties)
|
99
|
+
if @file.size > 0
|
100
|
+
raise "Can't write file properties, file is not empty"
|
101
|
+
end
|
102
|
+
|
103
|
+
@file.write serialize_track_properties(properties)
|
104
|
+
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Writes a track point to the file
|
110
|
+
#
|
111
|
+
# @param [TrackPoint] track_point
|
112
|
+
# @return [nil]
|
113
|
+
#
|
114
|
+
def write_track_point(track_point)
|
115
|
+
ensure_track_properties
|
116
|
+
|
117
|
+
@file.write serialize_track_point(track_point)
|
118
|
+
@file.write "\n"
|
119
|
+
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Writes an enumerable of track points to the file
|
125
|
+
#
|
126
|
+
# @param [Enumerable] enumerable
|
127
|
+
#
|
128
|
+
def write(enumerable)
|
129
|
+
enumerable.each { |track_point|
|
130
|
+
self.write_track_point(track_point)
|
131
|
+
}
|
132
|
+
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
##
|
139
|
+
# Ensures that track properties has been written to the file
|
140
|
+
#
|
141
|
+
def ensure_track_properties
|
142
|
+
return if @properties_written
|
143
|
+
|
144
|
+
@properties_written = true
|
145
|
+
|
146
|
+
if @file.size == 0
|
147
|
+
write_track_properties TrackProperties.new
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def serialize_track_properties(track_properties)
|
152
|
+
props = track_properties.to_a
|
153
|
+
props.delete_at(0) # The datum isn't a part of this list
|
154
|
+
props.map! { |item| item.is_a?(String) ? escape_text(item) : item }
|
155
|
+
|
156
|
+
<<-TEXT.gsub(/^[ ]{8}/, "") % props
|
157
|
+
OziExplorer Track Point File Version 2.1
|
158
|
+
#{track_properties.datum}
|
159
|
+
Altitude is in Feet
|
160
|
+
Reserved 3
|
161
|
+
0,%d,%d,%s,%d,%d,%d,%d
|
162
|
+
0
|
163
|
+
TEXT
|
164
|
+
end
|
165
|
+
|
166
|
+
def serialize_track_point(track_point)
|
167
|
+
" %.6f,%.6f,%d,%.1f,%.7f,%s,%s" % track_point.to_a
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/rozi/version.rb
CHANGED
@@ -0,0 +1,315 @@
|
|
1
|
+
|
2
|
+
require "rozi/file_wrapper_base"
|
3
|
+
require "rozi/shared"
|
4
|
+
|
5
|
+
module Rozi
|
6
|
+
##
|
7
|
+
# Writes an enumerable of waypoints to a file
|
8
|
+
#
|
9
|
+
# @param [Enumerable] waypoints
|
10
|
+
# @param [String] file_path
|
11
|
+
# @param [Hash] properties Any extra keyword arguments are processed as
|
12
|
+
# waypoint file properties
|
13
|
+
#
|
14
|
+
def self.write_waypoints(waypoints, file_path, **properties)
|
15
|
+
wpt_file = WaypointFile.open(file_path, "w")
|
16
|
+
|
17
|
+
wpt_file.write_properties(WaypointFileProperties.new(**properties))
|
18
|
+
wpt_file.write waypoints
|
19
|
+
|
20
|
+
wpt_file.close
|
21
|
+
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Represents a waypoint in Ozi Explorer
|
27
|
+
#
|
28
|
+
class Waypoint < DataStruct
|
29
|
+
PROPERTIES = [
|
30
|
+
:number, :name, :latitude, :longitude, :date, :symbol, :display_format,
|
31
|
+
:fg_color, :bg_color, :description, :pointer_direction, :altitude,
|
32
|
+
:font_size, :font_style, :symbol_size
|
33
|
+
]
|
34
|
+
|
35
|
+
DISPLAY_FORMATS = {
|
36
|
+
:number_only => 0,
|
37
|
+
:name_only => 1,
|
38
|
+
:number_and_name => 2,
|
39
|
+
:name_with_dot => 3,
|
40
|
+
:name_with_symbol => 4,
|
41
|
+
:symbol_only => 5,
|
42
|
+
:comment_with_symbol => 6,
|
43
|
+
:man_overboard => 7,
|
44
|
+
:marker => 8
|
45
|
+
}
|
46
|
+
|
47
|
+
include Shared
|
48
|
+
|
49
|
+
def initialize(*args, **kwargs)
|
50
|
+
update(
|
51
|
+
number: -1,
|
52
|
+
name: "",
|
53
|
+
latitude: 0.0,
|
54
|
+
longitude: 0.0,
|
55
|
+
date: nil,
|
56
|
+
symbol: 0,
|
57
|
+
display_format: :name_with_dot,
|
58
|
+
fg_color: 0,
|
59
|
+
bg_color: 65535,
|
60
|
+
description: "",
|
61
|
+
pointer_direction: 0,
|
62
|
+
altitude: -777,
|
63
|
+
font_size: 6,
|
64
|
+
font_style: 0,
|
65
|
+
symbol_size: 17
|
66
|
+
)
|
67
|
+
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Returns the value of the display format property
|
73
|
+
#
|
74
|
+
# @param [Boolean] raw If true, returns the raw value with no processing
|
75
|
+
#
|
76
|
+
def display_format(raw: false)
|
77
|
+
if raw
|
78
|
+
super
|
79
|
+
else
|
80
|
+
DISPLAY_FORMATS.invert[super]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def display_format=(display_format)
|
85
|
+
if display_format.is_a? Symbol
|
86
|
+
@data[:display_format] = DISPLAY_FORMATS[display_format]
|
87
|
+
else
|
88
|
+
@data[:display_format] = display_format
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Sets the foreground color
|
94
|
+
#
|
95
|
+
def fg_color=(color)
|
96
|
+
@data[:fg_color] = interpret_color(color)
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Sets the background color
|
101
|
+
#
|
102
|
+
def bg_color=(color)
|
103
|
+
@data[:bg_color] = interpret_color(color)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# This class represents the waypoint file properties contained in the top 4
|
109
|
+
# lines of a waypoint file
|
110
|
+
#
|
111
|
+
class WaypointFileProperties < DataStruct
|
112
|
+
include Shared::DatumSetter
|
113
|
+
|
114
|
+
PROPERTIES = [:datum, :version]
|
115
|
+
|
116
|
+
def initialize(*args, **kwargs)
|
117
|
+
update(
|
118
|
+
datum: "WGS 84",
|
119
|
+
version: "1.1"
|
120
|
+
)
|
121
|
+
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# A thin layer above +File+ that handles reading and writing of waypoints to
|
128
|
+
# files
|
129
|
+
#
|
130
|
+
class WaypointFile < FileWrapperBase
|
131
|
+
include Shared
|
132
|
+
|
133
|
+
#@group Writing methods
|
134
|
+
|
135
|
+
##
|
136
|
+
# Writes waypoints to the file
|
137
|
+
#
|
138
|
+
# @param [Enumerator<Waypoint>] waypoints
|
139
|
+
# @return [nil]
|
140
|
+
#
|
141
|
+
def write(waypoints)
|
142
|
+
waypoints.each { |wpt|
|
143
|
+
write_waypoint wpt
|
144
|
+
}
|
145
|
+
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Writes waypoint file properties to the file
|
151
|
+
#
|
152
|
+
# The file must be empty when this method is called!
|
153
|
+
#
|
154
|
+
# @raise [RuntimeError] if the file isn't empty
|
155
|
+
# @param [WaypointFileProperties] properties
|
156
|
+
# @return [nil]
|
157
|
+
#
|
158
|
+
def write_properties(properties)
|
159
|
+
if @file.size > 0
|
160
|
+
raise "Can't write file properties, file is not empty"
|
161
|
+
end
|
162
|
+
|
163
|
+
@file.write serialize_waypoint_file_properties(properties)
|
164
|
+
@file.write "\n"
|
165
|
+
|
166
|
+
nil
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Writes a waypoint to the file
|
171
|
+
#
|
172
|
+
# @param [Waypoint] waypoint
|
173
|
+
# @return [nil]
|
174
|
+
#
|
175
|
+
def write_waypoint(waypoint)
|
176
|
+
ensure_file_properties
|
177
|
+
|
178
|
+
@file.write serialize_waypoint(waypoint)
|
179
|
+
@file.write "\n"
|
180
|
+
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
|
184
|
+
#@group Reading methods
|
185
|
+
|
186
|
+
##
|
187
|
+
# Reads and yields all waypoints
|
188
|
+
#
|
189
|
+
def each_waypoint
|
190
|
+
return to_enum(:each_waypoint) unless block_given?
|
191
|
+
|
192
|
+
@file.rewind
|
193
|
+
|
194
|
+
loop { yield read_waypoint }
|
195
|
+
rescue EOFError
|
196
|
+
return nil
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Reads the waypoint file properties
|
201
|
+
#
|
202
|
+
# @raise [RuntimeError] If the file position isn't 0
|
203
|
+
# @return [WaypointFileProperties]
|
204
|
+
#
|
205
|
+
def read_properties
|
206
|
+
if @file.pos != 0
|
207
|
+
raise "File position must be 0 to read properties"
|
208
|
+
end
|
209
|
+
|
210
|
+
text = ""
|
211
|
+
|
212
|
+
4.times { text << @file.readline }
|
213
|
+
|
214
|
+
parse_waypoint_file_properties text
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Reads the next waypoint
|
219
|
+
#
|
220
|
+
# @raise [EOFError] When EOF is reached
|
221
|
+
# @return [Waypoint]
|
222
|
+
#
|
223
|
+
def read_waypoint
|
224
|
+
if @file.pos == 0
|
225
|
+
read_properties
|
226
|
+
end
|
227
|
+
|
228
|
+
parse_waypoint @file.readline
|
229
|
+
end
|
230
|
+
|
231
|
+
#@endgroup
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def parse_waypoint(text)
|
236
|
+
map = {
|
237
|
+
0 => {symbol: :number, cast: method(:Integer)},
|
238
|
+
1 => {symbol: :name, cast: method(:String)},
|
239
|
+
2 => {symbol: :latitude, cast: method(:Float)},
|
240
|
+
3 => {symbol: :longitude, cast: method(:Float)},
|
241
|
+
4 => {symbol: :date, cast: method(:Float)},
|
242
|
+
5 => {symbol: :symbol, cast: method(:Integer)},
|
243
|
+
7 => {symbol: :display_format, cast: method(:Integer)},
|
244
|
+
8 => {symbol: :fg_color, cast: method(:Integer)},
|
245
|
+
9 => {symbol: :bg_color, cast: method(:Integer)},
|
246
|
+
10 => {symbol: :description, cast: method(:String)},
|
247
|
+
11 => {symbol: :pointer_direction, cast: method(:Integer)},
|
248
|
+
14 => {symbol: :altitude, cast: method(:Integer)},
|
249
|
+
15 => {symbol: :font_size, cast: method(:Integer)},
|
250
|
+
16 => {symbol: :font_style, cast: method(:Integer)},
|
251
|
+
17 => {symbol: :symbol_size, cast: method(:Integer)},
|
252
|
+
}
|
253
|
+
|
254
|
+
text = text.strip
|
255
|
+
fields = text.split(",").map { |x| x.strip }
|
256
|
+
|
257
|
+
waypoint = Waypoint.new
|
258
|
+
|
259
|
+
map.each_pair { |index, data|
|
260
|
+
value = fields[index]
|
261
|
+
|
262
|
+
next if value.empty?
|
263
|
+
|
264
|
+
value = data[:cast].call(value)
|
265
|
+
|
266
|
+
if value.is_a? String
|
267
|
+
value = unescape_text(value)
|
268
|
+
end
|
269
|
+
|
270
|
+
waypoint.set(data[:symbol], value)
|
271
|
+
}
|
272
|
+
|
273
|
+
waypoint
|
274
|
+
end
|
275
|
+
|
276
|
+
def parse_waypoint_file_properties(text)
|
277
|
+
lines = text.lines
|
278
|
+
|
279
|
+
version = lines[0].strip[-3..-1]
|
280
|
+
datum = lines[1].strip
|
281
|
+
|
282
|
+
WaypointFileProperties.new(datum, version)
|
283
|
+
end
|
284
|
+
|
285
|
+
def serialize_waypoint(waypoint)
|
286
|
+
array = waypoint.to_a
|
287
|
+
array.map! { |item| item.is_a?(String) ? escape_text(item) : item }
|
288
|
+
array.map! { |item| item.nil? ? "" : item }
|
289
|
+
array.map! { |item| item.is_a?(Float) ? item.round(6) : item }
|
290
|
+
|
291
|
+
"%d,%s,%f,%f,%s,%d,1,%d,%d,%d,%s,%d,,,%d,%d,%d,%d" % array
|
292
|
+
end
|
293
|
+
|
294
|
+
def serialize_waypoint_file_properties(properties)
|
295
|
+
<<-TEXT.gsub(/^[ ]{8}/, "")
|
296
|
+
OziExplorer Waypoint File Version #{properties.version}
|
297
|
+
#{properties.datum}
|
298
|
+
Reserved 2
|
299
|
+
TEXT
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Ensures that waypoint file properties has been written to the file
|
304
|
+
#
|
305
|
+
def ensure_file_properties
|
306
|
+
return if @properties_written
|
307
|
+
|
308
|
+
@properties_written = true
|
309
|
+
|
310
|
+
if @file.size == 0
|
311
|
+
write_properties WaypointFileProperties.new
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|