waypoint_merge
– Waypoint and Route Merge Application¶
With multiple chart plotters, it’s very easy to have waypoints defined (or modified) separately. It’s necessary to reconcile the changes to arrive at a single, comprehensive list of waypoints that can be then distributed to all devices.
This requires reading and comparing GPX files to arrive at a master list of waypoints.
Similar analysis must be done for routes to accomodate changes.
Here’s the structure of this application
This module includes several groups of components.
The Input Parsing group is the functions and classes that acquire input from the GPX or CSV file.
The Processing functions work out range and bearing, magnetic bearing, total distance run, and elapsed time in minutes and hours.
The Output Writing group is the functions to write the CSV result.
Finally, the CLI components are used to build a proper command-line application.
Input Parsing¶
There are two kinds of inputs
GPX files. Each source has a unique logical layout imposed over a common physical format.
OpenCPN GPX. These tend to be richly detailed, using OpenCPN extensions.
Chartplotter GPX. These are minimal, skipping import details like GUID’s that provide unique identity to routes and waypoints.
USR files. Also called “Lowrance USR files.” These are a binary dump of chartplotter information.
Base Classes¶
Plotted image of a waypoint.
Input Processing¶
Generates
Waypoint_Plot
onjects from an OpenCPN GPX doc.<wpt lat="37.184990000" lon="-76.422203000"> <time>2020-09-30T07:52:39Z</time> <name>Chisman Creek</name> <sym>anchor</sym> <type>WPT</type> <extensions> <opencpn:guid>34de7898-f37e-458c-8ccb-e4e03fa325ec</opencpn:guid> <opencpn:viz_name>1</opencpn:viz_name> <opencpn:arrival_radius>0.050</opencpn:arrival_radius> <opencpn:waypoint_range_rings visible="false" number="-1" step="-1" units="-1" colour="#FFFFFF" /> <opencpn:scale_min_max UseScale="false" ScaleMin="2147483646" ScaleMax="0" /> </extensions>
This uses a BUNCH of namespaces
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:opencpn="http://www.opencpn.org"
- Parameters
source – an open XML file.
- Returns
An iterator over
LogEntry
objects.
Generates
Waypoint_Plot
onjects from an Chartplotter GPX doc.<metadata> <time>2021-06-04T16:55:38Z</time> <depthunits>meters</depthunits> <tempunits>C</tempunits> <sogunits>m/s</sogunits> </metadata> <wpt lon="-80.22695124" lat="25.71541470" > <time>2017-05-28T19:15:52Z</time> <name>Coconut Grove</name> <sym>anchor</sym> </wpt>
This uses one namespace
xmlns="http://www.topografix.com/GPX/1/1"
- Parameters
source – an open XML file.
- Returns
An iterator over
LogEntry
objects.
USR Details
{'uuid': UUID('41f0e2b8-e631-462a-82fd-f5292523f98d'), 'UID_unit_number': 12988, 'UID_sequence_number': 328, 'waypt_stream_version': 2, 'waypt_name_length': 18, 'waypt_name': 'ALLIGTR C', 'UID_unit_number_2': 12988, 'longitude': -76.64669528, 'latitude': 24.38829583, 'flags': 4, 'icon_id': 0, 'color_id': 0, 'waypt_description_length': -1, 'waypt_description': '', 'alarm_radius': 0.0, 'waypt_creation_date': datetime.date(2017, 6, 11), 'waypt_creation_time': datetime.timedelta(seconds=36080, microseconds=754000), 'unknown_2': -1, 'depth': 0.0, 'LORAN_GRI': -1, 'LORAN_Tda': 0, 'LORAN_Tdb': 0}
- Parameters
source –
- Returns
Processing¶
This is part of a four-step use case.
Dump charplotter-unique waypoints. These are not on the computer, which should be the single source of truth.
Merge the waypoints, loading OpenCPN. Make manual edits and updates to cleanup and simplify. Locate “to-be deleted” waypoints. These are duplicates (or near duplicates) that need to be merged and reconciled. Removing a waypoint can break routes, so route editing is part of this. There several cases:
Same name, new location. These are updates to OpenCPN to move an existing waypoint to a new location. It’s not clear how this should be done, but GUID matching should make this work.
Different names, proximate locations. The waypoint was renamed or is a duplicate.
New name, unique location. These are simply added.
Dump chartplotter-unique routes (if any.) These are not on the computer, which is the single source of truth.
Merge the routes into OpenCPN. Make any manual edits.
We’re focused on step 2, and the various comparisons between waypoints to determine what merge should be done. Step 2 decomposes into three phases:
Survey of differences. This is an text (or HTML) file with a comparison using all of rules.
Preparation of modifications; this is a GPX file that can be used to load OpenCPN. Some manual changes may be needed before these waypoints can be used.
Preparation of adds; this is a GPX file that is simply loaded into OpenCPN, since the waypoints are all new.
Comparisons¶
We have a number of comparison rules among waypoints. We can meaningfully compare waypoints on any of the following attributes:
“name” – While names change, they also reflect old technology limitations. So, some names are sometimes rewritten on a new device. There’s no near-miss matching here because “CHPTNK6” may have become “Choptank Entrance 6” with few overlapping letters.
“guid” – These should be immutable, but it’s not clear if it’s preserved in tranfers between devices.
“distance” – this is the waypoint-to-waypoint distance computation. This is the equirectangular distance. While accurate it’s also computationally intensive.
“geocode” – this is the faster geocode-based proximity test. Intead of computing \(m \times n\) distances, we can compute \(m + n\) geocodes, and use string comparison for a simple proximity check. The loxodromic or equirectangular distance involves a lot of computation. Using truncated OLC permits flexible adjacency via simple string-based processing. Less math. See https://en.m.wikipedia.org/wiki/Open_Location_Code
positions
degrees
distances
6
1/20
5566 meters, 3 nmi
8
1/400
278 meters, .15 nmi
10
1/8000
13.9 meters, 45 feet
This leads to a four comparison outcomes.
Same Name – Different Locations. This means a waypoint was moved. It can also mean two waypoints were created near each other with coincidentally identical names. This is a GPX file of waypoints which must be modified in OpenCPN.
Different Names – Proximate Locations. These are likely simple duplicates; one of the two must be removed, and the other used for all routes. Depending on the use within OpenCPN routes, this may be a complex change to modify reoutes to replace a waypoint.
Completely Different. In this case, Chartplotter points need to be added to the OpenCPN computer points. This should be visible as a GPX file of waypoints to add.
The Same. These are simple duplicates that can be ignored.
Classes and Functions¶
The state of the matching operation.
Each History reflects a waypoint and the matches against other waypoints from the same source, or waypoints from a different source.
Given a comparison function, updates two collections of
History
objects, to reflect matches between the waypoints.This does a brute-force \(m \times n\) comparison of all items in both lists. It applies the
comparison()
function to all elements to locate matches.The remaining items are exclusive to list 1 or list 2, representing the unmatched items.
Applies the rule to the History to accumulate all matches.
Compares names simply. Levenshtein distance might be helpful.
Compares GUID’s. Assumes collection 1 is OpenCPN.
Assumes pt1 is OpenCPN and pt2 is from the chartplotter.
Uses
Waypoint
definition ofnear()
. Has a hard-coded distance threshold of ±0.05 nmi.
Uses OLC matching to a given number of positions.
positions
degrees
distance
8
1/400
278 meters, .15 nmi
10
1/8000
13.9 meters, 45 feet
default size is 8.
Usaage:
c = CompareByGeocode.range(8)() c.compare(l1, l2)
Report on duplicates. Also, returns a reduced list of waypoints with duplicates removed.
Given a collection of duplicates, we use the we use the last_updated time to locate the newest copy and keep that.
- Parameters
wp_list – list[Waypoint_Plot], usually from a derived list in a chartplotter
- Returns
list[Waypoint_Plot] with duplicates removed.
Report on the comparison of two waypoint lists. This is a human-readable DIFF-like report, it uses all of the comparison algorithms to locate candidate duplicates.
- Parameters
wp_1 – Waypoints, usually from the master list in OpenCPN
wp_2 – Waypoints, usually from a derived list in a chartplotter
Output Writing¶
We translae the two sequences of history information into a single
stream of WP_Match
instances. We can then summarize these.
We use Jinja2 to write an XML-format file with the waypoints that need to be updated in OpenCPN.
The final match result. There are three states.
Both – they passed the given comparison test, and appear to be the same
1 only – From the first source (usually the computer) with no match
2 only – From the second source (usually the chart plotter) with no match
Alias for field number 0
Alias for field number 1
Return a new dict which maps field names to their values.
Make a new WP_Match object from a sequence or iterable
Return a new WP_Match object replacing specified fields with new values
Merge two sequences of
History
instances into single sequence ofWP_Match
instances.There are three subtypes of matches in the final sequence:
Something was the same.
Only in the first history list (usually the computer) with no match.
Only in the second history list (usually the chart plotter) with no match.
Inject waypoint data into an XML template.
- Parameters
source – source of Waypoint_Plot instances.
- Returns
XML document as text.
Report on a list of
WP_Match
instances. This is a GPX Upload to get the computer up-to-date with what’s on the chartplotter.This has two clumps of data.
Common but not proximate: these may need to be updated on the computer to reflect changes on the chartplotter. (Or. They may need to be updated on the chartplotter.)
Chartplotter only: these need to be transferred to the computer.
CLI¶
A typical command looks like this:
python navtools/waypoint_merge.py -p data/WaypointsRoutesTracks.usr -c "data/2021 opencpn waypoints.gpx" --by name --by geocode
This produces a report comparing the .USR output from the chartplotter with the GPX data dumped from OpenCPN. Waypoints are compared by name for literal matches, then by Geogode for likely matches that have different names.
The output is a list of points that have been created or changed in the chartplotter, plus a less-interesting list of points that are only in the computer.
Waypoint merge. Parses the command line options. Compares two files, and emits a report/summary or GPX.
Todo
parameterize the distance or geocode options.