rozi 0.0.7 → 0.1.3
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.
- 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
|