summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2015-06-26 14:27:44 -0700
committerMinh Nguyễn <mxn@1ec5.org>2015-06-26 21:46:15 -0700
commitac2ad993a594e309362787e0697ff1727b799e8e (patch)
tree8347096c4f0c0de64bfadb514387b44d27427f66
parent7b22caddd0bb58677cc29a29c005bec621368d4b (diff)
downloadqtlocation-mapboxgl-ac2ad993a594e309362787e0697ff1727b799e8e.tar.gz
Fit to coordinates
Whoever determined the boundaries of the District of Columbia did not have fit-to-bounds implementations in mind. With this change, shapes that are not unrotated rectangles fit much, much better.
-rw-r--r--include/mbgl/ios/MGLMapView.h7
-rw-r--r--include/mbgl/map/map.hpp1
-rw-r--r--platform/ios/MGLMapView.mm22
-rw-r--r--src/mbgl/map/map.cpp42
-rw-r--r--test/ios/MapViewTests.m21
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 {