summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/util/geo.hpp8
-rw-r--r--include/mbgl/util/projection.hpp8
-rw-r--r--test/test.gypi1
-rw-r--r--test/util/projection.cpp68
4 files changed, 80 insertions, 5 deletions
diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp
index 7e15d5c2f1..5db307e482 100644
--- a/include/mbgl/util/geo.hpp
+++ b/include/mbgl/util/geo.hpp
@@ -67,8 +67,8 @@ inline bool operator!=(const LatLng& a, const LatLng& b) {
class ProjectedMeters {
public:
- double northing = 0;
- double easting = 0;
+ double northing;
+ double easting;
ProjectedMeters(double n = 0, double e = 0)
: northing(n), easting(e) {}
@@ -78,6 +78,10 @@ public:
}
};
+inline bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) {
+ return a.northing == b.northing && a.easting == b.easting;
+}
+
class LatLngBounds {
public:
// Return a bounds covering the entire (unwrapped) world.
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index 8e1c994657..76c60f5cd8 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -12,19 +12,20 @@ class Projection {
public:
static inline double getMetersPerPixelAtLatitude(double lat, double zoom) {
- const double mapPixelWidthAtZoom = std::pow(2.0, zoom) * util::tileSize;
+ const double constrainedZoom = util::clamp(zoom, util::MIN_ZOOM, util::MAX_ZOOM);
+ const double mapPixelWidthAtZoom = std::pow(2.0, constrainedZoom) * util::tileSize;
const double constrainedLatitude = util::clamp(lat, -util::LATITUDE_MAX, util::LATITUDE_MAX);
-
return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / mapPixelWidthAtZoom;
}
static inline ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) {
const double constrainedLatitude = util::clamp(latLng.latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX);
+ const double constrainedLongitude = util::clamp(latLng.longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX);
const double m = 1 - 1e-15;
const double f = util::clamp(std::sin(util::DEG2RAD * constrainedLatitude), -m, m);
- const double easting = util::EARTH_RADIUS_M * latLng.longitude * util::DEG2RAD;
+ const double easting = util::EARTH_RADIUS_M * constrainedLongitude * util::DEG2RAD;
const double northing = 0.5 * util::EARTH_RADIUS_M * std::log((1 + f) / (1 - f));
return ProjectedMeters(northing, easting);
@@ -35,6 +36,7 @@ public:
double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M;
latitude = util::clamp(latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX);
+ longitude = util::clamp(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX);
return LatLng(latitude, longitude);
}
diff --git a/test/test.gypi b/test/test.gypi
index b7e7b5c32b..d93ecd1f35 100644
--- a/test/test.gypi
+++ b/test/test.gypi
@@ -32,6 +32,7 @@
'util/timer.cpp',
'util/token.cpp',
'util/work_queue.cpp',
+ 'util/projection.cpp',
'algorithm/covered_by_children.cpp',
'algorithm/generate_clip_ids.cpp',
diff --git a/test/util/projection.cpp b/test/util/projection.cpp
new file mode 100644
index 0000000000..5efba380b3
--- /dev/null
+++ b/test/util/projection.cpp
@@ -0,0 +1,68 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/projection.hpp>
+
+#include <limits>
+
+using namespace mbgl;
+
+TEST(Projection, MetersPerPixelAtLatitude) {
+ double zoom = 0;
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), 78271.516964020484);
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom), 6752.2284729446501);
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(util::LATITUDE_MAX, zoom),
+ Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom));
+
+ zoom = 20;
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), 0.074645535434742435);
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom), 0.0064394268731543065);
+
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(std::numeric_limits<double>::lowest(), zoom),
+ Projection::getMetersPerPixelAtLatitude(std::numeric_limits<double>::max(), zoom));
+
+ zoom = std::numeric_limits<double>::min();
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom),
+ Projection::getMetersPerPixelAtLatitude(0, util::MIN_ZOOM));
+
+ zoom = std::numeric_limits<double>::lowest();
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom),
+ Projection::getMetersPerPixelAtLatitude(0, util::MIN_ZOOM));
+
+ zoom = std::numeric_limits<double>::max();
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom),
+ Projection::getMetersPerPixelAtLatitude(0, util::MAX_ZOOM));
+}
+
+TEST(Projection, ProjectedMeters) {
+ const auto southWest = LatLng { -util::LATITUDE_MAX, -util::LONGITUDE_MAX };
+ const auto northEast = LatLng { util::LATITUDE_MAX, util::LONGITUDE_MAX };
+
+ auto latLng = LatLng {};
+ auto projectedMeters = Projection::projectedMetersForLatLng(latLng);
+ EXPECT_EQ(projectedMeters.northing, projectedMeters.easting);
+ EXPECT_EQ(latLng, Projection::latLngForProjectedMeters(projectedMeters));
+
+ latLng = LatLng { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
+ projectedMeters = Projection::projectedMetersForLatLng(latLng);
+ EXPECT_EQ(projectedMeters, Projection::projectedMetersForLatLng(southWest));
+ EXPECT_DOUBLE_EQ(projectedMeters.northing, -20037508.342789274);
+ EXPECT_DOUBLE_EQ(projectedMeters.easting, -20037508.342789244);
+
+ latLng = LatLng { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
+ projectedMeters = Projection::projectedMetersForLatLng(latLng);
+ EXPECT_EQ(projectedMeters, Projection::projectedMetersForLatLng(northEast));
+ EXPECT_DOUBLE_EQ(projectedMeters.northing, -Projection::projectedMetersForLatLng(southWest).northing);
+ EXPECT_DOUBLE_EQ(projectedMeters.easting, -Projection::projectedMetersForLatLng(southWest).easting);
+
+ projectedMeters = ProjectedMeters { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
+ latLng = Projection::latLngForProjectedMeters(projectedMeters);
+ EXPECT_EQ(latLng.latitude, -util::LATITUDE_MAX);
+ EXPECT_EQ(latLng.longitude, -util::LONGITUDE_MAX);
+
+ projectedMeters = ProjectedMeters { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
+ latLng = Projection::latLngForProjectedMeters(projectedMeters);
+ EXPECT_EQ(latLng.latitude, util::LATITUDE_MAX);
+ EXPECT_EQ(latLng.longitude, util::LONGITUDE_MAX);
+}