Validate GeoJSON Waypoints Before a UG21 Survey Handoff
A Python check for coordinate order, site bounds and what the receiving planner actually imported.
The mapping team sends a file called waypoints.geojson. The receiving planner draws every point and shows the expected number of rows. A spot check against the survey boundary reveals that the points landed in a different part of the world. Both columns contained valid angles, so the JSON parser and the map could do their jobs while the handoff still failed.
A useful handoff checks more than whether the file opens. It establishes what the coordinates mean, compares the result with evidence from the survey site, and checks what the receiving software actually read. The process below uses the UG21 as the aircraft context and a small Python program to catch a class of input errors before the waypoints enter an operational plan.
Write the coordinate contract alongside the file
The UNITED UAV UG21 is a fixed wing VTOL aircraft listed for mapping and inspection. A team evaluating it for survey work should ask the integrator which planner, firmware and import formats the proposed configuration uses. The GeoJSON in this walkthrough is an interchange artifact between a GIS and a planning team. Each team can check the file before converting it to the format its planner expects. UNITED UAV UG21 product and configuration page

Figure 1. UG21 catalog front view. The handoff procedure depends on the planner and configuration selected for the survey.
Write down the horizontal reference, coordinate units, axis order, data owner and export revision. Include one worked example that shows the named source fields and the exact two numbers written to a point. Someone opening the file later should be able to tell whether the first field came from longitude, latitude, easting or a display label. A column header like X gives too little information on its own, especially after a spreadsheet or desktop GIS has changed the field names.
RFC 7946 uses WGS 84 geographic coordinates expressed in decimal degrees. A point position puts longitude first and latitude second. This convention applies to the serialized coordinates. The coordinate reference system used inside a GIS library can have its own axis order, and an import dialog can expose yet another order. Set and test each boundary explicitly. RFC 7946 coordinate reference system
PROJ documents why the order accepted or returned by different interfaces can vary with the authority definition and with the tool. A transformation can be mathematically correct and still send the wrong tuple to the next interface. Inspect the actual result at a known point after serialization. If your source is a projected grid, keep the source reference and units with the transformation settings so a reviewer can reproduce that comparison. PROJ axis order guidance
Give one component responsibility for writing the coordinate order. For example, a transformation step can return named longitude and latitude fields, then a serializer can construct the position array from those names. Test the serializer with values that are easy to tell apart. If the receiving adapter also needs to change the order, test its output separately. Two compensating swaps can make a single end to end test pass while the intermediate file is still wrong for anyone who consumes it directly.
Establish the expected area from a separate source
A longitude range and a latitude range catch some malformed points. They give weak protection when both numbers are between minus ninety and ninety. A synthetic position of 11.52 degrees east and 48.12 degrees north is valid. The reversed pair is also valid on a world map. If the project envelope is around the first position, it can expose a reversal that a range test lets through.
Build that envelope from an independently reviewed survey boundary or control data. A bounding box exported by the same job that created the waypoint file shares its assumptions. If the export process swaps the coordinates of the points and the bounds together, both can agree while pointing at the wrong place. Record the source of the envelope, its revision and who checked it. Keep the original project boundary available as a separate artifact.
Give each envelope an explicit purpose. A survey acquisition block might have one envelope for the points under review and a different set of records for other parts of the mission. If a handoff includes two distant blocks, validate them separately so a broad rectangle between them cannot hide a point assigned to the wrong block. If a point belongs to a separate operating area, record that membership before checking it.
Choose a margin from the project context and document it. A buffer can accommodate a known source precision or an intentionally offset survey line. A buffer chosen after looking at failed points can turn errors into passes. The example uses a local rectangle in geographic degrees and a closed boundary, so a point exactly on an edge is included. Projects with a complex boundary can follow the rectangle check with a point in polygon test in a suitable reference system.
Keep distance tolerances separate from angular bounds. A fixed longitude increment covers a different east to west distance at different latitudes. If the acceptance rule uses a margin in metres, derive it in an appropriate reference or use a geodetic method, then document how it relates to the envelope supplied to the checker. A box chosen for the initial screen can be deliberately simple. The narrower site boundary still has a role when a point falls in the box but outside the intended survey polygon.
Run the check against the serialized positions
The example accepts a nonempty GeoJSON FeatureCollection of two-dimensional Point features with unique string identifiers. It uses a local envelope that does not cross the antimeridian. This deliberately small input contract fits a waypoint handoff. A file containing lines, polygons or three-dimensional positions needs its own documented handling before it comes through this check.
For each numeric pair, the checker evaluates both orders against the site envelope. A point with only the reversed order inside the site is a candidate for review. It stays in the report with its original ID and values. A point with both orders inside the site also requires review because the envelope cannot resolve the order. Bounds, shape and input errors have separate results so the importer does not conceal the cause behind a single failed count.
from math import isfinite
def finite_number(value):
return type(value) is int or (type(value) is float and isfinite(value))
def inspect_waypoints(collection, extent):
if (
not isinstance(extent, (tuple, list))
or len(extent) != 4
or not all(finite_number(value) for value in extent)
):
raise ValueError("Extent needs four finite numbers")
west, south, east, north = extent
if not (-180 <= west < east <= 180 and -90 <= south < north <= 90):
raise ValueError("Extent must be a local west, south, east, north box")
if not isinstance(collection, dict) or collection.get("type") != "FeatureCollection":
raise ValueError("Expected a FeatureCollection")
if "crs" in collection:
raise ValueError("Resolve legacy CRS metadata before this handoff")
features = collection.get("features")
if not isinstance(features, list) or not features:
raise ValueError("Expected a nonempty feature list")
def in_site(longitude, latitude):
return west <= longitude <= east and south <= latitude <= north
findings = {}
for feature in features:
if not isinstance(feature, dict) or feature.get("type") != "Feature":
raise ValueError("Expected a Feature")
key = feature.get("id")
if not isinstance(key, str) or not key.strip() or key != key.strip() or key in findings:
raise ValueError("Each waypoint needs a unique nonempty string ID")
geometry = feature.get("geometry")
if not isinstance(geometry, dict) or geometry.get("type") != "Point":
findings[key] = "unsupported_geometry"
continue
position = geometry.get("coordinates")
if (
not isinstance(position, list)
or len(position) != 2
or not all(finite_number(value) for value in position)
):
findings[key] = "invalid_position"
continue
longitude, latitude = position
declared_order = in_site(longitude, latitude)
reverse_order = in_site(latitude, longitude)
if declared_order and reverse_order:
findings[key] = "ambiguous_axis_order"
elif declared_order:
findings[key] = "site_match"
elif reverse_order:
findings[key] = "possible_axis_swap"
elif -180 <= longitude <= 180 and -90 <= latitude <= 90:
findings[key] = "outside_expected_site"
else:
findings[key] = "outside_geographic_range"
return findings
def point(key, coordinates):
return {
"type": "Feature", "id": key, "properties": {},
"geometry": {"type": "Point", "coordinates": coordinates},
}
site = (11.45, 48.05, 11.60, 48.20)
sample = {
"type": "FeatureCollection",
"features": [
point("P1", [11.52, 48.12]),
point("P2", [48.12, 11.52]),
point("P3", [13.25, 48.12]),
point("P4", [478120, 5331000]),
],
}
assert inspect_waypoints(sample, site) == {
"P1": "site_match",
"P2": "possible_axis_swap",
"P3": "outside_expected_site",
"P4": "outside_geographic_range",
}
near_equal_axes = {
"type": "FeatureCollection",
"features": [point("P5", [9.999, 10.001])],
}
assert inspect_waypoints(near_equal_axes, (9.99, 9.99, 10.01, 10.01)) == {
"P5": "ambiguous_axis_order",
}
In the synthetic result, the third point has credible geographic coordinates but falls outside the intended area. The fourth has values that resemble a projected coordinate pair. Its numeric magnitudes are a useful clue during diagnosis. The transformation still needs an explicit source definition, so find that definition in the original export or ask the team that produced the file.
When writing the report, join the result by feature ID back to the original coordinates and the source feature. Preserve the identifiers exactly across revisions. Include the original file digest and the envelope revision so a receiving team can rerun the same review. If you correct an export, generate a new file and compare its changed rows with the rejected version. The edit history should show how the producer repaired the source mapping and which points the correction affected.
The sample receives an already parsed object. At a production file boundary, decide how the JSON reader handles duplicate object keys and non-finite numbers. A parser that keeps only the last duplicate property can erase conflicting evidence before the checker sees the feature. If your interchange requires strict parsing, apply that rule when reading the original file. A duplicate feature ID is a separate problem and can still occur in otherwise valid JSON. The example rejects it because the review table would otherwise lose a row.
Look at the distribution of findings within each source batch. A whole batch flagged for a possible swap points toward a shared conversion or field mapping. A single flagged point among otherwise consistent features merits a comparison with the underlying source record. Preserve both the count and the identifiers. The individual records distinguish a systematic failure from a mix of valid and invalid rows, which need different fixes and a different scope of retesting.
The program tests containment and some input assumptions. A site_match state gives no accuracy measurement inside the box. Run the next check even when every expected point matches the site, particularly if the transfer will become a template for multiple jobs.
Compare coordinates at independently checked locations
Take several check points whose coordinates the survey team knows in the agreed project reference. Place them across the survey block where practical. Points concentrated along a single line give less information about errors off that line. The goal is to test what the receiving system interprets, so include at least one comparison that uses coordinates recovered after import or read directly from the receiver's data view.
Evaluate the horizontal difference in a common reference with metre units and enough resolution for the tolerance. Suppose a synthetic check point was expected at easting 480,000.0 metres and northing 5,330,000.0 metres. After recovering the imported position and expressing it in the same project grid, the comparison yields 480,002.4 metres east and 5,329,998.2 metres north. The component errors are 2.4 metres east and 1.8 metres south. Their horizontal magnitude is 3.0 metres. A hypothetical two metre limit would flag the handoff, even when the point passed the envelope test.
Keep the signed east and north residuals alongside the magnitudes. Several differences pointing in the same direction can guide an investigation of a shared reference, offset or transformation step. Errors that increase across the block suggest reviewing units, the selected operation or the applicability of the source definition. Treat the pattern as a lead. Reproduce the export and use the underlying source records to test the explanation.
Check the precision of every export used for the comparison. Around latitude 48 degrees, a step of 0.00001 degrees is roughly 1.1 metres north to south and 0.75 metres east to west. Rounding to five decimal places can move the reported coordinate by about half a step on either axis. A planner display rounded to that precision is a poor instrument for a tighter acceptance limit. Retrieve a higher precision value if the software exposes it and record the resolution of the value you actually used.
A round trip through the same library can find obvious loss of precision, but the two directions can share a wrong assumption. If an export declared the wrong source system, an inverse operation using that same declaration may return numbers close to the original and still miss the real check point. Compare against an independent observation or an agreed reference dataset as well as the round trip. Store the coordinates used for the check so a reviewer can reproduce the residuals.

Figure 2. UG21 catalog view showing the aircraft from above. The coordinate checks use the survey records and receiving planner data.
Track height and sequence as their own fields
The example checks horizontal position and expects exactly two ordinates. RFC 7946 defines the optional third position value as height relative to the WGS 84 ellipsoid, in metres. In planning software, a height field can instead refer to a vertical datum, a launch reference or a terrain model. Keep the height value, units and reference together in the accepted handoff. If the receiving format changes the reference, record the transformation or other conversion used and verify it against an agreed check value.
A waypoint can pass the horizontal review while its height is still unresolved. A separate height check keeps that uncertainty visible. When the source uses terrain relative heights, document which terrain data the team used and how the two systems interpret the reference. A small shared example with an input height and an expected receiving value is easier to verify than a general claim that two applications handle heights consistently.
Sequence is a separate check as well. An application can draw every point in the intended place and then sort them by ID during import. Keep an explicit sequence or compare the receiving planner's waypoint order with the approved sequence by ID. Preserve the boundaries between survey blocks if the interchange includes more than one group. If the actual format carries waypoint roles, compare the roles too, since two rows with the same coordinates can have different purposes.
Review the handoff on the receiving side
Ask the two teams to run a small handoff before processing a full project. Include an ordinary on-site point, a syntactically valid reversed pair, an intentional off-site point and a duplicate identifier. If the source works in a projected grid, include a point expressed in that grid and confirm it goes through the intended conversion step. Record how the receiving tool handles each case before relying on its display.
For the real file, compare feature counts and identifiers before and after conversion. Review the exception report against the original project data. Load the checked file into a non-operational workspace in the actual receiving planner and inspect the coordinate fields as well as the map. If the planner provides an export or a documented numerical view, compare the recovered values with the approved inputs at the necessary precision. Save the planner version and import settings with the results.
Include a row by row comparison in the acceptance record when the receiving software can provide the values. List missing and unexpected IDs before calculating differences for the matched rows. Otherwise a count can stay constant when an importer drops one point and substitutes another. Keep rejected revisions identifiable. If an operator receives two files with the same name, the revision and digest let them identify which file was reviewed and prevent a late correction from being confused with an earlier export.
The handover package can be short. Provide the source and accepted export, the coordinate contract, the independently sourced envelope, the exception report, the check point comparison and the receiving-side review. Record who accepted the conversion and which revision they accepted. If the source data or the import settings change, run the same checks on the new revision. The receiving team then has enough evidence to review the coordinates before relying on them for the survey.
About the author
- UNITED UAV Official publishes technical guidance across multirotor, fixed-wing, and VTOL systems, including mapping, inspection, logistics, public safety, agriculture, payload integration, navigation, maintenance, APIs, and automation.
Comments
Join the discussionNo comments yet. Be the first to comment.