diff options
author | zmiao <miao.zhao@mapbox.com> | 2019-11-05 15:57:44 +0200 |
---|---|---|
committer | zmiao <miao.zhao@mapbox.com> | 2019-11-06 11:36:42 +0200 |
commit | 8c7f2cbe33bf4cb913895afc6d5b70db33bdc87f (patch) | |
tree | c1e10d004b23a4a83653728d9e544fafae9a4d7f | |
parent | 1d49c23b8558e8c24a51e7ba84525ddd06f231be (diff) | |
download | qtlocation-mapboxgl-8c7f2cbe33bf4cb913895afc6d5b70db33bdc87f.tar.gz |
[core] Add batch conversion of latLng vs screenCoord
-rw-r--r-- | include/mbgl/map/map.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 19 | ||||
-rw-r--r-- | src/mbgl/map/transform.cpp | 17 | ||||
-rw-r--r-- | src/mbgl/map/transform.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/map/transform_state.cpp | 103 | ||||
-rw-r--r-- | src/mbgl/map/transform_state.hpp | 5 | ||||
-rw-r--r-- | test/api/annotations.test.cpp | 49 | ||||
-rw-r--r-- | test/api/query.test.cpp | 16 | ||||
-rw-r--r-- | test/map/transform.test.cpp | 37 |
9 files changed, 213 insertions, 38 deletions
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index ac0a398d25..4757bb398c 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -97,6 +97,8 @@ public: // Projection ScreenCoordinate pixelForLatLng(const LatLng&) const; LatLng latLngForPixel(const ScreenCoordinate&) const; + std::vector<ScreenCoordinate> pixelsForLatLngs(const std::vector<LatLng>&) const; + std::vector<LatLng> latLngsForPixels(const std::vector<ScreenCoordinate>&) const; // Annotations void addAnnotationImage(std::unique_ptr<style::Image>); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 649e0e321e..7714227e4d 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -183,8 +183,8 @@ CameraOptions cameraForLatLngs(const std::vector<LatLng>& latLngs, const Transfo ScreenCoordinate nePixel = {-INFINITY, -INFINITY}; ScreenCoordinate swPixel = {INFINITY, INFINITY}; double viewportHeight = size.height; - for (LatLng latLng : latLngs) { - ScreenCoordinate pixel = transform.latLngToScreenCoordinate(latLng); + const std::vector<ScreenCoordinate> pixels = transform.latLngsToScreenCoordinates(latLngs); + for (const auto& pixel : pixels) { swPixel.x = std::min(swPixel.x, pixel.x); nePixel.x = std::max(nePixel.x, pixel.x); swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y); @@ -364,6 +364,21 @@ LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const { return impl->transform.screenCoordinateToLatLng(pixel); } +std::vector<ScreenCoordinate> Map::pixelsForLatLngs(const std::vector<LatLng>& latLngs) const { + std::vector<LatLng> unwrappedLatLngs; + unwrappedLatLngs.reserve(latLngs.size()); + for (const auto& latLng : latLngs) { + LatLng unwrappedLatLng = latLng.wrapped(); + unwrappedLatLng.unwrapForShortestPath(impl->transform.getLatLng()); + unwrappedLatLngs.emplace_back(unwrappedLatLng); + } + return impl->transform.latLngsToScreenCoordinates(unwrappedLatLngs); +} + +std::vector<LatLng> Map::latLngsForPixels(const std::vector<ScreenCoordinate>& screenCoords) const { + return impl->transform.screenCoordinatesToLatLngs(screenCoords); +} + #pragma mark - Annotations void Map::addAnnotationImage(std::unique_ptr<style::Image> image) { diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index e88bb5465c..66edf0bba4 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -590,6 +590,23 @@ LatLng Transform::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng return state.screenCoordinateToLatLng(flippedPoint, wrapMode); } +std::vector<ScreenCoordinate> Transform::latLngsToScreenCoordinates(const std::vector<LatLng>& latLngs) const { + std::vector<ScreenCoordinate> points = state.latLngsToScreenCoordinates(latLngs); + for (auto& point : points) { + point.y = state.size.height - point.y; + } + return points; +} + +std::vector<LatLng> Transform::screenCoordinatesToLatLngs(const std::vector<ScreenCoordinate>& points, + LatLng::WrapMode wrapMode) const { + auto flippedPoints = points; + for (auto& flippedPoint : flippedPoints) { + flippedPoint.y = state.size.height - flippedPoint.y; + } + return state.screenCoordinatesToLatLngs(flippedPoints, wrapMode); +} + double Transform::getMaxPitchForEdgeInsets(const EdgeInsets &insets) const { double centerOffsetY = 0.5 * (insets.top() - insets.bottom()); // See TransformState::getCenterOffset. diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 30ce8a37a4..8b2928ee2e 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -109,6 +109,9 @@ public: // Conversion and projection ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const; LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Wrapped) const; + std::vector<ScreenCoordinate> latLngsToScreenCoordinates(const std::vector<LatLng>&) const; + std::vector<LatLng> screenCoordinatesToLatLngs(const std::vector<ScreenCoordinate>&, + LatLng::WrapMode = LatLng::Wrapped) const; private: MapObserver& observer; diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 61007422cb..04f227cd77 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -276,6 +276,40 @@ double TransformState::zoomScale(double zoom) const { double TransformState::scaleZoom(double s) const { return util::log2(s); } +namespace { +mbgl::TileCoordinate getTileCoordinate(const ScreenCoordinate& point, + const mbgl::Size& size, + const mbgl::mat4& inverted, + const uint8_t atZoom, + const double scale) { + float targetZ = 0; + double flippedY = size.height - point.y; + + // since we don't know the correct projected z value for the point, + // unproject two points to get a line and then find the point on that + // line with z=0 + + vec4 coord0; + vec4 coord1; + vec4 point0 = {{point.x, flippedY, 0, 1}}; + vec4 point1 = {{point.x, flippedY, 1, 1}}; + matrix::transformMat4(coord0, point0, inverted); + matrix::transformMat4(coord1, point1, inverted); + + double w0 = coord0[3]; + double w1 = coord1[3]; + + Point<double> p0 = Point<double>(coord0[0], coord0[1]) / w0; + Point<double> p1 = Point<double>(coord1[0], coord1[1]) / w1; + + double z0 = coord0[2] / w0; + double z1 = coord1[2] / w1; + double t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0); + + Point<double> p = util::interpolate(p0, p1, t) / scale * static_cast<double>(1 << atZoom); + return {{p.x, p.y}, static_cast<double>(atZoom)}; +} +} // namespace ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng) const { if (size.isEmpty()) { @@ -290,44 +324,54 @@ ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng) return { p[0] / p[3], size.height - p[1] / p[3] }; } +std::vector<ScreenCoordinate> TransformState::latLngsToScreenCoordinates(const std::vector<LatLng>& latLngs) const { + if (size.isEmpty()) { + return {}; + } + std::vector<ScreenCoordinate> points; + points.reserve(latLngs.size()); + mat4 mat = coordinatePointMatrix(); + vec4 p; + for (const auto& latLng : latLngs) { + Point<double> pt = Projection::project(latLng, scale) / util::tileSize; + vec4 c = {{pt.x, pt.y, 0, 1}}; + matrix::transformMat4(p, c, mat); + points.emplace_back(p[0] / p[3], size.height - p[1] / p[3]); + } + return points; +} + TileCoordinate TransformState::screenCoordinateToTileCoordinate(const ScreenCoordinate& point, uint8_t atZoom) const { if (size.isEmpty()) { return { {}, 0 }; } - - float targetZ = 0; mat4 mat = coordinatePointMatrix(); mat4 inverted; bool err = matrix::invert(inverted, mat); if (err) throw std::runtime_error("failed to invert coordinatePointMatrix"); + return getTileCoordinate(point, size, inverted, atZoom, scale); +} - double flippedY = size.height - point.y; - - // since we don't know the correct projected z value for the point, - // unproject two points to get a line and then find the point on that - // line with z=0 - - vec4 coord0; - vec4 coord1; - vec4 point0 = {{ point.x, flippedY, 0, 1 }}; - vec4 point1 = {{ point.x, flippedY, 1, 1 }}; - matrix::transformMat4(coord0, point0, inverted); - matrix::transformMat4(coord1, point1, inverted); - - double w0 = coord0[3]; - double w1 = coord1[3]; +std::vector<TileCoordinate> TransformState::screenCoordinatesToTileCoordinates( + const std::vector<ScreenCoordinate>& points, uint8_t atZoom) const { + if (size.isEmpty()) { + return {{{}, 0}}; + } + mat4 mat = coordinatePointMatrix(); - Point<double> p0 = Point<double>(coord0[0], coord0[1]) / w0; - Point<double> p1 = Point<double>(coord1[0], coord1[1]) / w1; + mat4 inverted; + bool err = matrix::invert(inverted, mat); - double z0 = coord0[2] / w0; - double z1 = coord1[2] / w1; - double t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0); + if (err) throw std::runtime_error("failed to invert coordinatePointMatrix"); - Point<double> p = util::interpolate(p0, p1, t) / scale * static_cast<double>(1 << atZoom); - return { { p.x, p.y }, static_cast<double>(atZoom) }; + std::vector<TileCoordinate> coords; + coords.reserve(points.size()); + for (const auto& point : points) { + coords.emplace_back(getTileCoordinate(point, size, inverted, atZoom, scale)); + } + return coords; } LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng::WrapMode wrapMode) const { @@ -335,6 +379,17 @@ LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, L return Projection::unproject(coord.p, 1 / util::tileSize, wrapMode); } +std::vector<LatLng> TransformState::screenCoordinatesToLatLngs(const std::vector<ScreenCoordinate>& points, + LatLng::WrapMode wrapMode) const { + const auto coords = screenCoordinatesToTileCoordinates(points, 0); + std::vector<LatLng> latLngs; + latLngs.reserve(coords.size()); + for (const auto& coord : coords) { + latLngs.emplace_back(Projection::unproject(coord.p, 1 / util::tileSize, wrapMode)); + } + return latLngs; +} + mat4 TransformState::coordinatePointMatrix() const { mat4 proj; getProjMatrix(proj); diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 10a92187d5..deab91dc78 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -82,8 +82,13 @@ public: // Conversion ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const; LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Unwrapped) const; + std::vector<ScreenCoordinate> latLngsToScreenCoordinates(const std::vector<LatLng>&) const; + std::vector<LatLng> screenCoordinatesToLatLngs(const std::vector<ScreenCoordinate>&, + LatLng::WrapMode = LatLng::Unwrapped) const; // Implements mapbox-gl-js pointCoordinate() : MercatorCoordinate. TileCoordinate screenCoordinateToTileCoordinate(const ScreenCoordinate&, uint8_t atZoom) const; + std::vector<TileCoordinate> screenCoordinatesToTileCoordinates(const std::vector<ScreenCoordinate>&, + uint8_t atZoom) const; double zoomScale(double zoom) const; double scaleZoom(double scale) const; diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index 4886d90a93..bedf7a208d 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -354,12 +354,24 @@ TEST(Annotations, QueryRenderedFeatures) { test.frontend.render(test.map); - auto features = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 })); + // Batch conversion of latLngs to pixels + auto points = test.map.pixelsForLatLngs({{0, 0}, {50, 0}}); + ASSERT_EQ(2, points.size()); + // Single conversion of latLng to pixel + auto point0 = test.map.pixelForLatLng({0, 0}); + ASSERT_NEAR(points[0].x, point0.x, 1e-8); + ASSERT_NEAR(points[0].y, point0.y, 1e-8); + + auto point1 = test.map.pixelForLatLng({50, 0}); + ASSERT_NEAR(points[1].x, point1.x, 1e-8); + ASSERT_NEAR(points[1].y, point1.y, 1e-8); + + auto features = test.frontend.getRenderer()->queryRenderedFeatures(point0); EXPECT_EQ(features.size(), 1u); EXPECT_EQ(features[0].id.is<NullValue>(), false); EXPECT_EQ(features[0].id, uint64_t(0)); - auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 50, 0 })); + auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(point1); EXPECT_EQ(features2.size(), 1u); EXPECT_EQ(features[0].id.is<NullValue>(), false); EXPECT_EQ(features2[0].id, uint64_t(1)); @@ -452,19 +464,37 @@ TEST(Annotations, ViewFrustumCulling) { const LatLng center = { 5.0, 5.0 }; test.map.jumpTo(CameraOptions().withCenter(center).withZoom(3.0)); + // Batch conversion of pixels to latLngs + const std::vector<LatLng> batchLatLngs = + test.map.latLngsForPixels({ScreenCoordinate(0, 0), + ScreenCoordinate(viewSize.width, viewSize.height), + ScreenCoordinate(viewSize.width, 0), + ScreenCoordinate(0, viewSize.height)}); + ASSERT_EQ(4, batchLatLngs.size()); + + // Single conversion of pixel to latLng LatLng tl = test.map.latLngForPixel(ScreenCoordinate(0, 0)); + ASSERT_NEAR(batchLatLngs[0].latitude(), tl.latitude(), 1e-8); + ASSERT_NEAR(batchLatLngs[0].longitude(), tl.longitude(), 1e-8); + LatLng br = test.map.latLngForPixel(ScreenCoordinate(viewSize.width, viewSize.height)); + ASSERT_NEAR(batchLatLngs[1].latitude(), br.latitude(), 1e-8); + ASSERT_NEAR(batchLatLngs[1].longitude(), br.longitude(), 1e-8); + + LatLng bl = test.map.latLngForPixel(ScreenCoordinate(viewSize.width, 0)); + ASSERT_NEAR(batchLatLngs[2].latitude(), bl.latitude(), 1e-8); + ASSERT_NEAR(batchLatLngs[2].longitude(), bl.longitude(), 1e-8); - std::vector<LatLng> latLngs = { - tl, - test.map.latLngForPixel(ScreenCoordinate(viewSize.width, 0)), - test.map.latLngForPixel(ScreenCoordinate(0, viewSize.height)), - br, - center}; + LatLng tr = test.map.latLngForPixel(ScreenCoordinate(0, viewSize.height)); + ASSERT_NEAR(batchLatLngs[3].latitude(), tr.latitude(), 1e-8); + ASSERT_NEAR(batchLatLngs[3].longitude(), tr.longitude(), 1e-8); + + std::vector<LatLng> latLngs = {tl, bl, tr, br, center}; std::vector<mbgl::AnnotationID> ids; for (auto latLng : latLngs) { - ids.push_back(test.map.addAnnotation(SymbolAnnotation { { latLng.longitude(), latLng.latitude() }, "default_marker" })); + ids.push_back( + test.map.addAnnotation(SymbolAnnotation{{latLng.longitude(), latLng.latitude()}, "default_marker"})); } std::vector<std::pair<CameraOptions, std::vector<uint64_t>>> expectedVisibleForCamera = { @@ -550,4 +580,3 @@ TEST(Annotations, ChangeMaxZoom) { test.map.jumpTo(CameraOptions().withZoom(*test.map.getBounds().maxZoom)); test.checkRendering("line_annotation_max_zoom"); } - diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index 83aeec81ec..54ae89767a 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -75,10 +75,22 @@ std::vector<Feature> getTopClusterFeature(QueryTest& test) { TEST(Query, QueryRenderedFeatures) { QueryTest test; - auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 })); + // Batch conversion of latLngs to pixels + auto points = test.map.pixelsForLatLngs({{0, 0}, {9, 9}}); + ASSERT_EQ(2, points.size()); + // Single conversion of latLng to pixel + auto point0 = test.map.pixelForLatLng({0, 0}); + ASSERT_NEAR(points[0].x, point0.x, 1e-8); + ASSERT_NEAR(points[0].y, point0.y, 1e-8); + + auto point1 = test.map.pixelForLatLng({9, 9}); + ASSERT_NEAR(points[1].x, point1.x, 1e-8); + ASSERT_NEAR(points[1].y, point1.y, 1e-8); + + auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(point0); EXPECT_EQ(features1.size(), 4u); - auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 9, 9 })); + auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(point1); EXPECT_EQ(features2.size(), 0u); } diff --git a/test/map/transform.test.cpp b/test/map/transform.test.cpp index 84fdb06b21..ec2abe9a84 100644 --- a/test/map/transform.test.cpp +++ b/test/map/transform.test.cpp @@ -123,6 +123,14 @@ TEST(Transform, PerspectiveProjection) { ASSERT_NEAR(-76.75823239205641, loc.longitude(), 1e-6); ASSERT_NEAR(37.692872969426375, loc.latitude(), 1e-6); + // Using batch conversion + const auto locs = transform.getState().screenCoordinatesToLatLngs({{0, 1000}, {1000, 0}}); + ASSERT_EQ(2, locs.size()); + ASSERT_NEAR(-77.59198961199148, locs.front().longitude(), 1e-6); + ASSERT_NEAR(38.74661326302018, locs.front().latitude(), 1e-6); + ASSERT_NEAR(-76.75823239205641, locs.back().longitude(), 1e-6); + ASSERT_NEAR(37.692872969426375, locs.back().latitude(), 1e-6); + ScreenCoordinate point = transform.getState().latLngToScreenCoordinate({38.74661326302018, -77.59198961199148}); ASSERT_NEAR(point.x, 0.0, 1e-5); ASSERT_NEAR(point.y, 1000.0, 1e-4); @@ -130,6 +138,15 @@ TEST(Transform, PerspectiveProjection) { point = transform.getState().latLngToScreenCoordinate({37.692872969426375, -76.75823239205641}); ASSERT_NEAR(point.x, 1000.0, 1e-5); ASSERT_NEAR(point.y, 0.0, 1e-4); + + // Using batch conversion + const auto points = transform.getState().latLngsToScreenCoordinates( + {{38.74661326302018, -77.59198961199148}, {37.692872969426375, -76.75823239205641}}); + ASSERT_EQ(2, points.size()); + ASSERT_NEAR(points.front().x, 0.0, 1e-5); + ASSERT_NEAR(points.front().y, 1000.0, 1e-4); + ASSERT_NEAR(points.back().x, 1000.0, 1e-5); + ASSERT_NEAR(points.back().y, 0.0, 1e-4); } TEST(Transform, UnwrappedLatLng) { @@ -160,6 +177,26 @@ TEST(Transform, UnwrappedLatLng) { ASSERT_NEAR(wrappedLeftwards.longitude(), -437.0, 1e-8); wrappedLeftwards.wrap(); ASSERT_NEAR(wrappedLeftwards.longitude(), -77.0, 1e-8); + + // Using batch conversion + std::vector<ScreenCoordinate> coords{{500, 500}}; + auto tempCoords = state.latLngsToScreenCoordinates({{38, 283}, {38, -437}}); + coords.insert(coords.end(), tempCoords.begin(), tempCoords.end()); + auto latLngs = state.screenCoordinatesToLatLngs(coords); + ASSERT_EQ(3, latLngs.size()); + + ASSERT_NEAR(38.0, latLngs[0].latitude(), 1e-8); + ASSERT_NEAR(-77.0, latLngs[0].longitude(), 1e-8); + + ASSERT_NEAR(38.0, latLngs[1].latitude(), 1e-8); + ASSERT_NEAR(283.0, latLngs[1].longitude(), 1e-8); + latLngs[1].wrap(); + ASSERT_NEAR(-77.0, latLngs[1].longitude(), 1e-8); + + ASSERT_DOUBLE_EQ(latLngs[1].latitude(), latLngs[2].latitude()); + ASSERT_NEAR(-437.0, latLngs[2].longitude(), 1e-8); + latLngs[2].wrap(); + ASSERT_NEAR(-77.0, latLngs[2].longitude(), 1e-8); } TEST(Transform, ConstrainHeightOnly) { |