diff --git a/indexer/feature.cpp b/indexer/feature.cpp index 384163f5e5..4660e96e37 100644 --- a/indexer/feature.cpp +++ b/indexer/feature.cpp @@ -2,6 +2,9 @@ #include "feature_visibility.hpp" #include "feature_loader_base.hpp" +#include "../geometry/distance.hpp" +#include "../geometry/robust_orientation.hpp" + #include "../platform/preferred_languages.hpp" #include "../defines.hpp" // just for file extensions @@ -290,3 +293,81 @@ double FeatureType::GetPopulationDrawRank() const return min(upperBound, static_cast(n)) / upperBound; } } + +namespace +{ + class DoCalcDistance + { + m2::PointD m_prev, m_pt; + bool m_hasPrev; + + static double Inf() { return numeric_limits::max(); } + + static double GetDistance(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p) + { + mn::DistanceToLineSquare calc; + calc.SetBounds(p1, p2); + return sqrt(calc(p)); + } + + public: + DoCalcDistance(m2::PointD const & pt) + : m_pt(pt), m_hasPrev(false), m_dist(Inf()) + { + } + + void TestPoint(m2::PointD const & p) + { + m_dist = m_pt.Length(p); + } + + void operator() (CoordPointT const & p) + { + m2::PointD pt(p.first, p.second); + + if (m_hasPrev) + m_dist = min(m_dist, GetDistance(m_prev, pt, m_pt)); + else + m_hasPrev = true; + + m_prev = pt; + } + + void operator() (m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + m2::PointD arrP[] = { p1, p2, p3 }; + + // make right-oriented triangle + if (m2::robust::OrientedS(arrP[0], arrP[1], arrP[2]) < 0.0) + swap(arrP[1], arrP[2]); + + double d = Inf(); + for (size_t i = 0; i < 3; ++i) + { + double const s = m2::robust::OrientedS(arrP[i], arrP[(i + 1) % 3], m_pt); + if (s < 0.0) + d = min(d, GetDistance(arrP[i], arrP[(i + 1) % 3], m_pt)); + } + + m_dist = ((d == Inf()) ? 0.0 : min(m_dist, d)); + } + + double m_dist; + }; +} + +double FeatureType::GetDistance(m2::PointD const & pt, int scale) const +{ + DoCalcDistance calc(pt); + + switch (GetFeatureType()) + { + case GEOM_POINT: calc.TestPoint(GetCenter()); break; + case GEOM_LINE: ForEachPointRef(calc, scale); break; + case GEOM_AREA: ForEachTriangleRef(calc, scale); break; + default: + CHECK ( false, () ); + } + + return calc.m_dist; +} diff --git a/indexer/feature.hpp b/indexer/feature.hpp index 33b8def83d..a28c7ce15a 100644 --- a/indexer/feature.hpp +++ b/indexer/feature.hpp @@ -239,6 +239,8 @@ public: inline string GetRoadNumber() const { return m_Params.ref; } + double GetDistance(m2::PointD const & pt, int scale) const; + /// @name Statistic functions. //@{ inline void ParseBeforeStatistic() const diff --git a/map/framework.cpp b/map/framework.cpp index b1a8da371b..29fdebffa4 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -13,19 +13,16 @@ #include "../search/result.hpp" #include "../indexer/categories_holder.hpp" -#include "../indexer/feature_visibility.hpp" #include "../indexer/feature.hpp" #include "../indexer/scales.hpp" -#include "../indexer/drawing_rules.hpp" #include "../base/math.hpp" #include "../base/string_utils.hpp" #include "../std/algorithm.hpp" -#include "../std/fstream.hpp" #include "../std/target_os.hpp" +#include "../std/vector.hpp" -using namespace feature; void Framework::AddMap(string const & file) { @@ -250,10 +247,10 @@ RenderPolicy::TRenderFn Framework::DrawModelFn() /// Actual rendering function. void Framework::DrawModel(shared_ptr const & e, - ScreenBase const & screen, - m2::RectD const & selectRect, - m2::RectD const & clipRect, - int scaleLevel) + ScreenBase const & screen, + m2::RectD const & selectRect, + m2::RectD const & clipRect, + int scaleLevel) { fwork::DrawProcessor doDraw(clipRect, screen, e, scaleLevel); @@ -676,3 +673,112 @@ void Framework::DeleteOldMaps() } m_lowestMapVersion = MAXIMUM_VERSION_TO_DELETE + 1; } + +namespace +{ + class DoGetFeatureTypes + { + typedef vector TypesC; + + class DistanceT + { + double m_dist; + + public: + DistanceT(double d, TypesC & types) : m_dist(d) + { + m_types.swap(types); + } + + bool operator<(DistanceT const & rhs) const + { + return (m_dist < rhs.m_dist); + } + + TypesC m_types; + }; + + class DoParseTypes + { + Classificator const & m_c; + + public: + DoParseTypes() : m_c(classif()) {} + + void operator() (uint32_t t) + { + m_types.push_back(m_c.GetFullObjectName(t)); + } + + TypesC m_types; + }; + + double GetCompareEpsilon(feature::EGeomType type) const + { + using namespace feature; + switch (type) + { + case GEOM_POINT: return 0.0 * m_eps; + case GEOM_LINE: return 1.0 * m_eps; + case GEOM_AREA: return 2.0 * m_eps; + default: + ASSERT ( false, () ); + return numeric_limits::max(); + } + } + + m2::PointD m_pt; + double m_eps; + int m_scale; + + vector m_cont; + + public: + DoGetFeatureTypes(m2::PointD const & pt, double eps, int scale) + : m_pt(pt), m_eps(eps), m_scale(scale) + { + } + + void operator() (FeatureType const & f) + { + double const d = f.GetDistance(m_pt, m_scale); + ASSERT_GREATER_OR_EQUAL(d, 0.0, ()); + + if (d <= m_eps) + { + DoParseTypes doParse; + f.ForEachTypeRef(doParse); + + m_cont.push_back(DistanceT(d + GetCompareEpsilon(f.GetFeatureType()), doParse.m_types)); + } + } + + void GetFeatureTypes(size_t count, TypesC & types) + { + sort(m_cont.begin(), m_cont.end()); + + for (size_t i = 0; i < min(count, m_cont.size()); ++i) + types.insert(types.end(), m_cont[i].m_types.begin(), m_cont[i].m_types.end()); + } + }; +} + +void Framework::GetFeatureTypes(m2::PointD pt, vector & types) const +{ + pt = m_navigator.ShiftPoint(pt); + + int const sm = 20; + m2::RectD pixR(m2::PointD(pt.x - sm, pt.y - sm), m2::PointD(pt.x + sm, pt.y + sm)); + + m2::RectD glbR; + m_navigator.Screen().PtoG(pixR, glbR); + + int const scale = my::rounds(GetCurrentScale()); + DoGetFeatureTypes getTypes(m_navigator.Screen().PtoG(pt), + max(glbR.SizeX() / 2.0, glbR.SizeY() / 2.0), + scale); + + m_model.ForEachFeature(glbR, getTypes, scale); + + getTypes.GetFeatureTypes(5, types); +} diff --git a/map/framework.hpp b/map/framework.hpp index 35ab175f87..8d3a88b3d9 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -172,6 +172,8 @@ public: Invalidate(true); } + void GetFeatureTypes(m2::PointD pt, vector & types) const; + virtual void BeginPaint(shared_ptr const & e); /// Function for calling from platform dependent-paint function. virtual void DoPaint(shared_ptr const & e); diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index 528a804f92..6b9b9e774a 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -10,6 +10,7 @@ #include "../platform/settings.hpp" #include +#include using namespace storage; @@ -287,20 +288,35 @@ namespace qt { if (e->modifiers() & Qt::ControlModifier) { - /// starting rotation + // starting rotation m_framework->StartRotate(get_rotate_event(e->pos(), this->rect().center())); + setCursor(Qt::CrossCursor); m_isRotate = true; } else { - /// starting drag + // starting drag m_framework->StartDrag(get_drag_event(e)); setCursor(Qt::CrossCursor); m_isDrag = true; } } + else if (e->button() == Qt::RightButton) + { + // show feature types + QPoint const & pt = e->pos(); + + vector types; + m_framework->GetFeatureTypes(m2::PointD(pt.x(), pt.y()), types); + + QMenu menu; + for (size_t i = 0; i < types.size(); ++i) + menu.addAction(QString::fromAscii(types[i].c_str())); + + menu.exec(pt); + } } void DrawWidget::mouseDoubleClickEvent(QMouseEvent * e) @@ -353,6 +369,7 @@ namespace qt if (m_isRotate && (e->button() == Qt::LeftButton)) { m_framework->StopRotate(get_rotate_event(e->pos(), this->rect().center())); + setCursor(Qt::ArrowCursor); m_isRotate = false; } @@ -389,15 +406,17 @@ namespace qt void DrawWidget::wheelEvent(QWheelEvent * e) { - if ((!m_isDrag) && (!m_isRotate)) + if (!m_isDrag && !m_isRotate) { - /// if we are inside the timer, cancel it + // if we are inside the timer, cancel it if (m_timer->isActive()) m_timer->stop(); m_timer->start(m_redrawInterval); + //m_framework->Scale(exp(e->delta() / 360.0)); m_framework->ScaleToPoint(ScaleToPointEvent(e->pos().x(), e->pos().y(), exp(e->delta() / 360.0))); + UpdateScaleControl(); emit ViewportChanged(); }