summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2019-11-05 15:57:44 +0200
committerzmiao <miao.zhao@mapbox.com>2019-11-06 11:36:42 +0200
commit8c7f2cbe33bf4cb913895afc6d5b70db33bdc87f (patch)
treec1e10d004b23a4a83653728d9e544fafae9a4d7f
parent1d49c23b8558e8c24a51e7ba84525ddd06f231be (diff)
downloadqtlocation-mapboxgl-8c7f2cbe33bf4cb913895afc6d5b70db33bdc87f.tar.gz
[core] Add batch conversion of latLng vs screenCoord
-rw-r--r--include/mbgl/map/map.hpp2
-rw-r--r--src/mbgl/map/map.cpp19
-rw-r--r--src/mbgl/map/transform.cpp17
-rw-r--r--src/mbgl/map/transform.hpp3
-rw-r--r--src/mbgl/map/transform_state.cpp103
-rw-r--r--src/mbgl/map/transform_state.hpp5
-rw-r--r--test/api/annotations.test.cpp49
-rw-r--r--test/api/query.test.cpp16
-rw-r--r--test/map/transform.test.cpp37
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) {