diff --git a/indexer/mercator.cpp b/indexer/mercator.cpp index ff22035db5..7ee65eebbe 100644 --- a/indexer/mercator.cpp +++ b/indexer/mercator.cpp @@ -23,3 +23,20 @@ m2::RectD MercatorBounds::MetresToXY(double lon, double lat, return m2::RectD(m2::PointD(LonToX(minLon), LatToY(minLat)), m2::PointD(LonToX(maxLon), LatToY(maxLat))); } + +m2::PointD MercatorBounds::GetSmPoint(m2::PointD const & pt, double lonMetresR, double latMetresR) +{ + double const lat = YToLat(pt.y); + double const lon = XToLon(pt.x); + + double const latDegreeOffset = latMetresR * degreeInMetres; + double const newLat = min(90.0, max(-90.0, lat + latDegreeOffset)); + + double const cosL = max(cos(my::DegToRad(newLat)), 0.00001); + ASSERT_GREATER ( cosL, 0.0, () ); + + double const lonDegreeOffset = lonMetresR * degreeInMetres / cosL; + double const newLon = min(180.0, max(-180.0, lon + lonDegreeOffset)); + + return m2::PointD(LonToX(newLon), LatToY(newLat)); +} diff --git a/indexer/mercator.hpp b/indexer/mercator.hpp index 261931c141..b3bd4f42a6 100644 --- a/indexer/mercator.hpp +++ b/indexer/mercator.hpp @@ -91,5 +91,7 @@ struct MercatorBounds return RectByCenterXYAndSizeInMeters(center.x, center.y, size, size); } + static m2::PointD GetSmPoint(m2::PointD const & pt, double lonMetresR, double latMetresR); + static double GetCellID2PointAbsEpsilon() { return 1.0E-4; } }; diff --git a/map/framework.cpp b/map/framework.cpp index 01c5409474..3f51699aff 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -165,8 +165,8 @@ Framework::Framework() m_drawPlacemark(false), //m_hasPendingShowRectFixed(false), - /// @todo Probably we can make this like static const int. - /// It's not a class state, so no need to store it in memory. + /// @todo It's not a class state, so no need to store it in memory. + /// Move this constants to Ruler (and don't store them at all). m_metresMinWidth(10), m_metresMaxWidth(1000000), #if defined(OMIM_OS_MAC) || defined(OMIM_OS_WINDOWS) || defined(OMIM_OS_LINUX) diff --git a/map/information_display.cpp b/map/information_display.cpp index 0592cbd992..ac9c6bd67d 100644 --- a/map/information_display.cpp +++ b/map/information_display.cpp @@ -156,8 +156,8 @@ void InformationDisplay::enableRuler(bool doEnable) void InformationDisplay::setRulerParams(unsigned pxMinWidth, double metresMinWidth, double metresMaxWidth) { m_ruler.setMinPxWidth(pxMinWidth); - m_ruler.setMinUnitsWidth(metresMinWidth); - m_ruler.setMaxUnitsWidth(metresMaxWidth); + m_ruler.setMinMetersWidth(metresMinWidth); + m_ruler.setMaxMetersWidth(metresMaxWidth); } void InformationDisplay::drawRuler(DrawerYG * pDrawer) diff --git a/map/ruler.cpp b/map/ruler.cpp index b8c80017ed..308ea9c43b 100644 --- a/map/ruler.cpp +++ b/map/ruler.cpp @@ -137,19 +137,19 @@ void Ruler::CalcMetresDiff(double v) if (arrU[0].m_i > v) { m_scalerText = string("< ") + arrU[0].m_s; - m_metresDiff = m_minUnitsWidth - 1; + m_metresDiff = m_minMetersWidth - 1.0; } else if (arrU[count-1].m_i <= v) { m_scalerText = string("> ") + arrU[count-1].m_s; - m_metresDiff = m_maxUnitsWidth + 1; + m_metresDiff = m_maxMetersWidth + 1.0; } else for (int i = 0; i < count; ++i) { if (arrU[i].m_i > v) { - m_metresDiff = arrU[i].m_i / m_conversionFn(1); + m_metresDiff = arrU[i].m_i / m_conversionFn(1.0); m_scalerText = arrU[i].m_s; break; } @@ -177,14 +177,14 @@ void Ruler::setMinPxWidth(unsigned minPxWidth) m_minPxWidth = minPxWidth; } -void Ruler::setMinUnitsWidth(double minUnitsWidth) +void Ruler::setMinMetersWidth(double v) { - m_minUnitsWidth = minUnitsWidth; + m_minMetersWidth = v; } -void Ruler::setMaxUnitsWidth(double maxUnitsWidth) +void Ruler::setMaxMetersWidth(double v) { - m_maxUnitsWidth = maxUnitsWidth; + m_maxMetersWidth = v; } void Ruler::setVisualScale(double visualScale) @@ -201,25 +201,26 @@ void Ruler::update() { if (m_isInitialized) { - m2::PointD glbPivot = m_screen.PtoG(pivot()); + int const rulerHeight = my::rounds(5 * m_visualScale); + int const minPxWidth = my::rounds(m_minPxWidth * m_visualScale); - int rulerHeight = static_cast(5 * m_visualScale); - unsigned minPxWidth = static_cast(m_minPxWidth * m_visualScale); + // pivot() here is the down right point of the ruler. + // Get global points of ruler and distance according to minPxWidth. - m2::PointD pt0 = m_screen.PtoG(pivot() - m2::PointD(minPxWidth / 2, 0)); - m2::PointD pt1 = m_screen.PtoG(pivot() + m2::PointD(minPxWidth / 2, 0)); + m2::PointD pt1 = m_screen.PtoG(pivot()); + m2::PointD pt0 = m_screen.PtoG(pivot() - m2::PointD(minPxWidth, 0)); - double const distanceInMetres = ms::DistanceOnEarth(MercatorBounds::YToLat(pt0.y), - MercatorBounds::XToLon(pt0.x), - MercatorBounds::YToLat(pt1.y), - MercatorBounds::XToLon(pt1.x)); + double const distanceInMetres = ms::DistanceOnEarth( + MercatorBounds::YToLat(pt0.y), MercatorBounds::XToLon(pt0.x), + MercatorBounds::YToLat(pt1.y), MercatorBounds::XToLon(pt1.x)); - /// converting into metres + // convert metres to units for calculating m_metresDiff CalcMetresDiff(m_conversionFn(distanceInMetres)); - bool const higherThanMax = m_metresDiff > m_maxUnitsWidth; - bool const lessThanMin = m_metresDiff < m_minUnitsWidth; + bool const higherThanMax = m_metresDiff > m_maxMetersWidth; + bool const lessThanMin = m_metresDiff < m_minMetersWidth; + // Calculate width of the ruler in pixels. double scalerWidthInPx = minPxWidth; if (higherThanMax) @@ -228,17 +229,13 @@ void Ruler::update() } else if (!lessThanMin) { - double a = ang::AngleTo(pt0, pt1); - m2::RectD r = MercatorBounds::RectByCenterXYAndSizeInMeters(glbPivot.x, - glbPivot.y, - abs(cos(a) * m_metresDiff / 2), - abs(sin(a) * m_metresDiff / 2)); + // Here we need to convert metres to pixels according to angle + // (in global coordinates) of the ruler. - pt0 = m_screen.GtoP(m2::PointD(r.minX(), r.minY())); - pt1 = m_screen.GtoP(m2::PointD(r.maxX(), r.maxY())); + double const a = ang::AngleTo(pt1, pt0); + pt0 = MercatorBounds::GetSmPoint(pt1, cos(a) * m_metresDiff, sin(a) * m_metresDiff); - scalerWidthInPx = pt0.Length(pt1); - scalerWidthInPx = abs(my::rounds(scalerWidthInPx)); + scalerWidthInPx = my::rounds(pivot().Length(m_screen.GtoP(pt0))); } m2::PointD scalerOrg = pivot() + m2::PointD(-scalerWidthInPx / 2, rulerHeight / 2); @@ -259,7 +256,6 @@ void Ruler::update() m_path.push_back(scalerOrg); m_path.push_back(scalerOrg + m2::PointD(scalerWidthInPx, 0)); - /// calculating bound rect m_boundRect = m2::RectD(m_path[0], m_path[1]); } else diff --git a/map/ruler.hpp b/map/ruler.hpp index 4115a7f7b7..8698f51c32 100644 --- a/map/ruler.hpp +++ b/map/ruler.hpp @@ -19,18 +19,27 @@ class Ruler : public yg::OverlayElement { private: + /// @todo Remove this variables. All this stuff are constants + /// (get values from Framework constructor) unsigned m_minPxWidth; unsigned m_maxPxWidth; - double m_minUnitsWidth; - double m_maxUnitsWidth; + double m_minMetersWidth; + double m_maxMetersWidth; + //@} + double m_visualScale; yg::FontDesc m_fontDesc; ScreenBase m_screen; - float m_metresDiff; //< current diff in units between two endpoints of the ruler. + /// Current diff in units between two endpoints of the ruler. + /// @todo No need to store it here. It's calculated once for calculating ruler's m_path. + double m_metresDiff; + string m_scalerText; + + /// @todo Make static array with 2 elements. vector m_path; m2::RectD m_boundRect; @@ -62,8 +71,8 @@ public: ScreenBase const & screen() const; void setMinPxWidth(unsigned minPxWidth); - void setMinUnitsWidth(double minUnitsWidth); - void setMaxUnitsWidth(double maxUnitsWidth); + void setMinMetersWidth(double v); + void setMaxMetersWidth(double v); void setVisualScale(double visualScale); void setFontDesc(yg::FontDesc const & fontDesc);