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

Fit to coordinates #1795

Merged
merged 1 commit into from
Jun 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/mbgl/ios/MGLMapView.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
1 change: 1 addition & 0 deletions include/mbgl/map/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class Map : private util::noncopyable {
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;
Expand Down
22 changes: 21 additions & 1 deletion platform/ios/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1523,13 +1523,33 @@ - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)an
}

- (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];
Expand Down
42 changes: 26 additions & 16 deletions src/mbgl/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
21 changes: 21 additions & 0 deletions test/ios/MapViewTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,27 @@ - (void)testFitToBounds {
@"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 {
Expand Down