Test fit to bounds; added MGLCoordinateBounds functions

Added a bunch of functions to work with MGLCoordinateBounds in a separate header analogous to MKGeometry.h. Added resolution-independent tests for common fit to bounds scenarios.
This commit is contained in:
Minh Nguyễn
2015-06-25 11:58:23 -07:00
parent 68e404c522
commit 1d11624efd
9 changed files with 131 additions and 16 deletions

View File

@@ -36,6 +36,8 @@
'../platform/ios/MGLUserLocationAnnotationView.m',
'../include/mbgl/ios/MGLTypes.h',
'../platform/ios/MGLTypes.m',
'../include/mbgl/ios/MGLGeometry.h',
'../platform/ios/MGLGeometry.m',
'../include/mbgl/ios/MGLMultiPoint.h',
'../platform/ios/MGLMultiPoint_Private.h',
'../platform/ios/MGLMultiPoint.mm',

View File

@@ -0,0 +1,85 @@
#pragma once
#import "MGLTypes.h"
#import <CoreLocation/CoreLocation.h>
NS_ASSUME_NONNULL_BEGIN
/** Defines the area spanned by an MGLCoordinateBounds. */
typedef struct {
CLLocationDegrees latitudeDelta;
CLLocationDegrees longitudeDelta;
} MGLCoordinateSpan;
/** Creates a new MGLCoordinateSpan from the given latitudinal and longitudinal deltas. */
NS_INLINE MGLCoordinateSpan MGLCoordinateSpanMake(CLLocationDegrees latitudeDelta, CLLocationDegrees longitudeDelta) {
MGLCoordinateSpan span;
span.latitudeDelta = latitudeDelta;
span.longitudeDelta = longitudeDelta;
return span;
}
/** Returns `YES` if the two coordinate spans represent the same latitudinal change and the same longitudinal change. */
NS_INLINE BOOL MGLCoordinateSpanEqualToCoordinateSpan(MGLCoordinateSpan span1, MGLCoordinateSpan span2) {
return (span1.latitudeDelta == span2.latitudeDelta &&
span1.longitudeDelta == span2.longitudeDelta);
}
/** An area of zero width and zero height. */
extern const MGLCoordinateSpan MGLCoordinateSpanZero;
/** A rectangular area as measured on a two-dimensional map projection. */
typedef struct {
CLLocationCoordinate2D sw;
CLLocationCoordinate2D ne;
} MGLCoordinateBounds;
/** Creates a new MGLCoordinateBounds structure from the given southwest and northeast coordinates. */
NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsMake(CLLocationCoordinate2D sw, CLLocationCoordinate2D ne) {
MGLCoordinateBounds bounds;
bounds.sw = sw;
bounds.ne = ne;
return bounds;
}
/** Returns `YES` if the two coordinate bounds are equal to each other. */
NS_INLINE BOOL MGLCoordinateBoundsEqualToCoordinateBounds(MGLCoordinateBounds bounds1, MGLCoordinateBounds bounds2) {
return (bounds1.sw.latitude == bounds2.sw.latitude &&
bounds1.sw.longitude == bounds2.sw.longitude &&
bounds1.ne.latitude == bounds2.ne.latitude &&
bounds1.ne.longitude == bounds2.ne.longitude);
}
/** Returns the area spanned by the coordinate bounds. */
NS_INLINE MGLCoordinateSpan MGLCoordinateBoundsGetCoordinateSpan(MGLCoordinateBounds bounds) {
return MGLCoordinateSpanMake(bounds.ne.latitude - bounds.sw.latitude,
bounds.ne.longitude - bounds.sw.longitude);
}
/** Returns a coordinate bounds with southwest and northeast coordinates that are offset from those of the source bounds. */
NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsOffset(MGLCoordinateBounds bounds, MGLCoordinateSpan offset) {
MGLCoordinateBounds offsetBounds = bounds;
offsetBounds.sw.latitude += offset.latitudeDelta;
offsetBounds.sw.longitude += offset.longitudeDelta;
offsetBounds.ne.latitude += offset.latitudeDelta;
offsetBounds.ne.longitude += offset.longitudeDelta;
return offsetBounds;
}
/** Returns `YES` if the coordinate bounds covers no area.
*
* Note that a bounds may be empty but have a non-zero coordinate span (e.g., when its northeast point lies due north of its southwest point). */
NS_INLINE BOOL MGLCoordinateBoundsIsEmpty(MGLCoordinateBounds bounds) {
MGLCoordinateSpan span = MGLCoordinateBoundsGetCoordinateSpan(bounds);
return span.latitudeDelta == 0 || span.longitudeDelta == 0;
}
/** Returns a formatted string for the given coordinate bounds. */
NS_INLINE NSString *MGLStringFromCoordinateBounds(MGLCoordinateBounds bounds) {
return [NSString stringWithFormat:@"{{%.1f, %.1f}, {%.1f, %.1f}}",
bounds.sw.latitude, bounds.sw.longitude,
bounds.ne.latitude, bounds.ne.longitude];
}
NS_ASSUME_NONNULL_END

View File

@@ -1,4 +1,4 @@
#import "MGLTypes.h"
#import "MGLGeometry.h"
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

View File

@@ -2,7 +2,7 @@
#import <CoreLocation/CoreLocation.h>
#import "MGLAnnotation.h"
#import "MGLTypes.h"
#import "MGLGeometry.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -1,5 +1,6 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#pragma once
#if !__has_feature(nullability)
#define NS_ASSUME_NONNULL_BEGIN
@@ -23,18 +24,6 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) {
MGLUserTrackingModeFollowWithHeading
};
typedef struct {
CLLocationCoordinate2D sw;
CLLocationCoordinate2D ne;
} MGLCoordinateBounds;
NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsMake(CLLocationCoordinate2D sw, CLLocationCoordinate2D ne) {
MGLCoordinateBounds bounds;
bounds.sw = sw;
bounds.ne = ne;
return bounds;
}
NS_ASSUME_NONNULL_END
#pragma clang diagnostic push

View File

@@ -1,5 +1,6 @@
#import "MGLAccountManager.h"
#import "MGLAnnotation.h"
#import "MGLGeometry.h"
#import "MGLMapView.h"
#import "MGLMultiPoint.h"
#import "MGLOverlay.h"

View File

@@ -0,0 +1,3 @@
#import "MGLGeometry.h"
const MGLCoordinateSpan MGLCoordinateSpanZero = {0, 0};

View File

@@ -1,5 +1,5 @@
#import "MGLMultiPoint.h"
#import "MGLTypes.h"
#import "MGLGeometry.h"
#import <mbgl/util/geo.hpp>

View File

@@ -150,6 +150,41 @@
@"disabling zoom gesture should disallow pinching");
}
- (void)testFitToBounds {
// No-op
MGLCoordinateBounds initialBounds = tester.mapView.visibleCoordinateBounds;
[tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
@"setting visible coordinate bounds to currently visible coordinate bounds should be a no-op");
// Roundtrip after zooming
tester.mapView.zoomLevel -= 3;
[tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
@"after zooming out, setting visible coordinate bounds back to %@ should not leave them at %@",
MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
tester.mapView.zoomLevel += 3;
[tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
@"after zooming in, setting visible coordinate bounds back to %@ should not leave them at %@",
MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
// Roundtrip after panning
MGLCoordinateBounds offsetBounds = MGLCoordinateBoundsOffset(initialBounds, MGLCoordinateSpanMake(0, 30));
[tester.mapView setVisibleCoordinateBounds:offsetBounds animated:NO];
[tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
@"after panning 30° to the east, setting visible coordinate bounds back to %@ should not leave them at %@",
MGLStringFromCoordinateBounds(initialBounds),
MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
}
- (void)testPan {
CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;