Repository navigation
fix: Coordinate comparisons - #131
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #131 +/- ##
=======================================
Coverage ? 96.47%
=======================================
Files ? 10
Lines ? 454
Branches ? 0
=======================================
Hits ? 438
Misses ? 16
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
cgarling
left a comment
There was a problem hiding this comment.
I think this looks good overall. Keeping == strict and pointing users to separation(a, b) < tol seems correct. The inline suggestions fix a precision loss when a projected coordinate's offsets are more precise than its origin and add regression tests.
I noticed that _unprojected_type can also fix an existing bug that this PR didn't introduce. cartesian fails on a nested projection like ProjectedCoords(cp, zero(cp.offset)), because it only looks one origin deep. I couldn't leave a suggestion because the line is outside the diff, but this change to src/projected.jl:81 fixes it (it needs the inline _unprojected_type suggestion applied first):
cartesian(c::AbstractProjectedCoords) = CartesianCoords{_unprojected_type(typeof(c))}(coords2cart(c))Co-authored-by: Chris Garling <[email protected]>
This PR keeps
==strict and documentsseparation(a, b) < tolas the on-sky comparison. Making that the recommended route exposed a bug with projected coordinates, soseparation,position_angle, andoffsetare fixed here as well.Usage
Comparing two descriptions of the same point on the sky:
Projected coordinates now work as the left argument:
Design
Comparison semantics
==keeps its current meaning: same frame, same representation, and exactly equal values. Converting first would not make it reliable, because conversions round. Over 100,000 random ICRS points, a round trip through another frame returned identical values for only 9% to 26% of them, depending on the frame, with a worst error around 1e-15.Converting into the left argument's frame would also make
==asymmetric:convert(GalCoords, c) == cwould betruewhilec == convert(GalCoords, c)would usually befalse. Converting into a fixed frame like ICRS restores symmetry, but returnsfalsefor about 85% of ICRS/Galactic pairs.This matches astropy, which deliberately does no conversion in
==(astropy/astropy#10154), returnsFalseforSkyCoords in different frames starting with 8.1 (astropy/astropy#20266), and points users toseparationfor tolerance checks.≈is unchanged. It is not defined between spherical coordinates in different frames today, so extending it later would not break anything if there is demand.Projected coordinates
The mixed-type methods converted the right argument to the left argument's type, which cannot work for a projected left argument because its origin is a value rather than a type parameter.
separationandposition_anglenow compare in the origin's frame instead, following nested projections through_unprojected_type. The two-coordinateoffsetis built from those two, so it needs no change of its own. Every other coordinate type maps to itself, and same-typeconvertreturns its input, so existing results do not change.offset(p, sep, pa)documents that it returns the input's type, so the result is re-projected around the same origin, sharing its arithmetic withprojectthrough_project_offset. This is handled inside the existingoffsetmethod because a dedicated::AbstractProjectedCoordsmethod would be ambiguous with the DynamicQuantities extension'soffsetmethods.