diff options
-rw-r--r-- | include/mbgl/ios/MGLMapView.h | 7 | ||||
-rw-r--r-- | include/mbgl/map/map.hpp | 1 | ||||
-rw-r--r-- | platform/ios/MGLMapView.mm | 22 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 42 | ||||
-rw-r--r-- | test/ios/MapViewTests.m | 21 |
5 files changed, 76 insertions, 17 deletions
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index d426fd0869..346bb62257 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -134,6 +134,13 @@ IB_DESIGNABLE * @param animated Specify `YES` to animate the change by smoothly scrolling and zooming or `NO` to immediately display the given bounds. */ - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; +/** Changes the receiver’s viewport to fit all of the given coordinates and optionally some additional padding on each side. +* @param coordinates The coordinates that the viewport will show. +* @param count The number of coordinates. This number must not be greater than the number of elements in `coordinates`. +* @param insets The minimum padding (in screen points) that will be visible around the given coordinate bounds. +* @param animated Specify `YES` to animate the change by smoothly scrolling and zooming or `NO` to immediately display the given bounds. */ +- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; + /** The heading of the map (measured in degrees) relative to true north. * * The value `0` means that the top edge of the map view corresponds to true north. The value `90` means the top of the map is pointing due east. The value `180` means the top of the map points due south, and so on. */ diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index abd93fad6a..6fd5e96063 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -100,6 +100,7 @@ public: double getZoom() const; void setLatLngZoom(LatLng latLng, double zoom, Duration = Duration::zero()); void fitBounds(LatLngBounds bounds, EdgeInsets padding, Duration duration = Duration::zero()); + void fitBounds(AnnotationSegment segment, EdgeInsets padding, Duration duration = Duration::zero()); void resetZoom(); double getMinZoom() const; double getMaxZoom() const; diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 50debf336b..07822dd03e 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -1524,12 +1524,32 @@ mbgl::LatLngBounds MGLLatLngBoundsFromCoordinateBounds(MGLCoordinateBounds coord - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated { + CLLocationCoordinate2D coordinates[] = { + {bounds.ne.latitude, bounds.sw.longitude}, + bounds.sw, + {bounds.sw.latitude, bounds.ne.longitude}, + bounds.ne, + }; + [self setVisibleCoordinates:coordinates + count:sizeof(coordinates) / sizeof(coordinates[0]) + edgePadding:insets + animated:animated]; +} + +- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated +{ // NOTE: does not disrupt tracking mode CGFloat duration = animated ? MGLAnimationDuration : 0; [self willChangeValueForKey:@"visibleCoordinateBounds"]; mbgl::EdgeInsets mbglInsets = {insets.top, insets.left, insets.bottom, insets.right}; - _mbglMap->fitBounds(MGLLatLngBoundsFromCoordinateBounds(bounds), mbglInsets, secondsAsDuration(duration)); + mbgl::AnnotationSegment segment; + segment.reserve(count); + for (NSUInteger i = 0; i < count; i++) + { + segment.push_back({coordinates[i].latitude, coordinates[i].longitude}); + } + _mbglMap->fitBounds(segment, mbglInsets, secondsAsDuration(duration)); [self didChangeValueForKey:@"visibleCoordinateBounds"]; [self unrotateIfNeededAnimated:animated]; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 41bd57cd23..940ab82379 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -153,25 +153,35 @@ void Map::setLatLngZoom(LatLng latLng, double zoom, Duration duration) { } void Map::fitBounds(LatLngBounds bounds, EdgeInsets padding, Duration duration) { - // Calculate the bounds of the possibly rotated `bounds` parameter with respect to the viewport. - vec2<> nwPixel = pixelForLatLng({bounds.ne.latitude, bounds.sw.longitude}); - vec2<> swPixel = pixelForLatLng(bounds.sw); - vec2<> sePixel = pixelForLatLng({bounds.sw.latitude, bounds.ne.longitude}); - vec2<> nePixel = pixelForLatLng(bounds.ne); - vec2<> visualBounds = { - (std::max(std::max(nwPixel.x, swPixel.x), - std::max(sePixel.x, nePixel.x)) - - std::min(std::min(nwPixel.x, swPixel.x), - std::min(sePixel.x, nePixel.x))), - (std::max(std::max(nwPixel.y, swPixel.y), - std::max(sePixel.y, nePixel.y)) - - std::min(std::min(nwPixel.y, swPixel.y), - std::min(sePixel.y, nePixel.y))), + AnnotationSegment segment = { + {bounds.ne.latitude, bounds.sw.longitude}, + bounds.sw, + {bounds.sw.latitude, bounds.ne.longitude}, + bounds.ne, }; + fitBounds(segment, padding, duration); +} + +void Map::fitBounds(AnnotationSegment segment, EdgeInsets padding, Duration duration) { + if (segment.empty()) { + return; + } + + // Calculate the bounds of the possibly rotated shape with respect to the viewport. + vec2<> nePixel = {-INFINITY, -INFINITY}; + vec2<> swPixel = {INFINITY, INFINITY}; + for (LatLng latLng : segment) { + vec2<> pixel = pixelForLatLng(latLng); + swPixel.x = std::min(swPixel.x, pixel.x); + nePixel.x = std::max(nePixel.x, pixel.x); + swPixel.y = std::min(swPixel.y, pixel.y); + nePixel.y = std::max(nePixel.y, pixel.y); + } + vec2<> size = nePixel - swPixel; // Calculate the zoom level. - double scaleX = (getWidth() - padding.left - padding.right) / visualBounds.x; - double scaleY = (getHeight() - padding.top - padding.bottom) / visualBounds.y; + double scaleX = (getWidth() - padding.left - padding.right) / size.x; + double scaleY = (getHeight() - padding.top - padding.bottom) / size.y; double minScale = std::fmin(scaleX, scaleY); double zoom = std::log2(getScale() * minScale); zoom = std::fmax(std::fmin(zoom, getMaxZoom()), getMinZoom()); diff --git a/test/ios/MapViewTests.m b/test/ios/MapViewTests.m index de4095497a..82360db231 100644 --- a/test/ios/MapViewTests.m +++ b/test/ios/MapViewTests.m @@ -183,6 +183,27 @@ @"after panning 30° to the east, setting visible coordinate bounds back to %@ should not leave them at %@", MGLStringFromCoordinateBounds(initialBounds), MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds)); + + // Inscribed shapes with rotation + tester.mapView.direction = 45; + // https://en.wikipedia.org/wiki/Boundary_Markers_of_the_Original_District_of_Columbia + CLLocationCoordinate2D dcCoordinates[] = { + {38.790339, -77.040583}, + {38.893219, -77.172304}, + {38.995946, -77.040947}, + {38.892829, -76.909229}, + }; + MGLCoordinateBounds dcBounds = {{38.790339, -77.172304}, {38.995946, -76.909229}}; + [tester.mapView setVisibleCoordinateBounds:dcBounds + animated:NO]; + double zoomLevel = tester.mapView.zoomLevel; + [tester.mapView setVisibleCoordinates:dcCoordinates + count:sizeof(dcCoordinates) / sizeof(dcCoordinates[0]) + edgePadding:UIEdgeInsetsZero + animated:NO]; + XCTAssertGreaterThan(tester.mapView.zoomLevel, zoomLevel, + @"when the map is rotated, DC should fit at a zoom level higher than %f, but instead the zoom level is %f", + zoomLevel, tester.mapView.zoomLevel); } - (void)testPan { |