From f79ed2af2c7399a72165295fa46688e1390f98b7 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 11 Jul 2018 14:25:06 +0300 Subject: [PATCH] City boundaries visualization. --- map/framework.cpp | 73 ++++++++++++++++++++++++++--- map/framework.hpp | 1 + qt/draw_widget.cpp | 28 +++++++++-- qt/draw_widget.hpp | 2 + qt/mainwindow.cpp | 30 +++++++++++- qt/mainwindow.hpp | 2 + qt/res/city_boundaries.png | Bin 0 -> 904 bytes qt/res/resources.qrc | 1 + search/cities_boundaries_table.cpp | 17 +++++++ search/cities_boundaries_table.hpp | 15 ++++++ 10 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 qt/res/city_boundaries.png diff --git a/map/framework.cpp b/map/framework.cpp index 9039f3cdaa..ff1c9b04bf 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -20,6 +20,7 @@ #include "routing_common/num_mwm_id.hpp" +#include "search/cities_boundaries_table.hpp" #include "search/downloader_search_callback.hpp" #include "search/editor_delegate.hpp" #include "search/engine.hpp" @@ -3119,12 +3120,34 @@ storage::TCountriesVec Framework::GetTopmostCountries(ms::LatLon const & latlon) namespace { - vector colorList = { dp::Color(255, 0, 0, 255), dp::Color(0, 255, 0, 255), dp::Color(0, 0, 255, 255), - dp::Color(255, 255, 0, 255), dp::Color(0, 255, 255, 255), dp::Color(255, 0, 255, 255), - dp::Color(100, 0, 0, 255), dp::Color(0, 100, 0, 255), dp::Color(0, 0, 100, 255), - dp::Color(100, 100, 0, 255), dp::Color(0, 100, 100, 255), dp::Color(100, 0, 100, 255) - }; -} // namespace +vector colorList = { + dp::Color(255, 0, 0, 255), dp::Color(0, 255, 0, 255), dp::Color(0, 0, 255, 255), + dp::Color(255, 255, 0, 255), dp::Color(0, 255, 255, 255), dp::Color(255, 0, 255, 255), + dp::Color(100, 0, 0, 255), dp::Color(0, 100, 0, 255), dp::Color(0, 0, 100, 255), + dp::Color(100, 100, 0, 255), dp::Color(0, 100, 100, 255), dp::Color(100, 0, 100, 255)}; + +dp::Color const cityBoundaryBBColor = dp::Color(255, 0, 0, 255); +dp::Color const cityBoundaryCBColor = dp::Color(0, 255, 0, 255); +dp::Color const cityBoundaryDBColor = dp::Color(0, 0, 255, 255); + +template +void DrawLine(Box const & box, dp::Color const & color, df::DrapeApi & drapeApi, string const & id) +{ + auto points = box.Points(); + CHECK(!points.empty(), ()); + points.push_back(points.front()); + + points.erase(unique(points.begin(), points.end(), [](m2::PointD const & p1, m2::PointD const & p2) { + m2::PointD const delta = p2 - p1; + return delta.IsAlmostZero(); + }), points.end()); + + if (points.size() <= 1) + return; + + drapeApi.AddLine(id, df::DrapeApiLineData(points, color).Width(3.0f).ShowPoints(true).ShowId()); +} +} // namespace void Framework::VisualizeRoadsInRect(m2::RectD const & rect) { @@ -3155,6 +3178,44 @@ void Framework::VisualizeRoadsInRect(m2::RectD const & rect) }, kScale); } +void Framework::VisualizeCityBoundariesInRect(m2::RectD const & rect) +{ + search::CitiesBoundariesTable table(GetDataSource()); + table.Load(); + + vector featureIds; + GetCityBoundariesInRectForTesting(table, rect, featureIds); + + for (auto const fid : featureIds) + { + search::CitiesBoundariesTable::Boundaries boundaries; + table.Get(fid, boundaries); + + string id = "fid:" + strings::to_string(fid); + FeaturesLoaderGuard loader(GetDataSource(), GetDataSource().GetMwmIdByCountryFile(CountryFile("World"))); + FeatureType ft; + if (loader.GetFeatureByIndex(fid, ft)) + { + string name; + ft.GetName(FeatureType::DEFAULT_LANG, name); + id += ", name:" + name; + } + + size_t const boundariesSize = boundaries.GetBoundariesForTesting().size(); + for (size_t i = 0; i < boundariesSize; ++i) + { + string idWithIndex = id; + auto const & cityBoundary = boundaries.GetBoundariesForTesting()[i]; + if (boundariesSize > 1) + idWithIndex = id + " , i:" + strings::to_string(i); + + DrawLine(cityBoundary.m_bbox, cityBoundaryBBColor, m_drapeApi, idWithIndex + ", bb"); + DrawLine(cityBoundary.m_cbox, cityBoundaryCBColor, m_drapeApi, idWithIndex + ", cb"); + DrawLine(cityBoundary.m_dbox, cityBoundaryDBColor, m_drapeApi, idWithIndex + ", db"); + } + } +} + ads::Engine const & Framework::GetAdsEngine() const { ASSERT(m_adsEngine, ()); diff --git a/map/framework.hpp b/map/framework.hpp index bd2619eebf..149366e8ec 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -339,6 +339,7 @@ public: // Utilities void VisualizeRoadsInRect(m2::RectD const & rect); + void VisualizeCityBoundariesInRect(m2::RectD const & rect); ads::Engine const & GetAdsEngine() const; diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index 665338231c..3be0c5e4e8 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -19,6 +19,8 @@ #include "platform/platform.hpp" #include "platform/settings.hpp" +#include "base/assert.hpp" + #include #include @@ -187,7 +189,7 @@ void DrawWidget::mousePressEvent(QMouseEvent * e) { SubmitBookmark(pt); } - else if (!m_selectionMode || IsCommandModifier(e)) + else if (!(m_selectionMode || m_cityBoundariesSelectionMode) || IsCommandModifier(e)) { ShowInfoPopup(e, pt); } @@ -208,7 +210,8 @@ void DrawWidget::mouseMoveEvent(QMouseEvent * e) if (IsLeftButton(e) && !IsAltModifier(e)) m_framework.TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_MOVE)); - if (m_selectionMode && m_rubberBand != nullptr && m_rubberBand->isVisible()) + if ((m_selectionMode || m_cityBoundariesSelectionMode) && m_rubberBand != nullptr && + m_rubberBand->isVisible()) { m_rubberBand->setGeometry(QRect(m_rubberBandOrigin, e->pos()).normalized()); } @@ -221,15 +224,25 @@ void DrawWidget::mouseReleaseEvent(QMouseEvent * e) { m_framework.TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_UP)); } - else if (m_selectionMode && IsRightButton(e) && m_rubberBand != nullptr && - m_rubberBand->isVisible()) + else if ((m_selectionMode || m_cityBoundariesSelectionMode) && IsRightButton(e) && + m_rubberBand != nullptr && m_rubberBand->isVisible()) { QPoint const lt = m_rubberBand->geometry().topLeft(); QPoint const rb = m_rubberBand->geometry().bottomRight(); m2::RectD rect; rect.Add(m_framework.PtoG(m2::PointD(L2D(lt.x()), L2D(lt.y())))); rect.Add(m_framework.PtoG(m2::PointD(L2D(rb.x()), L2D(rb.y())))); - m_framework.VisualizeRoadsInRect(rect); + if (m_selectionMode) + { + CHECK(!m_cityBoundariesSelectionMode, ()); + m_framework.VisualizeRoadsInRect(rect); + } + else + { + CHECK(m_cityBoundariesSelectionMode, ()); + m_framework.VisualizeCityBoundariesInRect(rect); + } + m_rubberBand->hide(); } } @@ -507,6 +520,11 @@ void DrawWidget::SetRouter(routing::RouterType routerType) void DrawWidget::SetSelectionMode(bool mode) { m_selectionMode = mode; } +void DrawWidget::SetCityBoundariesSelectionMode(bool mode) +{ + m_cityBoundariesSelectionMode = mode; +} + // static void DrawWidget::SetDefaultSurfaceFormat(bool apiOpenGLES3) { diff --git a/qt/draw_widget.hpp b/qt/draw_widget.hpp index 46edb9e1e5..ccb7d9d43a 100644 --- a/qt/draw_widget.hpp +++ b/qt/draw_widget.hpp @@ -73,6 +73,7 @@ public: void RetryToDownloadCountry(storage::TCountryId const & countryId); void SetSelectionMode(bool mode); + void SetCityBoundariesSelectionMode(bool mode); RouteMarkType GetRoutePointAddMode() const { return m_routePointAddMode; } void SetRoutePointAddMode(RouteMarkType mode) { m_routePointAddMode = mode; } @@ -114,6 +115,7 @@ private: storage::TCountryId m_countryId; bool m_selectionMode = false; + bool m_cityBoundariesSelectionMode = false; RouteMarkType m_routePointAddMode = RouteMarkType::Finish; }; } // namespace qt diff --git a/qt/mainwindow.cpp b/qt/mainwindow.cpp index 4a3b9c80a8..2712c5934d 100644 --- a/qt/mainwindow.cpp +++ b/qt/mainwindow.cpp @@ -323,6 +323,12 @@ void MainWindow::CreateNavigationBar() m_selectionMode->setCheckable(true); m_selectionMode->setToolTip(tr("Turn on/off selection mode")); + m_selectionCityBoundariesMode = + pToolBar->addAction(QIcon(":/navig64/city_boundaries.png"), tr("City boundaries selection mode"), + this, SLOT(OnSwitchCityBoundariesSelectionMode())); + m_selectionCityBoundariesMode->setCheckable(true); + m_selectionCityBoundariesMode->setChecked(m_pDrawWidget->GetFramework().LoadTrafficEnabled()); + m_clearSelection = pToolBar->addAction(QIcon(":/navig64/clear.png"), tr("Clear selection"), this, SLOT(OnClearSelection())); m_clearSelection->setToolTip(tr("Clear selection")); @@ -549,10 +555,32 @@ void MainWindow::OnCreateFeatureClicked() void MainWindow::OnSwitchSelectionMode() { + if (m_selectionCityBoundariesMode->isChecked()) + { + // Unchecking selection city boundaries mode. + m_selectionCityBoundariesMode->setChecked(false); + m_pDrawWidget->SetCityBoundariesSelectionMode(false); + } + m_pDrawWidget->SetSelectionMode(m_selectionMode->isChecked()); } -void MainWindow::OnClearSelection() { m_pDrawWidget->GetFramework().GetDrapeApi().Clear(); } +void MainWindow::OnSwitchCityBoundariesSelectionMode() +{ + if (m_selectionMode->isChecked()) + { + // Unchecking selection mode. + m_selectionMode->setChecked(false); + m_pDrawWidget->SetSelectionMode(false); + } + + m_pDrawWidget->SetCityBoundariesSelectionMode(m_selectionCityBoundariesMode->isChecked()); +} + +void MainWindow::OnClearSelection() +{ + m_pDrawWidget->GetFramework().GetDrapeApi().Clear(); +} void MainWindow::OnSearchButtonClicked() { diff --git a/qt/mainwindow.hpp b/qt/mainwindow.hpp index ddf2cdee6e..5ae9a88d90 100644 --- a/qt/mainwindow.hpp +++ b/qt/mainwindow.hpp @@ -45,6 +45,7 @@ class MainWindow : public QMainWindow, location::LocationObserver QAction * m_clearSelection = nullptr; QAction * m_pSearchAction = nullptr; QAction * m_trafficEnableAction = nullptr; + QAction * m_selectionCityBoundariesMode = nullptr; QToolButton * m_routePointsToolButton = nullptr; QAction * m_selectStartRoutePoint = nullptr; QAction * m_selectFinishRoutePoint = nullptr; @@ -105,6 +106,7 @@ protected Q_SLOTS: void OnRetryDownloadClicked(); void OnSwitchSelectionMode(); + void OnSwitchCityBoundariesSelectionMode(); void OnClearSelection(); void OnTrafficEnabled(); diff --git a/qt/res/city_boundaries.png b/qt/res/city_boundaries.png new file mode 100644 index 0000000000000000000000000000000000000000..87739245056637025268cc3a9a22f5b4fb49f923 GIT binary patch literal 904 zcmV;319$w1P)Px&LPTeN6ax)R6a*E;kN05eXlAQtx_YN~X7{R~=$_tJwN-DbYkPKgVZp^2 za0Z+KXTTY72AlzBz!`7`oPkxyz?u$nzG3ij-GH45=tV^N5BeiPiMs@y3%QRhm{6{s z9|{ZWCk*5z?0*`3>;!hV0Y6x%RKOu)gxmtGnwhD9wFq`uNpgp>W?8xk@RgduQ(T{| ztrfp)+XDQQScLw8euau`kV)paDX?3I`8Md=U~Ots-r*E^4bb*LA67pOB$&0}{u1-l16 zhE*i){gxY+bbg>lnEXvAu4AoF=Gs`!Jz!Zo!6j zQ=8`yP%5~6aD1zjbNIaq{cZKHZ2_x~HoR;B*&e0F%~~Kss(_CO@SGOpgll(Uzdz<< zNCbRGkdI@5PO$#1$u@@omZ&CL!?R#DpT*a52w+f_pg%h#w2$O#Qb%&K7u2u0?wPnf*teIB>~6b z{Umf(^)7!g@)xBC&=1vG>R{;`7HsPKui#R9z-?G?KWfUO=;;-Yselectmode.png clear.png traffic.png + city_boundaries.png point-finish.png point-intermediate.png point-start.png diff --git a/search/cities_boundaries_table.cpp b/search/cities_boundaries_table.cpp index 55d9c39c2c..07a2e3d8bf 100644 --- a/search/cities_boundaries_table.cpp +++ b/search/cities_boundaries_table.cpp @@ -105,4 +105,21 @@ bool CitiesBoundariesTable::Get(uint32_t fid, Boundaries & bs) const bs = Boundaries(it->second, m_eps); return true; } + +void GetCityBoundariesInRectForTesting(CitiesBoundariesTable const & table, m2::RectD const & rect, + vector & featureIds) +{ + featureIds.clear(); + for (auto const & kv : table.m_table) + { + for (auto const & cb : kv.second) + { + if (rect.IsIntersect(m2::RectD(cb.m_bbox.Min(), cb.m_bbox.Max()))) + { + featureIds.push_back(kv.first); + break; + } + } + } +} } // namespace search diff --git a/search/cities_boundaries_table.hpp b/search/cities_boundaries_table.hpp index e9ac3841fa..fbe60ffe01 100644 --- a/search/cities_boundaries_table.hpp +++ b/search/cities_boundaries_table.hpp @@ -4,6 +4,7 @@ #include "indexer/feature_decl.hpp" #include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" #include #include @@ -23,6 +24,10 @@ namespace search { class CitiesBoundariesTable { + friend void GetCityBoundariesInRectForTesting(CitiesBoundariesTable const &, + m2::RectD const & rect, + std::vector & featureIds); + public: class Boundaries { @@ -43,6 +48,8 @@ public: // |*this|. bool HasPoint(m2::PointD const & p) const; + std::vector const & GetBoundariesForTesting() const { return m_boundaries; } + friend std::string DebugPrint(Boundaries const & boundaries) { std::ostringstream os; @@ -68,10 +75,18 @@ public: bool Get(FeatureID const & fid, Boundaries & bs) const; bool Get(uint32_t fid, Boundaries & bs) const; + size_t GetSize() const { return m_table.size(); } + private: DataSource const & m_dataSource; MwmSet::MwmId m_mwmId; std::unordered_map> m_table; double m_eps = 0.0; }; + +/// \brief Fills |featureIds| with feature ids of city boundaries if bounding rect of +/// the city boundary crosses |rect|. +/// \note This method is inefficient and is written for debug and test purposes only. +void GetCityBoundariesInRectForTesting(CitiesBoundariesTable const &, m2::RectD const & rect, + std::vector & featureIds); } // namespace search