Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[core] Tighten LatLng and other geo.hpp class invariants #5286

Merged
merged 8 commits into from
Apr 6, 2017

Conversation

jfirebaugh
Copy link
Contributor

  • Remove LatLng::null and enforce non-NaN, non-infinite coordinates and -90 <= lat <= 90
  • Remove unnecessary operator bool()

This has caught two bugs so far:

  • lat/lng swapping in TileJSON center and bounds
  • Wrong w₀ value in Transform::flyTo

Fixes #3802. Reroll of #4355.

@brunoabinader
Copy link
Member

@jfirebaugh want to apply this before #8601?

@jfirebaugh
Copy link
Contributor Author

I rebased this on master, but I think it needs attention at the SDK binding point for LatLng -- we want the SDK to enforce the same invariants, but report errors using a platform-appropriate exception mechanism (not std::domain_error).

if (std::isnan(coordinate.longitude)) {
[NSException raise:NSInvalidArgumentException format:@"longitude must not be NaN"];
}
if (std::abs(coordinate.latitude) > 90.0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we’ll want to push these checks further up. In many places where we call this function, the analogous MapKit API fails silently when given out-of-bounds coordinates; an exception would be heavy-handed. In other cases, a debug assertion is all that’s needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be more specific about what "further up" means in this context, or what specific changes you'd recommend?

Copy link
Contributor

@1ec5 1ec5 Apr 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion was to move these checks to individual call sites, or to add a “checked” version of this function that wraps MGLLatLngFromLocationCoordinate2D(), adding these checks.

I reviewed the existing call sites to MGLLatLngFromLocationCoordinate2D() and tested similar functionality in MapKit using invalid coordinates. Based on MapKit behavior, having the exceptions where they are is OK, although we’ll want to add identical preflight checks in methods like -[MGLMapView convertCoordinate:toPointToView:] that return things like CGPointMake(NAN, NAN).

Mapbox call site MapKit equivalent MapKit behavior Mapbox expected behavior
-[MGLMapView handlePinchGesture:] Constrained NSAssert (but NSException is fine)
-[MGLMapView _setCenterCoordinate:edgePadding:zoomLevel:direction:duration:animationTimingFunction:completionHandler:] -[MKMapView setRegion:animated:] NSException NSException
-[MGLMapView cameraOptionsObjectForAnimatingToCamera:edgePadding:] -[MKMapView setCamera:animated:] NSException NSException
-[MGLMapView convertCoordinate:toPointToView:] -[MKMapView convertCoordinate:toPointToView:] CGPoint(NaN, NaN) CGPoint(NaN, NaN)
-[MGLMapView showAnnotations:edgePadding:animated:] -[MKMapView showAnnotations:animated:] Moves to invalid part of map, in ocean NSException is OK
-[MGLMapView directionByFollowingWithCourse] NSException
+[MGLMapCamera cameraLookingAtCenterCoordinate:fromEyeCoordinate:eyeAltitude:] +[MKMapCamera cameraLookingAtCenterCoordinate:fromEyeCoordinate:eyeAltitude:] Camera with invalid coordinates Camera with invalid coordinates
-[MGLMultiPoint overlayBounds] -[MKMultiPoint boundingMapRect] Empty MKMapRect with invalid origin ?
-[MGLPointCollection overlayBounds] ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ea20dd5 adds preflight checks to some of the call sites above that avoid the exception and instead return special values indicating invalidity. If you try to convert an invalid coordinate to a point, you get an invalid point. You can create a camera with an invalid coordinate, but you’ll get an exception when you try to move the map to that camera. You can create a polyline, polygon, or point collection with an invalid coordinate, and the overlay will be invalid, but you’ll get an exception when you try to add that shape to the map.

state.size.height - padding.top - padding.bottom);
double w0 = padding ? std::max(state.size.width - padding->left - padding->right,
state.size.height - padding->top - padding->bottom)
: std::max(state.size.width, state.size.height);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, great catch! I wonder if this makes #8300 more difficult to hit. Hard to say just from inspecting the code, since r() is a bit convoluted.

@@ -21,17 +22,29 @@ using ScreenBox = mapbox::geometry::box<double>;

class LatLng {
public:
struct null {};

double latitude;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we mark latitude and longitude as const here? We validate their values when initializing LatLng, but these are free to be modified after without checks.

Another option is to move these checks to a setter, and having these private.

Copy link
Contributor

@ivovandongen ivovandongen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 on the android parts

1ec5 added 2 commits April 6, 2017 08:28
Replaced preflight checks in MGLLatLngFromLocationCoordinate2D() with a try-catch block that relays exceptions raised by mbgl. De-inlined the function because it’s no longer trivial.
Invalid coordinates no longer cause an exception to be raised immediately when used in conversion methods and model objects. Instead, the appropriate invalid values are used, consistent with MapKit. Exceptions are still raised when invalid model objects are used with the map.
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the changes in ea20dd5 to fail gracefully on certain computational operations, the iOS/macOS side looks good.

@jfirebaugh jfirebaugh merged commit 8382420 into master Apr 6, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants