Skip to content

[Detail Bug] Location equality ignores geographic equivalence (antimeridian, poles, -0.0) #45

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_befd6425-a158-4e24-9d4d-1e5c08769515/bugs/bug_bb05317b-77ab-4610-8b17-45c2c2e99426

Summary

  • Context: The Location record is a fundamental domain model used throughout the SDK to represent geographic coordinates (latitude and longitude).
  • Bug: The Location record uses the default Java record equals() and hashCode() implementations, which perform a bitwise comparison of the double fields. This fails to account for geographic equivalence where different coordinate pairs represent the same physical location.
  • Actual vs. expected: Currently, Location objects with equivalent coordinates (e.g., longitude 180° vs -180°, or different longitudes at the poles) are considered unequal; they are expected to be equal geographically.
  • Impact: Using Location as a key in collections (like HashMap or HashSet) will lead to duplicate entries for the same geographic point, and comparisons between locations returned from different API calls may fail unexpectedly.

Code with bug

public record Location(double latitude, double longitude) { // <-- BUG 🔴 [Uses default record equality which doesn't account for geographic equivalence]
    private static final double MIN_LATITUDE = -90.0;
    private static final double MAX_LATITUDE = 90.0;
    private static final double MIN_LONGITUDE = -180.0;
    private static final double MAX_LONGITUDE = 180.0;
    // ...
    public Location {
        validateLatitudeLongitude(latitude, longitude);
    }
    // ...
}

Failing test

Antimeridian case showing inequivalence of equivalent coordinates:

LongitudeEqualityTest > antimeridianEquality() FAILED
    org.opentest4j.AssertionFailedError: Location(0.0, 180.0) should equal Location(0.0, -180.0) ==> expected: <Location[latitude=0.0, longitude=180.0]> but was: <Location[latitude=0.0, longitude=-180.0]>

Recommended fix

Normalize coordinates in the compact constructor of the Location record to ensure a canonical representation:

  • Normalize longitude to a standard range (e.g., 180.0 and -180.0 should both become 180.0).
  • If latitude is 90.0 or -90.0 (the poles), set longitude to a constant value (e.g., 0.0).
  • Convert negative zero (-0.0) to positive zero (0.0) for both latitude and longitude.
    public Location {
        validateLatitudeLongitude(latitude, longitude);
        
        // Normalize negative zero
        if (latitude == -0.0) latitude = 0.0; // <-- FIX 🟢
        if (longitude == -0.0) longitude = 0.0; // <-- FIX 🟢
        
        // Normalize poles: at the poles, longitude is irrelevant.
        if (latitude == 90.0 || latitude == -90.0) {
            longitude = 0.0;
        } else if (longitude == -180.0) {
            // Normalize antimeridian to 180.0
            longitude = 180.0;
        }
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions