From 4ae7f95bad855c16864715e8d588b17e5995ba8b Mon Sep 17 00:00:00 2001 From: Kirill Zhdanovich Date: Fri, 24 May 2013 15:11:31 +0300 Subject: [PATCH] Parsing big url API --- map/framework.cpp | 60 +++++++++++ map/framework.hpp | 15 +++ map/map_tests/mwm_url_tests.cpp | 185 ++++++++++++++++++++++++++++++++ map/mwm_url.cpp | 30 +++++- map/mwm_url.hpp | 19 ++-- 5 files changed, 300 insertions(+), 9 deletions(-) diff --git a/map/framework.cpp b/map/framework.cpp index 64c94681f5..523a2ac4de 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -838,6 +838,7 @@ void Framework::DrawAdditionalInfo(shared_ptr const & e) m_informationDisplay.drawPlacemark(pDrawer, DEFAULT_BOOKMARK_TYPE, m_navigator.GtoP(m_placemark)); m_bmManager.DrawBookmarks(e); + DrawMapApiPoints(e); pScreen->endFrame(); @@ -1449,6 +1450,10 @@ bool Framework::SetViewportByURL(string const & url, url_api::Request & request) return true; } } + else if (strings::StartsWith(url, "mapswithme://") || strings::StartsWith(url, "mwm://")) + { + m_ParsedMapApi.SetUriAndParse(url); + } return false; } @@ -1614,3 +1619,58 @@ string Framework::CodeGe0url(double const lat, double const lon, double const zo return res; } + +void Framework::DrawMapApiPoints(shared_ptr const & e) +{ + Navigator & navigator = GetNavigator(); + InformationDisplay & informationDisplay = GetInformationDisplay(); + // get viewport limit rect + m2::AnyRectD const & glbRect = navigator.Screen().GlobalRect(); + Drawer * pDrawer = e->drawer(); + + vector const & v = GetMapApiPoints(); + + for (size_t i = 0; i < v.size(); ++i) + { + m2::PointD const & org = m2::PointD(MercatorBounds::LonToX(v[i].m_lon), + MercatorBounds::LatToY(v[i].m_lat)); + if (glbRect.IsPointInside(org)) + //ToDo Use Custom Pins + //super magic hack!!! Only purple! Only hardcore + informationDisplay.drawPlacemark(pDrawer, "placemark-purple", navigator.GtoP(org)); + } +} + +void Framework::MapApiSetUriAndParse(string const & url) +{ + m_ParsedMapApi.SetUriAndParse(url); +} + +//Dummy method. TODO create method that will run all layers without copy/past +bool Framework::GetMapApiPoint(m2::PointD const & pxPoint, url_scheme::ApiPoint & point) +{ + int const sm = TOUCH_PIXEL_RADIUS * GetVisualScale(); + m2::RectD const rect(PtoG(m2::PointD(pxPoint.x - sm, pxPoint.y - sm)), + PtoG(m2::PointD(pxPoint.x + sm, pxPoint.y + sm))); + double minD = numeric_limits::max(); + bool result = false; + + vector const & vect = m_ParsedMapApi.GetPoints(); + + for (size_t i = 0; i < vect.size();++i) + { + m2::PointD const pt = m2::PointD(m2::PointD(MercatorBounds::LonToX(vect[i].m_lon), + MercatorBounds::LatToY(vect[i].m_lat))); + if (rect.IsPointInside(pt)) + { + double const d = rect.Center().SquareLength(pt); + if (d < minD) + { + point = vect[i]; + minD = d; + result = true; + } + } + } + return result; +} diff --git a/map/framework.hpp b/map/framework.hpp index 79a669d08f..13e8caffdc 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -11,6 +11,7 @@ #include "bookmark.hpp" #include "bookmark_manager.hpp" #include "url_api.hpp" +#include "mwm_url.hpp" #include "../defines.hpp" @@ -451,4 +452,18 @@ public: public: string CodeGe0url(Bookmark const * bmk, bool const addName); string CodeGe0url(double const lat, double const lon, double const zoomLevel, string const & name); + +private: + url_scheme::ParsedMapApi m_ParsedMapApi; + void DrawMapApiPoints(shared_ptr const & e); + +public: + void MapApiSetUriAndParse(string const & url); + bool GetMapApiPoint(m2::PointD const & pxPoint, url_scheme::ApiPoint & point); + vector const & GetMapApiPoints() { return m_ParsedMapApi.GetPoints(); } + void ClearMapApiPoints() { m_ParsedMapApi.Clear(); } + int GetMapApiVersion() const { return m_ParsedMapApi.GetApiversion(); } + string const & GetMapApiAppTitle() const { return m_ParsedMapApi.GetAppTitle(); } + string const & GetMapApiBackUrl() const { return m_ParsedMapApi.GetGlobalBackUrl(); } + m2::RectD GetMapApiRect() const { return m_ParsedMapApi.GetRect(); } }; diff --git a/map/map_tests/mwm_url_tests.cpp b/map/map_tests/mwm_url_tests.cpp index 912659e13c..d823a89b1d 100644 --- a/map/map_tests/mwm_url_tests.cpp +++ b/map/map_tests/mwm_url_tests.cpp @@ -3,6 +3,9 @@ #include "../mwm_url.hpp" #include "../../coding/uri.hpp" +#include "../../base/string_format.hpp" +#include "../../base/pseudo_random.hpp" +#include "../../base/logging.hpp" using namespace url_scheme; @@ -18,6 +21,7 @@ UNIT_TEST(MapApiSmoke) TEST_EQUAL(api.GetPoints()[0].m_lon, -9.419289, ()); TEST_EQUAL(api.GetPoints()[0].m_title, "Point Name", ()); TEST_EQUAL(api.GetPoints()[0].m_url, "", ()); + TEST_EQUAL(api.GetGlobalBackUrl(), "", ()); } UNIT_TEST(MapApiInvalidUrl) @@ -88,3 +92,184 @@ UNIT_TEST(MapApiPointURLEncoded) TEST_EQUAL(api.GetPoints()[0].m_title, "\xd0\x9c\xd0\xb8\xd0\xbd\xd1\x81\xd0\xba", ()); TEST_EQUAL(api.GetPoints()[0].m_url, "http://map?ll=1,2&n=test", ()); } + +UNIT_TEST(GlobalBackUrl) +{ + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=ge0://")); + TEST_EQUAL(api.GetGlobalBackUrl(), "ge0://", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=ge0%3A%2F%2F")); + TEST_EQUAL(api.GetGlobalBackUrl(), "ge0://", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=http://mapswithme.com")); + TEST_EQUAL(api.GetGlobalBackUrl(), "http://mapswithme.com", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=someapp://%D0%9C%D0%BE%D0%B1%D0%B8%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5%20%D0%9A%D0%B0%D1%80%D1%82%D1%8B")); + TEST_EQUAL(api.GetGlobalBackUrl(), "someapp://\xd0\x9c\xd0\xbe\xd0\xb1\xd0\xb8\xd0\xbb\xd1\x8c\xd0\xbd\xd1\x8b\xd0\xb5 \xd0\x9a\xd0\xb0\xd1\x80\xd1\x82\xd1\x8b", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName")); + TEST_EQUAL(api.GetGlobalBackUrl(), "", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%3A%2F%2F%D0%BE%D1%82%D0%BA%D1%80%D0%BE%D0%B9%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D1%83")); + TEST_EQUAL(api.GetGlobalBackUrl(), "приложение://откройСсылку", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%3A%2F%2F%D0%BE%D1%82%D0%BA%D1%80%D0%BE%D0%B9%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D1%83")); + TEST_EQUAL(api.GetGlobalBackUrl(), "приложение://откройСсылку", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName&backurl=%E6%88%91%E6%84%9Bmapswithme")); + TEST_EQUAL(api.GetGlobalBackUrl(), "我愛mapswithme", ()); + } +} + +UNIT_TEST(VersionTest) +{ + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&v=1&n=PointName")); + TEST_EQUAL(api.GetApiversion(), 1, ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&v=kotik&n=PointName")); + TEST_EQUAL(api.GetApiversion(), 0, ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&v=APacanyVoobsheKotjata&n=PointName")); + TEST_EQUAL(api.GetApiversion(), 0, ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&n=PointName")); + TEST_EQUAL(api.GetApiversion(), 0, ()); + } + { + ParsedMapApi api(Uri("mwm://map?v=666&ll=1,2&n=PointName")); + TEST_EQUAL(api.GetApiversion(), 666, ()); + } +} + +UNIT_TEST(AppNameTest) +{ + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&v=1&n=PointName&appname=Google")); + TEST_EQUAL(api.GetAppTitle(), "Google", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&v=1&n=PointName&appname=%D0%AF%D0%BD%D0%B4%D0%B5%D0%BA%D1%81")); + TEST_EQUAL(api.GetAppTitle(), "Яндекс", ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=1,2&v=1&n=PointName")); + TEST_EQUAL(api.GetAppTitle(), "", ()); + } +} + +UNIT_TEST(RectTest) +{ + { + ParsedMapApi api(Uri("mwm://map?ll=0,0")); + m2::RectD rect = api.GetRect(); + TEST_EQUAL(rect.maxX(), 0, ()); + TEST_EQUAL(rect.maxY(), 0, ()); + TEST_EQUAL(rect.minX(), 0, ()); + TEST_EQUAL(rect.minX(), 0, ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=0,0&ll=1,1&ll=2,2&ll=3,3&ll=4,4&ll=5,5&")); + m2::RectD rect = api.GetRect(); + TEST_EQUAL(rect.maxX(), 5, ()); + TEST_EQUAL(rect.maxY(), 5, ()); + TEST_EQUAL(rect.minX(), 0, ()); + TEST_EQUAL(rect.minX(), 0, ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=-90,90&ll=90,-90")); + m2::RectD rect = api.GetRect(); + TEST_EQUAL(rect.maxX(), 90, ()); + TEST_EQUAL(rect.maxY(), 90, ()); + TEST_EQUAL(rect.minX(), -90, ()); + TEST_EQUAL(rect.minX(), -90, ()); + } + { + ParsedMapApi api(Uri("mwm://map?ll=180,180&ll=0,0&ll=-180,-180")); + m2::RectD rect = api.GetRect(); + TEST_EQUAL(rect.maxX(), 0, ()); + TEST_EQUAL(rect.maxY(), 0, ()); + TEST_EQUAL(rect.minX(), 0, ()); + TEST_EQUAL(rect.minX(), 0, ()); + } + { + ParsedMapApi api(Uri("mwm://")); + m2::RectD rect = api.GetRect(); + TEST(!rect.IsValid(), ()); + } +} + +namespace +{ +string generatePartOfUrl(url_scheme::ApiPoint const & point) +{ + stringstream stream; + stream << "&ll=" << strings::ToString(point.m_lat) << "," << strings::ToString(point.m_lon) + << "&n=" << point.m_title + << "&u=" << point.m_title; + return stream.str(); +} + +string randomString(size_t size, size_t seed) +{ + string result(size, '0'); + LCG32 random(seed); + for (size_t i = 0; i < size; ++i) + result[i] = 'a' + random.Generate() % 26; + return result; +} + +void generateRandomTest(size_t numberOfPoints, size_t stringLength) +{ + vector vect(numberOfPoints); + for (size_t i = 0; i < numberOfPoints; ++i) + { + url_scheme::ApiPoint point; + LCG32 random(i); + point.m_lat = random.Generate() % 90; + point.m_lat *= random.Generate() % 2 == 0 ? 1 : -1; + point.m_lon = random.Generate() % 180; + point.m_lon *= random.Generate() % 2 == 0 ? 1 : -1; + point.m_title = randomString(stringLength, i); + point.m_url = randomString(stringLength, i); + vect[i] = point; + } + string result = "mapswithme://map?v=1"; + for (size_t i = 0; i < vect.size(); ++i) + result += generatePartOfUrl(vect[i]); + Uri uri(result); + ParsedMapApi api(uri); + vector const & points = api.GetPoints(); + TEST_EQUAL(points.size(), vect.size(), ()); + for (size_t i = 0; i < vect.size();++i) + { + TEST_EQUAL(points[i].m_lat, vect[i].m_lat, ()); + TEST_EQUAL(points[i].m_lon, vect[i].m_lon, ()); + TEST_EQUAL(points[i].m_title, vect[i].m_title, ()); + TEST_EQUAL(points[i].m_url, vect[i].m_url, ()); + } + TEST_EQUAL(api.GetApiversion(), 1, ()); +} + +} + +UNIT_TEST(100FullEnteriesRandomTest) +{ + generateRandomTest(100, 10); +} + +UNIT_TEST(StressTestRandomTest) +{ + generateRandomTest(10000, 100); +} diff --git a/map/mwm_url.cpp b/map/mwm_url.cpp index c020ac2dbd..f9dd4020f7 100644 --- a/map/mwm_url.cpp +++ b/map/mwm_url.cpp @@ -21,7 +21,10 @@ bool IsInvalidApiPoint(ApiPoint const & p) { return p.m_lat == INVALID_LAT_VALUE } // unnames namespace -ParsedMapApi::ParsedMapApi(Uri const & uri) +ParsedMapApi::ParsedMapApi():m_id(0) +{} + +ParsedMapApi::ParsedMapApi(Uri const & uri):m_id(0) { if (!Parse(uri)) { @@ -29,6 +32,12 @@ ParsedMapApi::ParsedMapApi(Uri const & uri) } } +void ParsedMapApi::SetUriAndParse(string const & url) +{ + Clear(); + Parse(url_scheme::Uri(url)); +} + bool ParsedMapApi::IsValid() const { return !m_points.empty(); @@ -48,6 +57,15 @@ bool ParsedMapApi::Parse(Uri const & uri) void ParsedMapApi::AddKeyValue(string const & key, string const & value) { + if (key == "backurl") + m_globalBackUrl = value; + if (key == "v") + { + if (!strings::to_int(value, m_id)) + m_id = 0; + } + if (key == "appname") + m_appTitle = value; if (key == "ll") { m_points.push_back(ApiPoint()); @@ -82,6 +100,7 @@ void ParsedMapApi::AddKeyValue(string const & key, string const & value) m_points.back().m_lat = lat; m_points.back().m_lon = lon; + m_showRect = m2::Add(m_showRect, m2::PointD(lat, lon)); } else if (key == "n") { @@ -98,3 +117,12 @@ void ParsedMapApi::AddKeyValue(string const & key, string const & value) LOG(LWARNING, ("Map API: Point url with no point. 'll' should come first!")); } } + +void ParsedMapApi::Clear() +{ + m_points.clear(); + m_globalBackUrl.clear(); + m_appTitle.clear(); + m_id = 0; + m_showRect = m2::RectD(); +} diff --git a/map/mwm_url.hpp b/map/mwm_url.hpp index b73617c902..a0bfeb9c80 100644 --- a/map/mwm_url.hpp +++ b/map/mwm_url.hpp @@ -19,23 +19,26 @@ class ParsedMapApi { public: ParsedMapApi(Uri const & uri); + ParsedMapApi(); + void SetUriAndParse(string const & url); bool IsValid() const; vector const & GetPoints() const { return m_points; } + string const & GetGlobalBackUrl() const { return m_globalBackUrl; } + string const & GetAppTitle() const { return m_appTitle; } + int GetApiversion() const { return m_id; } + m2::RectD GetRect() const { return m_showRect; } + void Clear(); private: bool Parse(Uri const & uri); void AddKeyValue(string const & key, string const & value); vector m_points; - /* - string m_title; - string m_backTitle; - string m_backUrl; - // Calculated from zoom parameter or from m_points + string m_globalBackUrl; + string m_appTitle; + int m_id; + //Lon Lat coordinates m2::RectD m_showRect; - - // vector m_iconData; - */ }; }