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

Commit

Permalink
[ios,macos] multipolygon coordinate (#8713)
Browse files Browse the repository at this point in the history
* [ios] MGLMultiPolygon's coordinate property implemented

* [ios,macos] Add polylabalel to project config

* [ios,macos] Change coordinate property for MGLPolyline,MGLPolygon,MGLMultiPolygon

* [macos] Change project configuration to support polilabel

* [ios,macos] Add MGLLocationCoordinate2DFromPoint

* [ios, macos] Update changelogs

* [ios, macos] remove unnecesary variables

* [ios, macos] Add radians conversions

* [ios, macos] Add coordinate calc to MGLPolyline

* [ios, macos] Move radian fuctions to MGLGeometry_Private.h

* [ios, macos] Fix code style

* [ios, macos] Fix code indentation

* [ios, macos] Fix radian per meters constant

* [ios, macos] Add test scenarios to testPolyline

* [ios, macos] Fix test accuracy
  • Loading branch information
fabian-guerra authored May 23, 2017
1 parent fe17f7e commit a57e9bc
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 14 deletions.
64 changes: 64 additions & 0 deletions platform/darwin/src/MGLGeometry_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,26 @@
#import <mbgl/util/geo.hpp>
#import <mbgl/util/geometry.hpp>

typedef double MGLLocationRadians;
typedef double MGLRadianDistance;
typedef double MGLRadianDirection;

/** Defines the coordinate by a `MGLRadianCoordinate2D`. */
typedef struct MGLRadianCoordinate2D {
MGLLocationRadians latitude;
MGLLocationRadians longitude;
} MGLRadianCoordinate2D;

/**
Creates a new `MGLRadianCoordinate2D` from the given latitudinal and longitudinal.
*/
NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinate2DMake(MGLLocationRadians latitude, MGLLocationRadians longitude) {
MGLRadianCoordinate2D radianCoordinate;
radianCoordinate.latitude = latitude;
radianCoordinate.longitude = longitude;
return radianCoordinate;
}

/// Returns the smallest rectangle that contains both the given rectangle and
/// the given point.
CGRect MGLExtendRect(CGRect rect, CGPoint point);
Expand All @@ -18,6 +38,10 @@ NS_INLINE mbgl::Point<double> MGLPointFromLocationCoordinate2D(CLLocationCoordin
return mbgl::Point<double>(coordinate.longitude, coordinate.latitude);
}

NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromPoint(mbgl::Point<double> point) {
return CLLocationCoordinate2DMake(point.y, point.x);
}

NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng) {
return CLLocationCoordinate2DMake(latLng.latitude(), latLng.longitude());
}
Expand Down Expand Up @@ -59,3 +83,43 @@ CLLocationDistance MGLAltitudeForZoomLevel(double zoomLevel, CGFloat pitch, CLLo
@param size The size of the viewport.
@return A zero-based zoom level. */
double MGLZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch, CLLocationDegrees latitude, CGSize size);

/** Returns MGLRadianCoordinate2D, converted from CLLocationCoordinate2D. */
NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateFromLocationCoordinate(CLLocationCoordinate2D locationCoordinate) {
return MGLRadianCoordinate2DMake(MGLRadiansFromDegrees(locationCoordinate.latitude),
MGLRadiansFromDegrees(locationCoordinate.longitude));
}

/*
Returns the distance in radians given two coordinates.
*/
NS_INLINE MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to)
{
double a = pow(sin((to.latitude - from.latitude) / 2), 2)
+ pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);

return 2 * atan2(sqrt(a), sqrt(1 - a));
}

/*
Returns direction in radians given two coordinates.
*/
NS_INLINE MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
double a = sin(to.longitude - from.longitude) * cos(to.latitude);
double b = cos(from.latitude) * sin(to.latitude)
- sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
return atan2(a, b);
}

/*
Returns coordinate at a given distance and direction away from coordinate.
*/
NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
MGLRadianDistance distance,
MGLRadianDirection direction) {
double otherLatitude = asin(sin(coordinate.latitude) * cos(distance)
+ cos(coordinate.latitude) * sin(distance) * cos(direction));
double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude);
}
20 changes: 19 additions & 1 deletion platform/darwin/src/MGLPolygon.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#import "MGLPolygon+MGLAdditions.h"

#import <mbgl/util/geojson.hpp>
#import <mapbox/polylabel.hpp>

@implementation MGLPolygon

Expand Down Expand Up @@ -54,6 +55,13 @@ - (NSUInteger)hash {
return [super hash] + [[self geoJSONDictionary] hash];
}

- (CLLocationCoordinate2D)coordinate {
// pole of inaccessibility
auto poi = mapbox::polylabel([self polygon]);

return MGLLocationCoordinate2DFromPoint(poi);
}

- (mbgl::LinearRing<double>)ring {
NSUInteger count = self.pointCount;
CLLocationCoordinate2D *coordinates = self.coordinates;
Expand Down Expand Up @@ -155,11 +163,17 @@ - (NSUInteger)hash {
return hash;
}

- (CLLocationCoordinate2D)coordinate {
MGLPolygon *firstPolygon = self.polygons.firstObject;

return firstPolygon.coordinate;
}

- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}

- (mbgl::Geometry<double>)geometryObject {
- (mbgl::MultiPolygon<double>)multiPolygon {
mbgl::MultiPolygon<double> multiPolygon;
multiPolygon.reserve(self.polygons.count);
for (MGLPolygon *polygon in self.polygons) {
Expand All @@ -173,6 +187,10 @@ - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return multiPolygon;
}

- (mbgl::Geometry<double>)geometryObject {
return [self multiPolygon];
}

- (NSDictionary *)geoJSONDictionary {
NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.polygons.count];
for (MGLPolygonFeature *feature in self.polygons) {
Expand Down
65 changes: 65 additions & 0 deletions platform/darwin/src/MGLPolyline.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#import "MGLPolyline+MGLAdditions.h"

#import <mbgl/util/geojson.hpp>
#import <mapbox/polylabel.hpp>

@implementation MGLPolyline

Expand Down Expand Up @@ -52,6 +53,61 @@ - (BOOL)isEqual:(id)other {
return self == other || ([other isKindOfClass:[MGLPolyline class]] && [super isEqual:other]);
}

- (CLLocationCoordinate2D)coordinate {
NSUInteger count = self.pointCount;
NSAssert(count > 0, @"Polyline must have coordinates");

CLLocationCoordinate2D *coordinates = self.coordinates;
CLLocationDistance middle = [self length] / 2.0;
CLLocationDistance traveled = 0.0;

if (count > 1 || middle > traveled) {
for (NSUInteger i = 0; i < count; i++) {

MGLRadianCoordinate2D from = MGLRadianCoordinateFromLocationCoordinate(coordinates[i]);
MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1]);

if (traveled >= middle) {
double overshoot = middle - traveled;
if (overshoot == 0) {
return coordinates[i];
}
to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i - 1]);
CLLocationDirection direction = [self direction:from to:to] - 180;
MGLRadianCoordinate2D otherCoordinate = MGLRadianCoordinateAtDistanceFacingDirection(from,
overshoot/mbgl::util::EARTH_RADIUS_M,
MGLRadiansFromDegrees(direction));
return CLLocationCoordinate2DMake(MGLDegreesFromRadians(otherCoordinate.latitude),
MGLDegreesFromRadians(otherCoordinate.longitude));
}

traveled += (MGLDistanceBetweenRadianCoordinates(from, to) * mbgl::util::EARTH_RADIUS_M);

}
}

return coordinates[count - 1];
}

- (CLLocationDistance)length
{
CLLocationDistance length = 0.0;

NSUInteger count = self.pointCount;
CLLocationCoordinate2D *coordinates = self.coordinates;

for (NSUInteger i = 0; i < count - 1; i++) {
length += (MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinateFromLocationCoordinate(coordinates[i]), MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1])) * mbgl::util::EARTH_RADIUS_M);
}

return length;
}

- (CLLocationDirection)direction:(MGLRadianCoordinate2D)from to:(MGLRadianCoordinate2D)to
{
return MGLDegreesFromRadians(MGLRadianCoordinatesDirection(from, to));
}

@end

@interface MGLMultiPolyline ()
Expand Down Expand Up @@ -114,6 +170,15 @@ - (NSUInteger)hash {
return hash;
}

- (CLLocationCoordinate2D)coordinate {
MGLPolyline *polyline = self.polylines.firstObject;
CLLocationCoordinate2D *coordinates = polyline.coordinates;
NSAssert([polyline pointCount] > 0, @"Polyline must have coordinates");
CLLocationCoordinate2D firstCoordinate = coordinates[0];

return firstCoordinate;
}

- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
Expand Down
119 changes: 116 additions & 3 deletions platform/darwin/test/MGLCodingTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,54 @@ - (void)testPolyline {
[unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates];

XCTAssertNotEqualObjects(polyline, unarchivedPolyline);

CLLocationCoordinate2D multiLineCoordinates[] = {
CLLocationCoordinate2DMake(51.000000, 0.000000),
CLLocationCoordinate2DMake(51.000000, 1.000000),
CLLocationCoordinate2DMake(51.000000, 2.000000),
};

NSUInteger multiLineCoordinatesCount = sizeof(multiLineCoordinates) / sizeof(CLLocationCoordinate2D);
MGLPolyline *multiLine = [MGLPolyline polylineWithCoordinates:multiLineCoordinates count:multiLineCoordinatesCount];
CLLocationCoordinate2D multiLineCenter = CLLocationCoordinate2DMake(51.000000, 1.000000);

XCTAssertEqual([multiLine coordinate].latitude, multiLineCenter.latitude);
XCTAssertEqual([multiLine coordinate].longitude, multiLineCenter.longitude);

CLLocationCoordinate2D segmentCoordinates[] = {
CLLocationCoordinate2DMake(35.040390, -85.311477),
CLLocationCoordinate2DMake(35.040390, -85.209510),
};

NSUInteger segmentCoordinatesCount = sizeof(segmentCoordinates) / sizeof(CLLocationCoordinate2D);
MGLPolyline *segmentLine = [MGLPolyline polylineWithCoordinates:segmentCoordinates count:segmentCoordinatesCount];
CLLocationCoordinate2D segmentCenter = CLLocationCoordinate2DMake(35.0404006631, -85.2604935);

XCTAssertEqualWithAccuracy([segmentLine coordinate].latitude, segmentCenter.latitude, 0.0001);
XCTAssertEqualWithAccuracy([segmentLine coordinate].longitude, segmentCenter.longitude, 0.0001);

CLLocationCoordinate2D sfToBerkeleyCoordinates[] = {
CLLocationCoordinate2DMake(37.782440, -122.397111),
CLLocationCoordinate2DMake(37.818384, -122.352994),
CLLocationCoordinate2DMake(37.831401, -122.274545),
CLLocationCoordinate2DMake(37.862172, -122.262700),
};

NSUInteger sfToBerkeleyCoordinatesCount = sizeof(sfToBerkeleyCoordinates) / sizeof(CLLocationCoordinate2D);
MGLPolyline *sfToBerkeleyLine = [MGLPolyline polylineWithCoordinates:sfToBerkeleyCoordinates count:sfToBerkeleyCoordinatesCount];
CLLocationCoordinate2D sfToBerkeleyCenter = CLLocationCoordinate2DMake(37.8230575118,-122.324867587);

XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].latitude, sfToBerkeleyCenter.latitude, 0.0001);
XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].longitude, sfToBerkeleyCenter.longitude, 0.0001);

}

- (void)testPolygon {
CLLocationCoordinate2D coordinates[] = {
CLLocationCoordinate2DMake(0.664482398, 1.8865675),
CLLocationCoordinate2DMake(2.13224687, 3.9984632)
CLLocationCoordinate2DMake(35.090745, -85.300259),
CLLocationCoordinate2DMake(35.092035, -85.298885),
CLLocationCoordinate2DMake(35.090639, -85.297416),
CLLocationCoordinate2DMake(35.089112, -85.298928)
};

NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
Expand All @@ -84,8 +126,24 @@ - (void)testPolygon {
[NSKeyedArchiver archiveRootObject:polygon toFile:filePath];

MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
[unarchivedPolygon coordinate];

XCTAssertEqualObjects(polygon, unarchivedPolygon);

CLLocationCoordinate2D squareCoordinates[] = {
CLLocationCoordinate2DMake(100.0, 0.0),
CLLocationCoordinate2DMake(101.0, 0.0),
CLLocationCoordinate2DMake(101.0, 1.0),
CLLocationCoordinate2DMake(100.0, 1.0),
};

NSUInteger squareCoordinatesCount = sizeof(squareCoordinates) / sizeof(CLLocationCoordinate2D);
MGLPolygon *squarePolygon = [MGLPolygon polygonWithCoordinates:squareCoordinates count:squareCoordinatesCount];
CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5);

XCTAssertEqual([squarePolygon coordinate].latitude, squareCenter.latitude);
XCTAssertEqual([squarePolygon coordinate].longitude, squareCenter.longitude);

}

- (void)testPolygonWithInteriorPolygons {
Expand Down Expand Up @@ -169,6 +227,11 @@ - (void)testPointCollection {
NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);

MGLPointCollection *pointCollection = [MGLPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
CLLocationCoordinate2D pointsCenter = CLLocationCoordinate2DMake(0, 1);

XCTAssertEqual([pointCollection coordinate].latitude, pointsCenter.latitude);
XCTAssertEqual([pointCollection coordinate].longitude, pointsCenter.longitude);

NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollection class]];
[NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath];

Expand Down Expand Up @@ -218,6 +281,30 @@ - (void)testMultiPolyline {
CLLocationCoordinate2DMake(20, 21),
CLLocationCoordinate2DMake(30, 31),
};

CLLocationCoordinate2D line1[] = {
CLLocationCoordinate2DMake(100, 40),
CLLocationCoordinate2DMake(105, 45),
CLLocationCoordinate2DMake(110, 55)
};

CLLocationCoordinate2D line2[] = {
CLLocationCoordinate2DMake(105, 40),
CLLocationCoordinate2DMake(110, 45),
CLLocationCoordinate2DMake(115, 55)
};

NSUInteger road1CoordinatesCount = sizeof(line1) / sizeof(CLLocationCoordinate2D);
NSUInteger road2CoordinatesCount = sizeof(line2) / sizeof(CLLocationCoordinate2D);

MGLPolyline *road1Polyline = [MGLPolyline polylineWithCoordinates:line1 count:road1CoordinatesCount];
MGLPolyline *road2Polyline = [MGLPolyline polylineWithCoordinates:line1 count:road2CoordinatesCount];

MGLMultiPolyline *roads = [MGLMultiPolyline multiPolylineWithPolylines:@[road1Polyline, road2Polyline]];
CLLocationCoordinate2D roadCenter = CLLocationCoordinate2DMake(100, 40);

XCTAssertEqual([roads coordinate].latitude, roadCenter.latitude);
XCTAssertEqual([roads coordinate].longitude, roadCenter.longitude);

NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);

Expand Down Expand Up @@ -248,6 +335,31 @@ - (void)testMultiPolygon {
CLLocationCoordinate2DMake(20, 21),
CLLocationCoordinate2DMake(30, 31),
};

CLLocationCoordinate2D outerSquare[] = {
CLLocationCoordinate2DMake(100.0, 0.0),
CLLocationCoordinate2DMake(101.0, 0.0),
CLLocationCoordinate2DMake(101.0, 1.0),
CLLocationCoordinate2DMake(100.0, 1.0),
};

CLLocationCoordinate2D innerSquare[] = {
CLLocationCoordinate2DMake(100.35, 0.35),
CLLocationCoordinate2DMake(100.65, 0.35),
CLLocationCoordinate2DMake(100.65, 0.65),
CLLocationCoordinate2DMake(100.35, 0.65),
};

NSUInteger outerCoordinatesCount = sizeof(outerSquare) / sizeof(CLLocationCoordinate2D);
NSUInteger innerCoordinatesCount = sizeof(innerSquare) / sizeof(CLLocationCoordinate2D);

MGLPolygon *innerPolygonSquare = [MGLPolygon polygonWithCoordinates:innerSquare count:innerCoordinatesCount];
MGLPolygon *outerPolygonSquare = [MGLPolygon polygonWithCoordinates:outerSquare count:outerCoordinatesCount interiorPolygons:@[innerPolygonSquare]];
MGLMultiPolygon *squares = [MGLMultiPolygon multiPolygonWithPolygons:@[outerPolygonSquare, innerPolygonSquare]];
CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5);

XCTAssertEqual([squares coordinate].latitude, squareCenter.latitude);
XCTAssertEqual([squares coordinate].longitude, squareCenter.longitude);

NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);

Expand All @@ -265,9 +377,10 @@ - (void)testMultiPolygon {

MGLMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
MGLMultiPolygon *anotherMultiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];

XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon);
XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon);

}

- (void)testShapeCollection {
Expand Down
1 change: 1 addition & 0 deletions platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Other changes

* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775))
* Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713)
* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773))
* Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562))
* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418))
Expand Down
Loading

0 comments on commit a57e9bc

Please sign in to comment.