diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 65f988b5b..52136aeb4 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -98,6 +98,7 @@ public: // Position void moveBy(double dx, double dy, const Duration& = Duration::zero()); + void setLatLng(LatLng latLng, vec2 point, const Duration& = Duration::zero()); void setLatLng(LatLng latLng, const Duration& = Duration::zero()); LatLng getLatLng() const; void resetPosition(); diff --git a/platform/android/jni.cpp b/platform/android/jni.cpp index d0d3ea297..5808dff9c 100644 --- a/platform/android/jni.cpp +++ b/platform/android/jni.cpp @@ -637,7 +637,7 @@ void JNICALL nativeSetLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, j return; } - nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), std::chrono::milliseconds(duration)); + nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), mbgl::Duration(duration)); } jobject JNICALL nativeGetLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 0085e036b..99fbb5aa8 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -329,7 +329,10 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { double dx = x - view->lastX; double dy = y - view->lastY; if (dx || dy) { - view->map->moveBy(dx, dy); + double flippedY = view->height - y; + view->map->setLatLng( + view->map->latLngForPixel(mbgl::vec2(x - dx, flippedY + dy)), + mbgl::vec2(x, flippedY)); } } else if (view->rotating) { view->map->rotateBy(view->lastX, view->lastY, x, y); diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index ce1ecfd6d..45a537a55 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -879,7 +879,14 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) CGPoint delta = CGPointMake([pan translationInView:pan.view].x - self.centerPoint.x, [pan translationInView:pan.view].y - self.centerPoint.y); - _mbglMap->moveBy(delta.x, delta.y); + double flippedY = self.bounds.size.height - [pan locationInView:pan.view].y; + _mbglMap->setLatLng( + _mbglMap->latLngForPixel(mbgl::vec2( + [pan locationInView:pan.view].x - delta.x, + flippedY + delta.y)), + mbgl::vec2( + [pan locationInView:pan.view].x, + flippedY)); self.centerPoint = CGPointMake(self.centerPoint.x + delta.x, self.centerPoint.y + delta.y); @@ -1052,7 +1059,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) _mbglMap->setBearing(newDegrees, [rotate locationInView:rotate.view].x, - [rotate locationInView:rotate.view].y); + self.bounds.size.height - [rotate locationInView:rotate.view].y); [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; } diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 729726837..a1823dc8e 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -166,6 +166,11 @@ void Map::setLatLng(LatLng latLng, const Duration& duration) { update(Update::Repaint); } +void Map::setLatLng(LatLng latLng, vec2 point, const Duration& duration) { + transform->setLatLng(latLng, point, duration); + update(Update::Repaint); +} + LatLng Map::getLatLng() const { return transform->getLatLng(); } diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index bb5590914..87e86ea3e 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -113,6 +114,27 @@ void Transform::setLatLng(const LatLng latLng, const Duration& duration) { easeTo(options); } +void Transform::setLatLng(const LatLng latLng, vec2 point, const Duration& duration) { + if (std::isnan(latLng.latitude) || std::isnan(latLng.longitude)) { + return; + } + + auto coord = state.latLngToCoordinate(latLng); + auto coordAtPoint = state.pointToCoordinate(point); + auto coordCenter = state.pointToCoordinate({ state.width / 2.0f, state.height / 2.0f }); + + float columnDiff = coordAtPoint.column - coord.column; + float rowDiff = coordAtPoint.row - coord.row; + + auto newLatLng = state.coordinateToLatLng({ + coordCenter.column - columnDiff, + coordCenter.row - rowDiff, + coordCenter.zoom + }); + + setLatLng(newLatLng, duration); +} + void Transform::setLatLngZoom(const LatLng latLng, const double zoom, const Duration& duration) { CameraOptions options; options.center = latLng; @@ -173,25 +195,20 @@ void Transform::_setScale(double new_scale, double cx, double cy, const Duration new_scale = state.max_scale; } - // Zoom in on the center if we don't have click or gesture anchor coordinates. - if (cx < 0 || cy < 0) { - cx = static_cast(state.width) / 2.0; - cy = static_cast(state.height) / 2.0; + const double factor = new_scale / state.scale; + double dx = 0; + double dy = 0; + + if (cx > 0 || cy > 0) { + auto coord = state.pointToCoordinate({ cx, state.getHeight() - cy }).zoomTo(state.getZoom()); + auto centerCoord = state.pointToCoordinate({ state.width / 2.0f, state.height / 2.0f }).zoomTo(state.getZoom()); + auto coordDiff = centerCoord - coord; + dx = coordDiff.column * util::tileSize * (1.0 - factor); + dy = coordDiff.row * util::tileSize * (1.0 - factor); } - // Account for the x/y offset from the center (= where the user clicked or pinched) - const double factor = new_scale / state.scale; - const double dx = (cx - static_cast(state.width) / 2.0) * (1.0 - factor); - const double dy = (cy - static_cast(state.height) / 2.0) * (1.0 - factor); - - // Account for angle - const double angle_sin = std::sin(-state.angle); - const double angle_cos = std::cos(-state.angle); - const double ax = angle_cos * dx - angle_sin * dy; - const double ay = angle_sin * dx + angle_cos * dy; - - const double xn = state.x * factor + ax; - const double yn = state.y * factor + ay; + const double xn = state.x * factor - dx; + const double yn = state.y * factor - dy; _setScaleXY(new_scale, xn, yn, duration); } @@ -318,18 +335,17 @@ void Transform::setAngle(const double new_angle, const double cx, const double c return; } - double dx = 0, dy = 0; + LatLng rotationCenter; if (cx >= 0 && cy >= 0) { - dx = (static_cast(state.width) / 2.0) - cx; - dy = (static_cast(state.height) / 2.0) - cy; - _moveBy(dx, dy, Duration::zero()); + rotationCenter = state.pointToLatLng({ cx, cy }); + setLatLng(rotationCenter, Duration::zero()); } _setAngle(new_angle); if (cx >= 0 && cy >= 0) { - _moveBy(-dx, -dy, Duration::zero()); + setLatLng(rotationCenter, { cx, cy }, Duration::zero()); } } diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 545d8a3b2..a72e9f24e 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -29,6 +29,7 @@ public: // Position void moveBy(double dx, double dy, const Duration& = Duration::zero()); void setLatLng(LatLng latLng, const Duration& = Duration::zero()); + void setLatLng(LatLng latLng, vec2 point, const Duration& duration = Duration::zero()); void setLatLngZoom(LatLng latLng, double zoom, const Duration& = Duration::zero()); inline const LatLng getLatLng() const { return state.getLatLng(); }