Skip to content

fix: report null instead of fabricated zeros for unavailable location fields - #1104

Open
Lyokone wants to merge 2 commits into
masterfrom
fix/null-instead-of-fabricated-zeros
Open

fix: report null instead of fabricated zeros for unavailable location fields#1104
Lyokone wants to merge 2 commits into
masterfrom
fix/null-instead-of-fabricated-zeros

Conversation

@Lyokone

@Lyokone Lyokone commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

Every platform had at least one field where "this fix does not carry that measurement" was indistinguishable from a real reading — of zero, due north, stationary, or perfectly accurate. LocationData has always documented these fields as double? / "will be null if not available"; this makes the native side actually honour that.

The iOS speed sentinel fix (#741) covered one field on one platform. This extends the same treatment to every scalar on every platform.

Before → after, per platform

Was reported Now
Android 0.0 from getSpeed()/getBearing()/getAccuracy()/… on a fix that carries no such field key omitted, gated on the matching Location.hasX()
iOS/macOS raw negative sentinel for horizontalAccuracy, verticalAccuracy, speedAccuracy, course; altitude always sent nil; altitude follows verticalAccuracy, which is what Core Location documents as marking it invalid
Windows heading/speed forced to 0.0; altitude always sent omitted when the IReference<double> is null; altitude gated on AltitudeAccuracy, verticalAccuracy now forwarded
Linux GeoClue2 sentinels forwarded verbatim — -1 speed/heading, altitude of -1.797e308 recognised and omitted; time now populated from Timestamp
Web heading as NaN when stationary non-finite readings normalised to null

Windows and Linux matter most in practice: without a GPS radio both position by Wi-Fi/IP, so every fix hit the bad path — Windows reported sea level, Linux reported an altitude 1.8e308 metres below the ellipsoid.

The web case is subtler than a fake zero. The Geolocation spec specifies heading as NaN, not null, when the device is stationary; since NaN != NaN, that made two otherwise identical LocationData compare unequal with unstable hashCodes.

Also included

  • iOS/macOS: a fix whose horizontalAccuracy is negative is skipped rather than delivered — Core Location documents that as meaning the coordinate itself is invalid, and latitude/longitude are non-nullable, so it cannot be passed on without inventing one. didUpdateLocations takes the last valid fix in a batch, so this cannot drop an update that had a good fix available.
  • Android: the NMEA mean-sea-level altitude is only applied to a fix that has an altitude to correct, and the NMEA caches are cleared when the listener detaches. It is a correction of the fix's own WGS 84 altitude, not a stand-in for a missing one, so a stale GPS reading could previously surface as a network-provider fix's current altitude.

Behaviour change to be aware of

isMock and isProducedByAccessory are now null when the platform exposes no such flag, instead of defaulting to false — "nothing checked" and "checked, and it was not mocked" are different claims. Android now always reports isMock; Apple reports both on iOS 15.0+/macOS 12.0+; Windows/Linux/web report null.

Nothing stops compiling — the affected fields were already nullable — but an app that force-unwraps (data.speed!, data.isMock!) or that treated the old 0 as a real reading will now see a null. Documented under ## Unreleased in all three changelogs; version bumps left as a separate commit per the usual flow.

Verification

Platform Check Result
Android compileDebugKotlin + lintDebug compiles; lint identical to baseline (4 errors / 7 warnings, no new NewApi)
iOS/macOS swiftc -typecheck against the engine framework clean
Linux location desktop CI clean, no compiler diagnostics
Windows location desktop CI clean, no compiler diagnostics
Dart/web flutter analyze, 50 tests, dart format clean

New tests cover the sparse-map path (all optionals null) and reported-0 vs absent flags, in both the double shape Android sends and the int shape Apple sends. One existing test asserted the old isProducedByAccessory default of false and was updated.

Not runtime-verified: GeoClue2's sentinel values and the AltitudeAccuracy-is-null-when-no-altitude behaviour are from the respective platform docs. Both compile, but the desktop workflow only builds — confirming them behaviourally needs a real Linux desktop and a Windows machine without GPS.

Lyokone added 2 commits August 7, 2026 09:38
Every platform had at least one field where "this fix does not carry that
measurement" was indistinguishable from a real reading of zero, due north,
stationary, or perfectly accurate.

- Android: gate each field on its Location.hasX() companion.
- iOS/macOS: map Core Location's negative sentinel to nil for every scalar,
  not just speed (#741); altitude follows verticalAccuracy, which is what
  marks it invalid. Skip fixes whose horizontalAccuracy is negative, since
  that means the coordinate itself is invalid and lat/long are non-nullable.
- Windows: omit heading/speed when their IReference is null, and gate
  altitude on AltitudeAccuracy -- BasicGeoposition.Altitude is a plain double
  that cannot express "unknown", so Wi-Fi/IP fixes reported sea level. Also
  forward verticalAccuracy.
- Linux: recognise GeoClue2's sentinels (-1 for Speed/Heading/Accuracy,
  -G_MAXDOUBLE for Altitude) instead of forwarding them, and populate time
  from the Timestamp property.
- Web: normalise non-finite readings to null. The Geolocation spec makes
  heading NaN, not null, when stationary, and NaN made two identical
  LocationData compare unequal with unstable hashCodes.

Also: isMock/isProducedByAccessory stay null when the platform exposes no
such flag rather than defaulting to false, and Android no longer applies a
stale NMEA mean-sea-level altitude to a fix that carries none of its own.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant