[core] Tighten geo.hpp ctors
This commit is contained in:
committed by
John Firebaugh
parent
8877452ec3
commit
2f4d162deb
@@ -70,28 +70,43 @@ public:
|
||||
LatLng(const CanonicalTileID& id);
|
||||
LatLng(const UnwrappedTileID& id);
|
||||
|
||||
friend constexpr bool operator==(const LatLng& a, const LatLng& b) {
|
||||
friend bool operator==(const LatLng& a, const LatLng& b) {
|
||||
return a.lat == b.lat && a.lon == b.lon;
|
||||
}
|
||||
|
||||
friend constexpr bool operator!=(const LatLng& a, const LatLng& b) {
|
||||
friend bool operator!=(const LatLng& a, const LatLng& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
class ProjectedMeters {
|
||||
private:
|
||||
double _northing; // Distance measured northwards.
|
||||
double _easting; // Distance measured eastwards.
|
||||
|
||||
public:
|
||||
double northing;
|
||||
double easting;
|
||||
ProjectedMeters(double n_ = 0, double e_ = 0)
|
||||
: _northing(n_), _easting(e_) {
|
||||
if (std::isnan(_northing)) {
|
||||
throw std::domain_error("northing must not be NaN");
|
||||
}
|
||||
if (std::isnan(_easting)) {
|
||||
throw std::domain_error("easting must not be NaN");
|
||||
}
|
||||
}
|
||||
|
||||
ProjectedMeters(double n = 0, double e = 0)
|
||||
: northing(n), easting(e) {}
|
||||
double northing() const { return _northing; }
|
||||
double easting() const { return _easting; }
|
||||
|
||||
friend bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) {
|
||||
return a._northing == b._northing && a._easting == b._easting;
|
||||
}
|
||||
|
||||
friend bool operator!=(const ProjectedMeters& a, const ProjectedMeters& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr 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.
|
||||
@@ -188,18 +203,15 @@ private:
|
||||
LatLngBounds(LatLng sw_, LatLng ne_)
|
||||
: sw(std::move(sw_)), ne(std::move(ne_)) {}
|
||||
|
||||
friend constexpr bool operator==(const LatLngBounds&, const LatLngBounds&);
|
||||
friend constexpr bool operator!=(const LatLngBounds&, const LatLngBounds&);
|
||||
friend bool operator==(const LatLngBounds& a, const LatLngBounds& b) {
|
||||
return a.sw == b.sw && a.ne == b.ne;
|
||||
}
|
||||
|
||||
friend bool operator!=(const LatLngBounds& a, const LatLngBounds& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool operator==(const LatLngBounds& a, const LatLngBounds& b) {
|
||||
return a.sw == b.sw && a.ne == b.ne;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const LatLngBounds& a, const LatLngBounds& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
// Determines the orientation of the map.
|
||||
enum class NorthOrientation : uint8_t {
|
||||
Upwards, // Default
|
||||
@@ -210,43 +222,60 @@ enum class NorthOrientation : uint8_t {
|
||||
|
||||
/// The distance on each side between a rectangle and a rectangle within.
|
||||
class EdgeInsets {
|
||||
private:
|
||||
double _top; // Number of pixels inset from the top edge.
|
||||
double _left; // Number of pixels inset from the left edge.
|
||||
double _bottom; // Number of pixels inset from the bottom edge.
|
||||
double _right; // Number of pixels inset from the right edge.
|
||||
|
||||
public:
|
||||
double top = 0; // Number of pixels inset from the top edge.
|
||||
double left = 0; // Number of pixels inset from the left edge.
|
||||
double bottom = 0; // Number of pixels inset from the bottom edge.
|
||||
double right = 0; // Number of pixels inset from the right edge.
|
||||
EdgeInsets(double t_ = 0, double l_ = 0, double b_ = 0, double r_ = 0)
|
||||
: _top(t_), _left(l_), _bottom(b_), _right(r_) {
|
||||
if (std::isnan(_top)) {
|
||||
throw std::domain_error("top must not be NaN");
|
||||
}
|
||||
if (std::isnan(_left)) {
|
||||
throw std::domain_error("left must not be NaN");
|
||||
}
|
||||
if (std::isnan(_bottom)) {
|
||||
throw std::domain_error("bottom must not be NaN");
|
||||
}
|
||||
if (std::isnan(_right)) {
|
||||
throw std::domain_error("right must not be NaN");
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsets() {}
|
||||
|
||||
EdgeInsets(const double t, const double l, const double b, const double r)
|
||||
: top(t), left(l), bottom(b), right(r) {}
|
||||
double top() const { return _top; }
|
||||
double left() const { return _left; }
|
||||
double bottom() const { return _bottom; }
|
||||
double right() const { return _right; }
|
||||
|
||||
bool isFlush() const {
|
||||
return top == 0 && left == 0 && bottom == 0 && right == 0;
|
||||
return _top == 0 && _left == 0 && _bottom == 0 && _right == 0;
|
||||
}
|
||||
|
||||
void operator+=(const EdgeInsets& o) {
|
||||
top += o.top;
|
||||
left += o.left;
|
||||
bottom += o.bottom;
|
||||
right += o.right;
|
||||
_top += o._top;
|
||||
_left += o._left;
|
||||
_bottom += o._bottom;
|
||||
_right += o._right;
|
||||
}
|
||||
|
||||
EdgeInsets operator+(const EdgeInsets& o) const {
|
||||
return {
|
||||
top + o.top, left + o.left, bottom + o.bottom, right + o.right,
|
||||
_top + o._top, _left + o._left, _bottom + o._bottom, _right + o._right,
|
||||
};
|
||||
}
|
||||
|
||||
ScreenCoordinate getCenter(uint16_t width, uint16_t height) const;
|
||||
|
||||
friend bool operator==(const EdgeInsets& a, const EdgeInsets& b) {
|
||||
return a._top == b._top && a._left == b._left && a._bottom == b._bottom && a._right == b._right;
|
||||
}
|
||||
|
||||
friend bool operator!=(const EdgeInsets& a, const EdgeInsets& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool operator==(const EdgeInsets& a, const EdgeInsets& b) {
|
||||
return a.top == b.top && a.left == b.left && a.bottom == b.bottom && a.right == b.right;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const EdgeInsets& a, const EdgeInsets& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -38,8 +38,8 @@ public:
|
||||
}
|
||||
|
||||
static LatLng latLngForProjectedMeters(const ProjectedMeters& projectedMeters) {
|
||||
double latitude = (2 * std::atan(std::exp(projectedMeters.northing / util::EARTH_RADIUS_M)) - (M_PI / 2.0)) * util::RAD2DEG;
|
||||
double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M;
|
||||
double latitude = (2 * std::atan(std::exp(projectedMeters.northing() / util::EARTH_RADIUS_M)) - (M_PI / 2.0)) * util::RAD2DEG;
|
||||
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);
|
||||
|
||||
@@ -643,7 +643,7 @@ jni::jdouble NativeMapView::getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble la
|
||||
|
||||
jni::Object<ProjectedMeters> NativeMapView::projectedMetersForLatLng(JNIEnv& env, jni::jdouble latitude, jni::jdouble longitude) {
|
||||
mbgl::ProjectedMeters projectedMeters = map->projectedMetersForLatLng(mbgl::LatLng(latitude, longitude));
|
||||
return ProjectedMeters::New(env, projectedMeters.northing, projectedMeters.easting);
|
||||
return ProjectedMeters::New(env, projectedMeters.northing(), projectedMeters.easting());
|
||||
}
|
||||
|
||||
jni::Object<PointF> NativeMapView::pixelForLatLng(JNIEnv& env, jdouble latitude, jdouble longitude) {
|
||||
|
||||
@@ -32,11 +32,11 @@ BOOL MGLEqualFloatWithAccuracy(CGFloat left, CGFloat right, CGFloat accuracy)
|
||||
|
||||
mbgl::ProjectedMeters centerMeters = mbgl::Projection::projectedMetersForLatLng(centerLatLng);
|
||||
mbgl::ProjectedMeters eyeMeters = mbgl::Projection::projectedMetersForLatLng(eyeLatLng);
|
||||
heading = std::atan((centerMeters.northing - eyeMeters.northing) /
|
||||
(centerMeters.easting - eyeMeters.easting));
|
||||
heading = std::atan((centerMeters.northing() - eyeMeters.northing()) /
|
||||
(centerMeters.easting() - eyeMeters.easting()));
|
||||
|
||||
double groundDistance = std::hypot(centerMeters.northing - eyeMeters.northing,
|
||||
centerMeters.easting - eyeMeters.easting);
|
||||
double groundDistance = std::hypot(centerMeters.northing() - eyeMeters.northing(),
|
||||
centerMeters.easting() - eyeMeters.easting());
|
||||
pitch = std::atan(eyeAltitude / groundDistance);
|
||||
}
|
||||
|
||||
|
||||
@@ -4559,8 +4559,8 @@ public:
|
||||
mbgl::LatLng targetLatLng = MGLLatLngFromLocationCoordinate2D(self.targetCoordinate);
|
||||
mbgl::ProjectedMeters userMeters = mbgl::Projection::projectedMetersForLatLng(userLatLng);
|
||||
mbgl::ProjectedMeters targetMeters = mbgl::Projection::projectedMetersForLatLng(targetLatLng);
|
||||
double angle = atan2(targetMeters.easting - userMeters.easting,
|
||||
targetMeters.northing - userMeters.northing);
|
||||
double angle = atan2(targetMeters.easting() - userMeters.easting(),
|
||||
targetMeters.northing() - userMeters.northing());
|
||||
direction = mbgl::util::wrap(MGLDegreesFromRadians(angle), 0., 360.);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1130,7 +1130,7 @@ double QMapboxGL::metersPerPixelAtLatitude(double latitude, double zoom) const
|
||||
QMapbox::ProjectedMeters QMapboxGL::projectedMetersForCoordinate(const QMapbox::Coordinate &coordinate_) const
|
||||
{
|
||||
auto projectedMeters = d_ptr->mapObj->projectedMetersForLatLng(mbgl::LatLng { coordinate_.first, coordinate_.second });
|
||||
return QMapbox::ProjectedMeters(projectedMeters.northing, projectedMeters.easting);
|
||||
return QMapbox::ProjectedMeters(projectedMeters.northing(), projectedMeters.easting());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1229,10 +1229,10 @@ void QMapboxGL::setMargins(const QMargins &margins_)
|
||||
QMargins QMapboxGL::margins() const
|
||||
{
|
||||
return QMargins(
|
||||
d_ptr->margins.left,
|
||||
d_ptr->margins.top,
|
||||
d_ptr->margins.right,
|
||||
d_ptr->margins.bottom
|
||||
d_ptr->margins.left(),
|
||||
d_ptr->margins.top(),
|
||||
d_ptr->margins.right(),
|
||||
d_ptr->margins.bottom()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -600,8 +600,8 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
|
||||
if (width > 0 || height > 0) {
|
||||
double scaleX = double(getSize().width) / width;
|
||||
double scaleY = double(getSize().height) / height;
|
||||
scaleX -= (padding.left + padding.right) / width;
|
||||
scaleY -= (padding.top + padding.bottom) / height;
|
||||
scaleX -= (padding.left() + padding.right()) / width;
|
||||
scaleY -= (padding.top() + padding.bottom()) / height;
|
||||
minScale = util::min(scaleX, scaleY);
|
||||
}
|
||||
double zoom = getZoom() + util::log2(minScale);
|
||||
@@ -610,12 +610,12 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
|
||||
// Calculate the center point of a virtual bounds that is extended in all directions by padding.
|
||||
ScreenCoordinate centerPixel = nePixel + swPixel;
|
||||
ScreenCoordinate paddedNEPixel = {
|
||||
padding.right / minScale,
|
||||
padding.top / minScale,
|
||||
padding.right() / minScale,
|
||||
padding.top() / minScale,
|
||||
};
|
||||
ScreenCoordinate paddedSWPixel = {
|
||||
padding.left / minScale,
|
||||
padding.bottom / minScale,
|
||||
padding.left() / minScale,
|
||||
padding.bottom() / minScale,
|
||||
};
|
||||
centerPixel = centerPixel + paddedNEPixel - paddedSWPixel;
|
||||
centerPixel /= 2.0;
|
||||
|
||||
@@ -200,8 +200,8 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
|
||||
|
||||
/// w₀: Initial visible span, measured in pixels at the initial scale.
|
||||
/// Known henceforth as a <i>screenful</i>.
|
||||
double w0 = std::max(state.size.width - padding.left - padding.right,
|
||||
state.size.height - padding.top - padding.bottom);
|
||||
double w0 = std::max(state.size.width - padding.left() - padding.right(),
|
||||
state.size.height - padding.top() - padding.bottom());
|
||||
/// w₁: Final visible span, measured in pixels with respect to the initial
|
||||
/// scale.
|
||||
double w1 = w0 / state.zoomScale(zoom - startZoom);
|
||||
|
||||
@@ -34,8 +34,8 @@ LatLngBounds::LatLngBounds(const CanonicalTileID& id)
|
||||
|
||||
ScreenCoordinate EdgeInsets::getCenter(uint16_t width, uint16_t height) const {
|
||||
return {
|
||||
(width - left - right) / 2.0f + left,
|
||||
(height - top - bottom) / 2.0f + top,
|
||||
(width - left() - right()) / 2.0 + left(),
|
||||
(height - top() - bottom()) / 2.0 + top(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -471,14 +471,12 @@ TEST(Transform, DefaultTransform) {
|
||||
// Cannot assign invalid sizes.
|
||||
std::vector<Size> invalidSizes = { {}, { min, max }, { max, min } };
|
||||
for (const Size& size : invalidSizes) {
|
||||
ASSERT_TRUE(size.isEmpty());
|
||||
bool pass = false;
|
||||
try {
|
||||
transform.resize(size);
|
||||
ASSERT_TRUE(false) << "Should throw";
|
||||
} catch (...) {
|
||||
pass = true;
|
||||
ASSERT_TRUE(size.isEmpty());
|
||||
}
|
||||
ASSERT_TRUE(pass) << "Expected to throw";
|
||||
}
|
||||
|
||||
Size validSize { max, max };
|
||||
|
||||
@@ -6,6 +6,75 @@
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(LatLng, InvalidLatLng) {
|
||||
try {
|
||||
LatLng { NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "latitude must not be NaN");
|
||||
}
|
||||
try {
|
||||
LatLng { 0, NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "longitude must not be NaN");
|
||||
}
|
||||
try {
|
||||
LatLng { 91.0 };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "latitude must be between -90 and 90");
|
||||
}
|
||||
try {
|
||||
LatLng { 0, std::numeric_limits<double>::infinity() };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "longitude must not be infinite");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ProjectedMeters, InvalidProjectedMeters) {
|
||||
try {
|
||||
ProjectedMeters { NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "northing must not be NaN");
|
||||
}
|
||||
try {
|
||||
ProjectedMeters { 0, NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "easting must not be NaN");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EdgeInsets, InvalidEdgeInsets) {
|
||||
try {
|
||||
EdgeInsets { NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "top must not be NaN");
|
||||
}
|
||||
try {
|
||||
EdgeInsets { 0, NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "left must not be NaN");
|
||||
}
|
||||
try {
|
||||
EdgeInsets { 0, 0, NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "bottom must not be NaN");
|
||||
}
|
||||
try {
|
||||
EdgeInsets { 0, 0, 0, NAN };
|
||||
ASSERT_TRUE(false) << "should throw";
|
||||
} catch (const std::domain_error& error) {
|
||||
ASSERT_EQ(std::string(error.what()), "right must not be NaN");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LatLngBounds, World) {
|
||||
auto result = LatLngBounds::world();
|
||||
ASSERT_DOUBLE_EQ(-90, result.south());
|
||||
|
||||
@@ -38,18 +38,18 @@ TEST(Projection, MetersPerPixelAtLatitude) {
|
||||
TEST(Projection, ProjectedMeters) {
|
||||
auto latLng = LatLng {};
|
||||
auto projectedMeters = Projection::projectedMetersForLatLng(latLng);
|
||||
EXPECT_EQ(projectedMeters.northing, projectedMeters.easting);
|
||||
EXPECT_EQ(projectedMeters.northing(), projectedMeters.easting());
|
||||
EXPECT_EQ(latLng, Projection::latLngForProjectedMeters(projectedMeters));
|
||||
|
||||
const auto southWest = LatLng { -util::LATITUDE_MAX, -util::LONGITUDE_MAX };
|
||||
projectedMeters = Projection::projectedMetersForLatLng(southWest);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.northing, -20037508.342789274);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.easting, -20037508.342789244);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.northing(), -20037508.342789274);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.easting(), -20037508.342789244);
|
||||
|
||||
const auto northEast = LatLng { util::LATITUDE_MAX, util::LONGITUDE_MAX };
|
||||
projectedMeters = Projection::projectedMetersForLatLng(northEast);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.northing, 20037508.342789274);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.easting, 20037508.342789244);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.northing(), 20037508.342789274);
|
||||
EXPECT_DOUBLE_EQ(projectedMeters.easting(), 20037508.342789244);
|
||||
|
||||
projectedMeters = ProjectedMeters { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
|
||||
latLng = Projection::latLngForProjectedMeters(projectedMeters);
|
||||
|
||||
Reference in New Issue
Block a user