From 96eadf606e3426aa857630e1806c444a8e1a7bc5 Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Wed, 27 Jul 2016 12:02:55 +0300 Subject: [PATCH 1/8] [api] Routing api. --- coding/uri.cpp | 6 +- coding/uri.hpp | 5 +- map/framework.cpp | 65 ++++++++----- map/framework.hpp | 4 + map/geourl_process.cpp | 8 +- map/mwm_url.cpp | 216 +++++++++++++++++++++++++++++------------ map/mwm_url.hpp | 33 +++++-- routing/router.cpp | 19 +++- routing/router.hpp | 1 + 9 files changed, 255 insertions(+), 102 deletions(-) diff --git a/coding/uri.cpp b/coding/uri.cpp index 57242042ac..f3b9d9f577 100644 --- a/coding/uri.cpp +++ b/coding/uri.cpp @@ -47,7 +47,7 @@ bool Uri::Parse() return true; } -void Uri::ForEachKeyValue(CallbackT const & callback) const +bool Uri::ForEachKeyValue(CallbackT const & callback) const { // parse query for keys and values size_t const count = m_url.size(); @@ -71,11 +71,13 @@ void Uri::ForEachKeyValue(CallbackT const & callback) const else key = UrlDecode(m_url.substr(start, end - start)); - callback(key, value); + if (!callback(key, value)) + return false; } start = end + 1; } + return true; } } diff --git a/coding/uri.hpp b/coding/uri.hpp index e1ee611000..f0ea0a7824 100644 --- a/coding/uri.hpp +++ b/coding/uri.hpp @@ -13,7 +13,7 @@ namespace url_scheme class Uri { public: - typedef function CallbackT; + typedef function CallbackT; explicit Uri(string const & uri) : m_url(uri) { Init(); } Uri(char const * uri, size_t size) : m_url(uri, uri + size) { Init(); } @@ -21,8 +21,7 @@ public: string const & GetScheme() const { return m_scheme; } string const & GetPath() const { return m_path; } bool IsValid() const { return !m_scheme.empty(); } - - void ForEachKeyValue(CallbackT const & callback) const; + bool ForEachKeyValue(CallbackT const & callback) const; private: void Init(); diff --git a/map/framework.cpp b/map/framework.cpp index 35b720d862..9fe8f45303 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1749,31 +1749,16 @@ bool Framework::ShowMapForURL(string const & url) result = NEED_CLICK; } } - else if (StartsWith(url, "mapswithme://") || StartsWith(url, "mwm://")) + else if (StartsWith(url, "mapswithme://") || StartsWith(url, "mwm://") || + StartsWith(url, "mapsme://")) { - // clear every current API-mark. - { - UserMarkControllerGuard guard(m_bmManager, UserMarkType::API_MARK); - guard.m_controller.Clear(); - guard.m_controller.SetIsVisible(true); - guard.m_controller.SetIsDrawable(true); - } + if (!m_ParsedMapApi.GetViewportRect(rect)) + rect = df::GetWorldRect(); - if (m_ParsedMapApi.SetUriAndParse(url)) - { - if (!m_ParsedMapApi.GetViewportRect(rect)) - rect = df::GetWorldRect(); - - if ((apiMark = m_ParsedMapApi.GetSinglePoint())) - result = NEED_CLICK; - else - result = NO_NEED_CLICK; - } + if ((apiMark = m_ParsedMapApi.GetSinglePoint())) + result = NEED_CLICK; else - { - UserMarkControllerGuard guard(m_bmManager, UserMarkType::API_MARK); - guard.m_controller.SetIsVisible(false); - } + result = NO_NEED_CLICK; } else // Actually, we can parse any geo url scheme with correct coordinates. { @@ -1819,6 +1804,39 @@ bool Framework::ShowMapForURL(string const & url) return false; } +url_scheme::ParsingResult Framework::ParseApiURL(string const & url) +{ + using namespace url_scheme; + using namespace strings; + + // clear every current API-mark. + { + UserMarkControllerGuard guard(m_bmManager, UserMarkType::API_MARK); + guard.m_controller.Clear(); + guard.m_controller.SetIsVisible(true); + guard.m_controller.SetIsDrawable(true); + } + + if (!StartsWith(url, "mapswithme://") && !StartsWith(url, "mwm://") && + !StartsWith(url, "mapsme://")) + return ParsingResult::Incorrect; + + auto const resultType = m_ParsedMapApi.SetUriAndParse(url); + + if (resultType == ParsingResult::Incorrect) + { + LOG(LWARNING, ("Incorrect api url", url)); + UserMarkControllerGuard guard(m_bmManager, UserMarkType::API_MARK); + guard.m_controller.SetIsVisible(false); + } + return resultType; +} + +Framework::TParsedRoutingPointAndType Framework::GetParsedRoutingData() const +{ + return {m_ParsedMapApi.GetRoutePoints(), routing::FromString(m_ParsedMapApi.GetRoutingType())}; +} + void Framework::ForEachFeatureAtPoint(TFeatureTypeFn && fn, m2::PointD const & mercator, double featureDistanceToleranceInMeters) const { @@ -2240,7 +2258,6 @@ void Framework::BuildRoute(m2::PointD const & start, m2::PointD const & finish, if (IsRoutingActive()) CloseRouting(); - SetLastUsedRouter(m_currentRouterType); m_routingSession.SetUserCurrentPosition(start); auto readyCallback = [this] (Route const & route, IRouter::ResultCode code) @@ -2310,6 +2327,8 @@ void Framework::SetRouter(RouterType type) if (m_currentRouterType == type) return; + + SetLastUsedRouter(type); SetRouterImpl(type); } diff --git a/map/framework.hpp b/map/framework.hpp index 1c807b6e10..ebd0e43306 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -503,6 +503,10 @@ public: /// Set correct viewport, parse API, show balloon. bool ShowMapForURL(string const & url); + url_scheme::ParsingResult ParseApiURL(string const & url); + + using TParsedRoutingPointAndType = pair, routing::RouterType>; + TParsedRoutingPointAndType GetParsedRoutingData() const; private: // TODO(vng): Uncomment when needed. diff --git a/map/geourl_process.cpp b/map/geourl_process.cpp index a254758ef4..9cb16670d8 100644 --- a/map/geourl_process.cpp +++ b/map/geourl_process.cpp @@ -123,20 +123,19 @@ namespace url_scheme { return (m_latPriority == m_lonPriority && m_latPriority != -1); } - - void operator()(string const & key, string const & value) + bool operator()(string const & key, string const & value) { if (key == "z" || key == "zoom") { double x; if (strings::to_double(value, x)) m_info.SetZoom(x); - return; + return true; } int const priority = GetCoordinatesPriority(key); if (priority == -1 || priority < m_latPriority || priority < m_lonPriority) - return; + return false; if (priority != LL_PRIORITY) { @@ -160,6 +159,7 @@ namespace url_scheme } } } + return true; } }; diff --git a/map/mwm_url.cpp b/map/mwm_url.cpp index 1c213088d4..89794d56d9 100644 --- a/map/mwm_url.cpp +++ b/map/mwm_url.cpp @@ -22,9 +22,53 @@ namespace url_scheme namespace { +string const kLatLon = "ll"; +string const kSourceLatLon = "sll"; +string const kDestLatLon = "dll"; +string const kZoomLevel = "z"; +string const kName = "n"; +string const kSourceName = "saddr"; +string const kDestName = "daddr"; +string const kId = "id"; +string const kStyle = "s"; +string const kBackUrl = "backurl"; +string const kVersion = "v"; +string const kAppName = "appname"; +string const kBalloonAction = "balloonaction"; +string const kRouteType = "type"; static int const INVALID_LAT_VALUE = -1000; +bool ParseLatLon(double & lat, double & lon, string const & key, string const & value) +{ + size_t const firstComma = value.find(','); + if (firstComma == string::npos) + { + LOG(LWARNING, ("Map API: no comma between lat and lon for 'll' key", key, value)); + return false; + } + + if (value.find(',', firstComma + 1) != string::npos) + { + LOG(LWARNING, ("Map API: more than one comma in a value for 'll' key", key, value)); + return false; + } + + if (!strings::to_double(value.substr(0, firstComma), lat) || + !strings::to_double(value.substr(firstComma + 1), lon)) + { + LOG(LWARNING, ("Map API: can't parse lat,lon for 'll' key", key, value)); + return false; + } + + if (!MercatorBounds::ValidLat(lat) || !MercatorBounds::ValidLon(lon)) + { + LOG(LWARNING, ("Map API: incorrect value for lat and/or lon", key, value, lat, lon)); + return false; + } + return true; +} + bool IsInvalidApiPoint(ApiPoint const & p) { return p.m_lat == INVALID_LAT_VALUE; } } // unnames namespace @@ -41,113 +85,162 @@ void ParsedMapApi::SetBookmarkManager(BookmarkManager * manager) { m_bmManager = manager; } - -bool ParsedMapApi::SetUriAndParse(string const & url) +ParsingResult ParsedMapApi::SetUriAndParse(string const & url) { Reset(); - return Parse(url_scheme::Uri(url)); + ParsingResult const res = Parse(url_scheme::Uri(url)); + m_isValid = res != ParsingResult::Incorrect; + return res; } -bool ParsedMapApi::IsValid() const -{ - ASSERT(m_bmManager != nullptr, ()); - UserMarkControllerGuard guard(*m_bmManager, UserMarkType::API_MARK); - return guard.m_controller.GetUserMarkCount() > 0; -} - -bool ParsedMapApi::Parse(Uri const & uri) +ParsingResult ParsedMapApi::Parse(Uri const & uri) { string const & scheme = uri.GetScheme(); - if ((scheme != "mapswithme" && scheme != "mwm") || uri.GetPath() != "map") + string const & path = uri.GetPath(); + bool const isRoutePath = path == "route"; + if ((scheme != "mapswithme" && scheme != "mwm" && scheme != "mapsme") || + (path != "map" && !isRoutePath)) + return ParsingResult::Incorrect; + + if (isRoutePath) + { + vector pattern{kSourceLatLon, kSourceName, kDestLatLon, kDestName, kRouteType}; + if (!uri.ForEachKeyValue(bind(&ParsedMapApi::RouteKeyValue, this, _1, _2, ref(pattern)))) + return ParsingResult::Incorrect; + + if (pattern.size() != 0) + return ParsingResult::Incorrect; + + if (m_routePoints.size() != 2) + { + ASSERT(false, ()); + return ParsingResult::Incorrect; + } + + return ParsingResult::Route; + } + else + { + ASSERT(m_bmManager != nullptr, ()); + UserMarkControllerGuard guard(*m_bmManager, UserMarkType::API_MARK); + vector points; + if (!uri.ForEachKeyValue(bind(&ParsedMapApi::AddKeyValue, this, _1, _2, ref(points)))) + return ParsingResult::Incorrect; + + points.erase(remove_if(points.begin(), points.end(), &IsInvalidApiPoint), points.end()); + if ((isRoutePath && (points.size() < 2 || m_routingType.empty())) || points.empty()) + return ParsingResult::Incorrect; + + for (auto const & p : points) + { + m2::PointD glPoint(MercatorBounds::FromLatLon(p.m_lat, p.m_lon)); + ApiMarkPoint * mark = static_cast(guard.m_controller.CreateUserMark(glPoint)); + mark->SetName(p.m_name); + mark->SetID(p.m_id); + mark->SetStyle(style::GetSupportedStyle(p.m_style, p.m_name, "")); + } + + return ParsingResult::Map; + } +} + +bool ParsedMapApi::RouteKeyValue(string key, string const & value, vector & pattern) +{ + if (key != pattern.front()) return false; - ASSERT(m_bmManager != nullptr, ()); - UserMarkControllerGuard guard(*m_bmManager, UserMarkType::API_MARK); - - vector points; - uri.ForEachKeyValue(bind(&ParsedMapApi::AddKeyValue, this, _1, _2, ref(points))); - points.erase(remove_if(points.begin(), points.end(), &IsInvalidApiPoint), points.end()); - - for (size_t i = 0; i < points.size(); ++i) + if (key == kSourceLatLon || key == kDestLatLon) { - ApiPoint const & p = points[i]; - m2::PointD glPoint(MercatorBounds::FromLatLon(p.m_lat, p.m_lon)); - ApiMarkPoint * mark = static_cast(guard.m_controller.CreateUserMark(glPoint)); - mark->SetName(p.m_name); - mark->SetID(p.m_id); - mark->SetStyle(style::GetSupportedStyle(p.m_style, p.m_name, "")); + double lat = 0.0; + double lon = 0.0; + if (!ParseLatLon(lat, lon, key, value)) + return false; + + RoutePoint p; + p.m_org = MercatorBounds::FromLatLon(lat, lon); + m_routePoints.push_back(p); + } + else if (key == kSourceName || key == kDestName) + { + m_routePoints.back().m_name = value; + } + else if (key == kRouteType) + { + string const lowerValue = strings::MakeLowerCase(value); + if (lowerValue == "pedestrian" || lowerValue == "vehicle" || lowerValue == "bicycle") + { + m_routingType = lowerValue; + } + else + { + LOG(LWARNING, ("Incorrect routing type:", value)); + return false; + } } + pattern.erase(pattern.begin()); return true; } -void ParsedMapApi::AddKeyValue(string key, string const & value, vector & points) +bool ParsedMapApi::AddKeyValue(string key, string const & value, vector & points) { strings::AsciiToLower(key); - if (key == "ll") + if (key == kLatLon) { points.push_back(ApiPoint()); points.back().m_lat = INVALID_LAT_VALUE; - size_t const firstComma = value.find(','); - if (firstComma == string::npos) - { - LOG(LWARNING, ("Map API: no comma between lat and lon for 'll' key", key, value)); - return; - } - - if (value.find(',', firstComma + 1) != string::npos) - { - LOG(LWARNING, ("Map API: more than one comma in a value for 'll' key", key, value)); - return; - } - double lat = 0.0; double lon = 0.0; - if (!strings::to_double(value.substr(0, firstComma), lat) || - !strings::to_double(value.substr(firstComma + 1), lon)) - { - LOG(LWARNING, ("Map API: can't parse lat,lon for 'll' key", key, value)); - return; - } - - if (!MercatorBounds::ValidLat(lat) || !MercatorBounds::ValidLon(lon)) - { - LOG(LWARNING, ("Map API: incorrect value for lat and/or lon", key, value, lat, lon)); - return; - } + if (!ParseLatLon(lat, lon, key, value)) + return false; points.back().m_lat = lat; points.back().m_lon = lon; } - else if (key == "z") + else if (key == kZoomLevel) { if (!strings::to_double(value, m_zoomLevel)) m_zoomLevel = 0.0; } - else if (key == "n") + else if (key == kName) { if (!points.empty()) + { points.back().m_name = value; + } else + { LOG(LWARNING, ("Map API: Point name with no point. 'll' should come first!")); + return false; + } } - else if (key == "id") + else if (key == kId) { if (!points.empty()) + { points.back().m_id = value; + } else + { LOG(LWARNING, ("Map API: Point url with no point. 'll' should come first!")); + return false; + } } - else if (key == "s") + else if (key == kStyle) { if (!points.empty()) + { points.back().m_style = value; + } else + { LOG(LWARNING, ("Map API: Point style with no point. 'll' should come first!")); + return false; + } } - else if (key == "backurl") + else if (key == kBackUrl) { // Fix missing :// in back url, it's important for iOS if (value.find("://") == string::npos) @@ -155,19 +248,20 @@ void ParsedMapApi::AddKeyValue(string key, string const & value, vector GetRoutePoints() const { return m_routePoints; } + string GetRoutingType() const { return m_routingType; } private: - bool Parse(Uri const & uri); - void AddKeyValue(string key, string const & value, vector & points); + ParsingResult Parse(Uri const & uri); + bool AddKeyValue(string key, string const & value, vector & points); + bool RouteKeyValue(string key, string const & value, vector & pattern); BookmarkManager * m_bmManager; + vector m_routePoints; string m_globalBackUrl; string m_appTitle; + string m_routingType; int m_version; /// Zoom level in OSM format (e.g. from 1.0 to 20.0) /// Taken into an account when calculating viewport rect, but only if points count is == 1 double m_zoomLevel; - bool m_goBackOnBalloonClick; + bool m_goBackOnBalloonClick = false; + bool m_isValid = false; }; } diff --git a/routing/router.cpp b/routing/router.cpp index 2cfd491360..ffd69ad642 100644 --- a/routing/router.cpp +++ b/routing/router.cpp @@ -7,12 +7,25 @@ string ToString(RouterType type) { switch(type) { - case RouterType::Vehicle: return "Vehicle"; - case RouterType::Pedestrian: return "Pedestrian"; - case RouterType::Bicycle: return "Bicycle"; + case RouterType::Vehicle: return "Vehicle"; + case RouterType::Pedestrian: return "Pedestrian"; + case RouterType::Bicycle: return "Bicycle"; } ASSERT(false, ()); return "Error"; } +RouterType FromString(string const & str) +{ + if (str == "vehicle") + return RouterType::Vehicle; + if (str == "pedestrian") + return RouterType::Pedestrian; + if (str == "bicycle") + return RouterType::Bicycle; + + ASSERT(false, ("Incorrect routing string:", str)); + return RouterType::Vehicle; +} + } // namespace routing diff --git a/routing/router.hpp b/routing/router.hpp index 667521b432..ac91916414 100644 --- a/routing/router.hpp +++ b/routing/router.hpp @@ -25,6 +25,7 @@ enum class RouterType }; string ToString(RouterType type); +RouterType FromString(string const & str); class IRouter { From 3816b2a9e1c8374073bea46e133c0ed334923a6e Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Wed, 27 Jul 2016 12:03:27 +0300 Subject: [PATCH 2/8] Tests. --- map/map_tests/mwm_url_tests.cpp | 58 ++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/map/map_tests/mwm_url_tests.cpp b/map/map_tests/mwm_url_tests.cpp index 006aa22871..4b42ecf1dc 100644 --- a/map/map_tests/mwm_url_tests.cpp +++ b/map/map_tests/mwm_url_tests.cpp @@ -31,7 +31,8 @@ namespace m_m = &m_fm.GetBookmarkManager(); m_api.SetBookmarkManager(m_m); - if (m_api.SetUriAndParse(uriString)) + ParsingResult const res = m_api.SetUriAndParse(uriString); + if (res != ParsingResult::Incorrect) { if (!m_api.GetViewportRect(m_viewportRect)) m_viewportRect = df::GetWorldRect(); @@ -44,6 +45,7 @@ namespace string const & GetAppTitle() const { return m_api.GetAppTitle(); } bool GoBackOnBalloonClick() const { return m_api.GoBackOnBalloonClick(); } int GetPointCount() const { return UserMarkControllerGuard(*m_m, type).m_controller.GetUserMarkCount(); } + vector GetRoutePoints() const { return m_api.GetRoutePoints(); } string const & GetGlobalBackUrl() const { return m_api.GetGlobalBackUrl(); } int GetApiVersion() const { return m_api.GetApiVersion(); } bool TestLatLon(int index, double lat, double lon) const @@ -52,6 +54,12 @@ namespace return my::AlmostEqualULPs(ll.lat, lat) && my::AlmostEqualULPs(ll.lon, lon); } + bool TestRoutePoint(int index, double lat, double lon, string const & name) + { + RoutePoint const pt = GetRoutePoints()[index]; + return pt.m_org == MercatorBounds::FromLatLon(lat, lon) && pt.m_name == name; + } + bool TestName(int index, string const & name) const { return GetMark(index)->GetName() == name; @@ -61,7 +69,7 @@ namespace { return GetMark(index)->GetID() == id; } - + bool TestRouteType(string const & type) const { return m_api.GetRoutingType() == type; } private: ApiMarkPoint const * GetMark(int index) const { @@ -80,12 +88,11 @@ namespace bool IsValid(Framework & fm, string const & uriString) { ParsedMapApi api; - bool isValid = false; + bool isValid; + api.SetBookmarkManager(&fm.GetBookmarkManager()); + api.SetUriAndParse(uriString); + isValid = api.IsValid(); { - api.SetBookmarkManager(&fm.GetBookmarkManager()); - if (api.SetUriAndParse(uriString)) - isValid = api.IsValid(); - UserMarkControllerGuard guard(fm.GetBookmarkManager(), UserMarkType::API_MARK); guard.m_controller.Clear(); } @@ -109,6 +116,19 @@ UNIT_TEST(MapApiSmoke) TEST_EQUAL(test.GetGlobalBackUrl(), "", ()); } +UNIT_TEST(RouteApiSmoke) +{ + string const uriString = + "mapswithme://route?sll=1,1&saddr=name0&dll=2,2&daddr=name1&type=vehicle"; + TEST(Uri(uriString).IsValid(), ()); + + ApiTest test(uriString); + TEST(test.IsValid(), ()); + TEST(test.TestRoutePoint(0, 1, 1, "name0"), ()); + TEST(test.TestRoutePoint(1, 2, 2, "name1"), ()); + TEST(test.TestRouteType("vehicle"), ()); +} + UNIT_TEST(MapApiInvalidUrl) { Framework fm; @@ -119,6 +139,26 @@ UNIT_TEST(MapApiInvalidUrl) TEST(!IsValid(fm, "mapswithme://map?ll=1,2,3"), ("Too many values for ll")); } +UNIT_TEST(RouteApiInvalidUrl) +{ + Framework fm; + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&saddr=name0&dll=2,2&daddr=name2"), + ("Route type doesn't exist")); + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&saddr=name0"), ("Destination doesn't exist")); + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&dll=2,2&type=vehicle"), + ("Source or destination name doesn't exist")); + TEST(!IsValid(fm, "mapswithme://route?saddr=name0&daddr=name1&type=vehicle"), ()); + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&sll=2.2&type=vehicle"), ()); + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&dll=2.2&type=666"), ()); + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&saddr=name0&sll=2,2&saddr=name1&type=vehicle"), ()); + TEST(!IsValid(fm, "mapswithme://route?sll=1,1&type=vehicle"), ()); + TEST(!IsValid(fm, + "mapswithme://" + "route?sll=1,1&saddr=name0&sll=2,2&saddr=name1&sll=1,1&saddr=name0&type=vehicle"), + ()); + TEST(!IsValid(fm, "mapswithme://route?type=vehicle"), ()); +} + UNIT_TEST(MapApiLatLonLimits) { Framework fm; @@ -131,7 +171,7 @@ UNIT_TEST(MapApiLatLonLimits) UNIT_TEST(MapApiPointNameBeforeLatLon) { ApiTest test("mapswithme://map?n=Name&ll=1,2"); - TEST(test.IsValid(), ()); + TEST(!test.IsValid(), ()); TEST_EQUAL(test.GetPointCount(), 1, ()); TEST(test.TestName(0, ""), ()); } @@ -160,7 +200,7 @@ UNIT_TEST(MapApiMultiplePoints) UNIT_TEST(MapApiInvalidPointLatLonButValidOtherParts) { ApiTest api("mapswithme://map?ll=1,1,1&n=A&ll=2,2&n=B&ll=3,3,3&n=C"); - TEST(api.IsValid(), ()); + TEST(!api.IsValid(), ()); TEST_EQUAL(api.GetPointCount(), 1, ()); TEST(api.TestLatLon(0, 2, 2), ()); TEST(api.TestName(0, "B"), ()); From 747b385db98472f1f9cd1323fb9f2fd5d8a68000 Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Wed, 27 Jul 2016 12:04:03 +0300 Subject: [PATCH 3/8] [xcode] Added tests to xcode project. --- xcode/map/map.xcodeproj/project.pbxproj | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index 04cab246ba..94ecc3ac72 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -100,6 +100,14 @@ F6B283081C1B03320081957A /* gps_track_storage.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6B283001C1B03320081957A /* gps_track_storage.hpp */; }; F6B283091C1B03320081957A /* gps_track.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6B283011C1B03320081957A /* gps_track.cpp */; }; F6B2830A1C1B03320081957A /* gps_track.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6B283021C1B03320081957A /* gps_track.hpp */; }; + F6DA2A951CCE24DA00F087B5 /* libdrape.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674A29F61B26FE62001A525C /* libdrape.a */; }; + F6DA2A971CCE24DB00F087B5 /* libdrape_frontend.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A961CCE24DB00F087B5 /* libdrape_frontend.a */; }; + F6DA2A9E1CCE252600F087B5 /* libeditor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A981CCE252600F087B5 /* libeditor.a */; }; + F6DA2A9F1CCE252600F087B5 /* liboauthcpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A991CCE252600F087B5 /* liboauthcpp.a */; }; + F6DA2AA01CCE252600F087B5 /* libplatform_tests_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A9A1CCE252600F087B5 /* libplatform_tests_support.a */; }; + F6DA2AA11CCE252600F087B5 /* libpugixml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A9B1CCE252600F087B5 /* libpugixml.a */; }; + F6DA2AA21CCE252600F087B5 /* libsdf_image.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A9C1CCE252600F087B5 /* libsdf_image.a */; }; + F6DA2AA31CCE252600F087B5 /* libstb_image.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DA2A9D1CCE252600F087B5 /* libstb_image.a */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -214,6 +222,13 @@ F6B283001C1B03320081957A /* gps_track_storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gps_track_storage.hpp; sourceTree = ""; }; F6B283011C1B03320081957A /* gps_track.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_track.cpp; sourceTree = ""; }; F6B283021C1B03320081957A /* gps_track.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gps_track.hpp; sourceTree = ""; }; + F6DA2A961CCE24DB00F087B5 /* libdrape_frontend.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdrape_frontend.a; path = "../../../omim-xcode-build/Debug/libdrape_frontend.a"; sourceTree = ""; }; + F6DA2A981CCE252600F087B5 /* libeditor.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libeditor.a; path = "../../../omim-xcode-build/Debug/libeditor.a"; sourceTree = ""; }; + F6DA2A991CCE252600F087B5 /* liboauthcpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liboauthcpp.a; path = "../../../omim-xcode-build/Debug/liboauthcpp.a"; sourceTree = ""; }; + F6DA2A9A1CCE252600F087B5 /* libplatform_tests_support.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform_tests_support.a; path = "/Users/v.mikhaylenko/mapsme/omim/xcode/platform/../../../omim-xcode-build/Debug/libplatform_tests_support.a"; sourceTree = ""; }; + F6DA2A9B1CCE252600F087B5 /* libpugixml.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpugixml.a; path = "../../../omim-xcode-build/Debug/libpugixml.a"; sourceTree = ""; }; + F6DA2A9C1CCE252600F087B5 /* libsdf_image.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsdf_image.a; path = "../../../omim-xcode-build/Debug/libsdf_image.a"; sourceTree = ""; }; + F6DA2A9D1CCE252600F087B5 /* libstb_image.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libstb_image.a; path = "../../../omim-xcode-build/Debug/libstb_image.a"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -347,6 +362,13 @@ 675345B21A4054AD00A0A8C3 = { isa = PBXGroup; children = ( + F6DA2A981CCE252600F087B5 /* libeditor.a */, + F6DA2A991CCE252600F087B5 /* liboauthcpp.a */, + F6DA2A9A1CCE252600F087B5 /* libplatform_tests_support.a */, + F6DA2A9B1CCE252600F087B5 /* libpugixml.a */, + F6DA2A9C1CCE252600F087B5 /* libsdf_image.a */, + F6DA2A9D1CCE252600F087B5 /* libstb_image.a */, + F6DA2A961CCE24DB00F087B5 /* libdrape_frontend.a */, 670D05A41B0DF4250013A7AC /* defaults.xcconfig */, 674A2A341B2700B2001A525C /* libs */, 675345BD1A4054AD00A0A8C3 /* map */, From bc5a666c97ee869411ac6b54f36f0ac04ee9d477 Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Wed, 27 Jul 2016 12:06:28 +0300 Subject: [PATCH 4/8] [ios] Support routing api. --- iphone/Maps/Classes/MapsAppDelegate.mm | 28 +++++++++++++++++++++--- iphone/Maps/Classes/Routing/MWMRouter.h | 3 +++ iphone/Maps/Classes/Routing/MWMRouter.mm | 9 ++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index 3c14c2ffa2..597f83a01d 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -254,11 +254,32 @@ using namespace osm_auth_ios; } else if (m_mwmURL) { - if (f.ShowMapForURL([m_mwmURL UTF8String])) + string const url = m_mwmURL.UTF8String; + auto const parsingType = f.ParseApiURL(url); + switch (parsingType) { - [[Statistics instance] logApiUsage:m_sourceApplication]; + case url_scheme::ParsingResult::Incorrect: + LOG(LWARNING, ("Incorrect parsing result for url:", url)); + break; + case url_scheme::ParsingResult::Route: + { + auto const parsedData = f.GetParsedRoutingData(); + f.SetRouter(parsedData.second); + auto const points = parsedData.first; + auto const & p1 = points[0]; + auto const & p2 = points[1]; + [[MWMRouter router] buildFromPoint:MWMRoutePoint(p1.m_org, @(p1.m_name.c_str())) + toPoint:MWMRoutePoint(p2.m_org, @(p2.m_name.c_str())) + bestRouter:YES]; [self showMap]; [self.mapViewController showAPIBar]; + break; + } + case url_scheme::ParsingResult::Map: + f.ShowMapForURL(url); + [self showMap]; + [self.mapViewController showAPIBar]; + break; } } else if (m_fileURL) @@ -802,7 +823,8 @@ using namespace osm_auth_ios; m_geoURL = [url absoluteString]; return YES; } - else if ([scheme isEqualToString:@"mapswithme"] || [scheme isEqualToString:@"mwm"]) + else if ([scheme isEqualToString:@"mapswithme"] || [scheme isEqualToString:@"mwm"] || + [scheme isEqualToString:@"mapsme"]) { m_mwmURL = [url absoluteString]; return YES; diff --git a/iphone/Maps/Classes/Routing/MWMRouter.h b/iphone/Maps/Classes/Routing/MWMRouter.h index de691c2d3b..ca4fb95387 100644 --- a/iphone/Maps/Classes/Routing/MWMRouter.h +++ b/iphone/Maps/Classes/Routing/MWMRouter.h @@ -13,6 +13,9 @@ - (void)swapPointsAndRebuild; - (void)buildFromPoint:(MWMRoutePoint const &)start bestRouter:(BOOL)bestRouter; - (void)buildToPoint:(MWMRoutePoint const &)finish bestRouter:(BOOL)bestRouter; +- (void)buildFromPoint:(MWMRoutePoint const &)start + toPoint:(MWMRoutePoint const &)finish + bestRouter:(BOOL)bestRouter; - (void)rebuildWithBestRouter:(BOOL)bestRouter; - (void)start; - (void)stop; diff --git a/iphone/Maps/Classes/Routing/MWMRouter.mm b/iphone/Maps/Classes/Routing/MWMRouter.mm index c2832e092e..478ab1c212 100644 --- a/iphone/Maps/Classes/Routing/MWMRouter.mm +++ b/iphone/Maps/Classes/Routing/MWMRouter.mm @@ -116,6 +116,15 @@ bool isMarkerPoint(MWMRoutePoint const & point) { return point.IsValid() && !poi [self rebuildWithBestRouter:bestRouter]; } +- (void)buildFromPoint:(MWMRoutePoint const &)start + toPoint:(MWMRoutePoint const &)finish + bestRouter:(BOOL)bestRouter +{ + self.startPoint = start; + self.finishPoint = finish; + [self rebuildWithBestRouter:bestRouter]; +} + - (void)rebuildWithBestRouter:(BOOL)bestRouter { if (self.startPoint.IsMyPosition()) From 3ee8d0d2f8913bfcfe5e0d1f38664486a168a0fd Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Wed, 27 Jul 2016 13:12:26 +0300 Subject: [PATCH 5/8] Review fixes. --- coding/uri.cpp | 2 +- coding/uri.hpp | 4 +- iphone/Maps/Classes/MapsAppDelegate.mm | 22 +++--- map/framework.cpp | 33 +++------ map/framework.hpp | 15 +++- map/geourl_process.cpp | 11 +-- map/map_tests/mwm_url_tests.cpp | 39 +++++----- map/mwm_url.cpp | 98 +++++++++++--------------- map/mwm_url.hpp | 27 ++++--- 9 files changed, 118 insertions(+), 133 deletions(-) diff --git a/coding/uri.cpp b/coding/uri.cpp index f3b9d9f577..4b98a96839 100644 --- a/coding/uri.cpp +++ b/coding/uri.cpp @@ -47,7 +47,7 @@ bool Uri::Parse() return true; } -bool Uri::ForEachKeyValue(CallbackT const & callback) const +bool Uri::ForEachKeyValue(TCallback const & callback) const { // parse query for keys and values size_t const count = m_url.size(); diff --git a/coding/uri.hpp b/coding/uri.hpp index f0ea0a7824..8d2bae7d20 100644 --- a/coding/uri.hpp +++ b/coding/uri.hpp @@ -13,7 +13,7 @@ namespace url_scheme class Uri { public: - typedef function CallbackT; + using TCallback = function; explicit Uri(string const & uri) : m_url(uri) { Init(); } Uri(char const * uri, size_t size) : m_url(uri, uri + size) { Init(); } @@ -21,7 +21,7 @@ public: string const & GetScheme() const { return m_scheme; } string const & GetPath() const { return m_path; } bool IsValid() const { return !m_scheme.empty(); } - bool ForEachKeyValue(CallbackT const & callback) const; + bool ForEachKeyValue(TCallback const & callback) const; private: void Init(); diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index 597f83a01d..b9d1894514 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -254,18 +254,20 @@ using namespace osm_auth_ios; } else if (m_mwmURL) { + using namespace url_scheme; + string const url = m_mwmURL.UTF8String; - auto const parsingType = f.ParseApiURL(url); + auto const parsingType = f.ParseAndSetApiURL(url); switch (parsingType) { - case url_scheme::ParsingResult::Incorrect: + case ParsedMapApi::ParsingResult::Incorrect: LOG(LWARNING, ("Incorrect parsing result for url:", url)); break; - case url_scheme::ParsingResult::Route: + case ParsedMapApi::ParsingResult::Route: { auto const parsedData = f.GetParsedRoutingData(); - f.SetRouter(parsedData.second); - auto const points = parsedData.first; + f.SetRouter(parsedData.m_type); + auto const points = parsedData.m_points; auto const & p1 = points[0]; auto const & p2 = points[1]; [[MWMRouter router] buildFromPoint:MWMRoutePoint(p1.m_org, @(p1.m_name.c_str())) @@ -275,10 +277,12 @@ using namespace osm_auth_ios; [self.mapViewController showAPIBar]; break; } - case url_scheme::ParsingResult::Map: - f.ShowMapForURL(url); - [self showMap]; - [self.mapViewController showAPIBar]; + case ParsedMapApi::ParsingResult::Map: + if (f.ShowMapForURL(url)) + { + [self showMap]; + [self.mapViewController showAPIBar]; + } break; } } diff --git a/map/framework.cpp b/map/framework.cpp index 9fe8f45303..8b1612f5ee 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1749,16 +1749,13 @@ bool Framework::ShowMapForURL(string const & url) result = NEED_CLICK; } } - else if (StartsWith(url, "mapswithme://") || StartsWith(url, "mwm://") || - StartsWith(url, "mapsme://")) + else if (m_ParsedMapApi.IsValid()) { if (!m_ParsedMapApi.GetViewportRect(rect)) rect = df::GetWorldRect(); - if ((apiMark = m_ParsedMapApi.GetSinglePoint())) - result = NEED_CLICK; - else - result = NO_NEED_CLICK; + apiMark = m_ParsedMapApi.GetSinglePoint(); + result = apiMark ? NEED_CLICK : NO_NEED_CLICK; } else // Actually, we can parse any geo url scheme with correct coordinates. { @@ -1804,12 +1801,11 @@ bool Framework::ShowMapForURL(string const & url) return false; } -url_scheme::ParsingResult Framework::ParseApiURL(string const & url) +url_scheme::ParsedMapApi::ParsingResult Framework::ParseAndSetApiURL(string const & url) { using namespace url_scheme; - using namespace strings; - // clear every current API-mark. + // Clear every current API-mark. { UserMarkControllerGuard guard(m_bmManager, UserMarkType::API_MARK); guard.m_controller.Clear(); @@ -1817,24 +1813,13 @@ url_scheme::ParsingResult Framework::ParseApiURL(string const & url) guard.m_controller.SetIsDrawable(true); } - if (!StartsWith(url, "mapswithme://") && !StartsWith(url, "mwm://") && - !StartsWith(url, "mapsme://")) - return ParsingResult::Incorrect; - - auto const resultType = m_ParsedMapApi.SetUriAndParse(url); - - if (resultType == ParsingResult::Incorrect) - { - LOG(LWARNING, ("Incorrect api url", url)); - UserMarkControllerGuard guard(m_bmManager, UserMarkType::API_MARK); - guard.m_controller.SetIsVisible(false); - } - return resultType; + return m_ParsedMapApi.SetUriAndParse(url); } -Framework::TParsedRoutingPointAndType Framework::GetParsedRoutingData() const +Framework::ParsedRoutingData Framework::GetParsedRoutingData() const { - return {m_ParsedMapApi.GetRoutePoints(), routing::FromString(m_ParsedMapApi.GetRoutingType())}; + return Framework::ParsedRoutingData(m_ParsedMapApi.GetRoutePoints(), + routing::FromString(m_ParsedMapApi.GetRoutingType())); } void Framework::ForEachFeatureAtPoint(TFeatureTypeFn && fn, m2::PointD const & mercator, diff --git a/map/framework.hpp b/map/framework.hpp index ebd0e43306..ab884b26ea 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -503,10 +503,19 @@ public: /// Set correct viewport, parse API, show balloon. bool ShowMapForURL(string const & url); - url_scheme::ParsingResult ParseApiURL(string const & url); + url_scheme::ParsedMapApi::ParsingResult ParseAndSetApiURL(string const & url); - using TParsedRoutingPointAndType = pair, routing::RouterType>; - TParsedRoutingPointAndType GetParsedRoutingData() const; + struct ParsedRoutingData + { + ParsedRoutingData(vector const & points, routing::RouterType type) + : m_points(points), m_type(type) + { + } + vector m_points; + routing::RouterType m_type; + }; + + ParsedRoutingData GetParsedRoutingData() const; private: // TODO(vng): Uncomment when needed. diff --git a/map/geourl_process.cpp b/map/geourl_process.cpp index 9cb16670d8..ef9721c70b 100644 --- a/map/geourl_process.cpp +++ b/map/geourl_process.cpp @@ -123,6 +123,7 @@ namespace url_scheme { return (m_latPriority == m_lonPriority && m_latPriority != -1); } + bool operator()(string const & key, string const & value) { if (key == "z" || key == "zoom") @@ -148,14 +149,16 @@ namespace url_scheme { if (key == "lat") { - if (m_info.SetLat(x)) - m_latPriority = priority; + if (!m_info.SetLat(x)) + return false; + m_latPriority = priority; } else { ASSERT_EQUAL(key, "lon", ()); - if (m_info.SetLon(x)) - m_lonPriority = priority; + if (!m_info.SetLon(x)) + return false; + m_lonPriority = priority; } } } diff --git a/map/map_tests/mwm_url_tests.cpp b/map/map_tests/mwm_url_tests.cpp index 4b42ecf1dc..0d1261bd47 100644 --- a/map/map_tests/mwm_url_tests.cpp +++ b/map/map_tests/mwm_url_tests.cpp @@ -31,8 +31,8 @@ namespace m_m = &m_fm.GetBookmarkManager(); m_api.SetBookmarkManager(m_m); - ParsingResult const res = m_api.SetUriAndParse(uriString); - if (res != ParsingResult::Incorrect) + auto const res = m_api.SetUriAndParse(uriString); + if (res != ParsedMapApi::ParsingResult::Incorrect) { if (!m_api.GetViewportRect(m_viewportRect)) m_viewportRect = df::GetWorldRect(); @@ -69,6 +69,7 @@ namespace { return GetMark(index)->GetID() == id; } + bool TestRouteType(string const & type) const { return m_api.GetRoutingType() == type; } private: ApiMarkPoint const * GetMark(int index) const @@ -88,16 +89,14 @@ namespace bool IsValid(Framework & fm, string const & uriString) { ParsedMapApi api; - bool isValid; api.SetBookmarkManager(&fm.GetBookmarkManager()); api.SetUriAndParse(uriString); - isValid = api.IsValid(); { UserMarkControllerGuard guard(fm.GetBookmarkManager(), UserMarkType::API_MARK); guard.m_controller.Clear(); } - return isValid; + return api.IsValid(); } } @@ -137,26 +136,27 @@ UNIT_TEST(MapApiInvalidUrl) TEST(!IsValid(fm, "mwm://"), ("No parameters")); TEST(!IsValid(fm, "mapswithme://map?"), ("No longtitude")); TEST(!IsValid(fm, "mapswithme://map?ll=1,2,3"), ("Too many values for ll")); + TEST(!IsValid(fm, "mapswithme://fffff://map?ll=1,2"), ()); } UNIT_TEST(RouteApiInvalidUrl) { - Framework fm; - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&saddr=name0&dll=2,2&daddr=name2"), + Framework f; + TEST(!IsValid(f, "mapswithme://route?sll=1,1&saddr=name0&dll=2,2&daddr=name2"), ("Route type doesn't exist")); - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&saddr=name0"), ("Destination doesn't exist")); - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&dll=2,2&type=vehicle"), + TEST(!IsValid(f, "mapswithme://route?sll=1,1&saddr=name0"), ("Destination doesn't exist")); + TEST(!IsValid(f, "mapswithme://route?sll=1,1&dll=2,2&type=vehicle"), ("Source or destination name doesn't exist")); - TEST(!IsValid(fm, "mapswithme://route?saddr=name0&daddr=name1&type=vehicle"), ()); - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&sll=2.2&type=vehicle"), ()); - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&dll=2.2&type=666"), ()); - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&saddr=name0&sll=2,2&saddr=name1&type=vehicle"), ()); - TEST(!IsValid(fm, "mapswithme://route?sll=1,1&type=vehicle"), ()); - TEST(!IsValid(fm, + TEST(!IsValid(f, "mapswithme://route?saddr=name0&daddr=name1&type=vehicle"), ()); + TEST(!IsValid(f, "mapswithme://route?sll=1,1&sll=2.2&type=vehicle"), ()); + TEST(!IsValid(f, "mapswithme://route?sll=1,1&dll=2.2&type=666"), ()); + TEST(!IsValid(f, "mapswithme://route?sll=1,1&saddr=name0&sll=2,2&saddr=name1&type=vehicle"), ()); + TEST(!IsValid(f, "mapswithme://route?sll=1,1&type=vehicle"), ()); + TEST(!IsValid(f, "mapswithme://" "route?sll=1,1&saddr=name0&sll=2,2&saddr=name1&sll=1,1&saddr=name0&type=vehicle"), ()); - TEST(!IsValid(fm, "mapswithme://route?type=vehicle"), ()); + TEST(!IsValid(f, "mapswithme://route?type=vehicle"), ()); } UNIT_TEST(MapApiLatLonLimits) @@ -172,8 +172,7 @@ UNIT_TEST(MapApiPointNameBeforeLatLon) { ApiTest test("mapswithme://map?n=Name&ll=1,2"); TEST(!test.IsValid(), ()); - TEST_EQUAL(test.GetPointCount(), 1, ()); - TEST(test.TestName(0, ""), ()); + TEST_EQUAL(test.GetPointCount(), 0, ()); } UNIT_TEST(MapApiPointNameOverwritten) @@ -201,9 +200,7 @@ UNIT_TEST(MapApiInvalidPointLatLonButValidOtherParts) { ApiTest api("mapswithme://map?ll=1,1,1&n=A&ll=2,2&n=B&ll=3,3,3&n=C"); TEST(!api.IsValid(), ()); - TEST_EQUAL(api.GetPointCount(), 1, ()); - TEST(api.TestLatLon(0, 2, 2), ()); - TEST(api.TestName(0, "B"), ()); + TEST_EQUAL(api.GetPointCount(), 0, ()); } UNIT_TEST(MapApiPointURLEncoded) diff --git a/map/mwm_url.cpp b/map/mwm_url.cpp index 89794d56d9..593f9a3ea8 100644 --- a/map/mwm_url.cpp +++ b/map/mwm_url.cpp @@ -19,7 +19,6 @@ namespace url_scheme { - namespace { string const kLatLon = "ll"; @@ -36,28 +35,23 @@ string const kVersion = "v"; string const kAppName = "appname"; string const kBalloonAction = "balloonaction"; string const kRouteType = "type"; +string const kRouteTypeVehicle = "vehicle"; +string const kRouteTypePedestrian = "pedestrian"; +string const kRouteTypeBicycle = "bicycle"; -static int const INVALID_LAT_VALUE = -1000; - -bool ParseLatLon(double & lat, double & lon, string const & key, string const & value) +bool ParseLatLon(string const & key, string const & value, double & lat, double & lon) { size_t const firstComma = value.find(','); if (firstComma == string::npos) { - LOG(LWARNING, ("Map API: no comma between lat and lon for 'll' key", key, value)); - return false; - } - - if (value.find(',', firstComma + 1) != string::npos) - { - LOG(LWARNING, ("Map API: more than one comma in a value for 'll' key", key, value)); + LOG(LWARNING, ("Map API: no comma between lat and lon for key:", key, " value:", value)); return false; } if (!strings::to_double(value.substr(0, firstComma), lat) || !strings::to_double(value.substr(firstComma + 1), lon)) { - LOG(LWARNING, ("Map API: can't parse lat,lon for 'll' key", key, value)); + LOG(LWARNING, ("Map API: can't parse lat,lon for key:", key, " value:", value)); return false; } @@ -69,41 +63,41 @@ bool ParseLatLon(double & lat, double & lon, string const & key, string const & return true; } -bool IsInvalidApiPoint(ApiPoint const & p) { return p.m_lat == INVALID_LAT_VALUE; } - -} // unnames namespace - -ParsedMapApi::ParsedMapApi() - : m_bmManager(nullptr) - , m_version(0) - , m_zoomLevel(0.0) - , m_goBackOnBalloonClick(false) -{ -} +} // namespace void ParsedMapApi::SetBookmarkManager(BookmarkManager * manager) { m_bmManager = manager; } -ParsingResult ParsedMapApi::SetUriAndParse(string const & url) +ParsedMapApi::ParsingResult ParsedMapApi::SetUriAndParse(string const & url) { Reset(); + + if (!strings::StartsWith(url, "mapswithme://") && !strings::StartsWith(url, "mwm://") && + !strings::StartsWith(url, "mapsme://")) + { + return ParsingResult::Incorrect; + } + ParsingResult const res = Parse(url_scheme::Uri(url)); m_isValid = res != ParsingResult::Incorrect; return res; } -ParsingResult ParsedMapApi::Parse(Uri const & uri) +ParsedMapApi::ParsingResult ParsedMapApi::Parse(Uri const & uri) { string const & scheme = uri.GetScheme(); string const & path = uri.GetPath(); bool const isRoutePath = path == "route"; if ((scheme != "mapswithme" && scheme != "mwm" && scheme != "mapsme") || (path != "map" && !isRoutePath)) + { return ParsingResult::Incorrect; + } if (isRoutePath) { + m_routePoints.clear(); vector pattern{kSourceLatLon, kSourceName, kDestLatLon, kDestName, kRouteType}; if (!uri.ForEachKeyValue(bind(&ParsedMapApi::RouteKeyValue, this, _1, _2, ref(pattern)))) return ParsingResult::Incorrect; @@ -119,41 +113,38 @@ ParsingResult ParsedMapApi::Parse(Uri const & uri) return ParsingResult::Route; } - else + + vector points; + if (!uri.ForEachKeyValue(bind(&ParsedMapApi::AddKeyValue, this, _1, _2, ref(points)))) + return ParsingResult::Incorrect; + + if (points.empty()) + return ParsingResult::Incorrect; + + ASSERT(m_bmManager != nullptr, ()); + UserMarkControllerGuard guard(*m_bmManager, UserMarkType::API_MARK); + for (auto const & p : points) { - ASSERT(m_bmManager != nullptr, ()); - UserMarkControllerGuard guard(*m_bmManager, UserMarkType::API_MARK); - vector points; - if (!uri.ForEachKeyValue(bind(&ParsedMapApi::AddKeyValue, this, _1, _2, ref(points)))) - return ParsingResult::Incorrect; - - points.erase(remove_if(points.begin(), points.end(), &IsInvalidApiPoint), points.end()); - if ((isRoutePath && (points.size() < 2 || m_routingType.empty())) || points.empty()) - return ParsingResult::Incorrect; - - for (auto const & p : points) - { - m2::PointD glPoint(MercatorBounds::FromLatLon(p.m_lat, p.m_lon)); - ApiMarkPoint * mark = static_cast(guard.m_controller.CreateUserMark(glPoint)); - mark->SetName(p.m_name); - mark->SetID(p.m_id); - mark->SetStyle(style::GetSupportedStyle(p.m_style, p.m_name, "")); - } - - return ParsingResult::Map; + m2::PointD glPoint(MercatorBounds::FromLatLon(p.m_lat, p.m_lon)); + ApiMarkPoint * mark = static_cast(guard.m_controller.CreateUserMark(glPoint)); + mark->SetName(p.m_name); + mark->SetID(p.m_id); + mark->SetStyle(style::GetSupportedStyle(p.m_style, p.m_name, "")); } + + return ParsingResult::Map; } bool ParsedMapApi::RouteKeyValue(string key, string const & value, vector & pattern) { - if (key != pattern.front()) + if (pattern.empty() || key != pattern.front()) return false; if (key == kSourceLatLon || key == kDestLatLon) { double lat = 0.0; double lon = 0.0; - if (!ParseLatLon(lat, lon, key, value)) + if (!ParseLatLon(key, value, lat, lon)) return false; RoutePoint p; @@ -167,7 +158,7 @@ bool ParsedMapApi::RouteKeyValue(string key, string const & value, vector GetRoutePoints() const { return m_routePoints; } - string GetRoutingType() const { return m_routingType; } + vector const & GetRoutePoints() const { return m_routePoints; } + string const & GetRoutingType() const { return m_routingType; } private: ParsingResult Parse(Uri const & uri); bool AddKeyValue(string key, string const & value, vector & points); bool RouteKeyValue(string key, string const & value, vector & pattern); - BookmarkManager * m_bmManager; + BookmarkManager * m_bmManager = nullptr; vector m_routePoints; string m_globalBackUrl; string m_appTitle; string m_routingType; - int m_version; + int m_version = 0; /// Zoom level in OSM format (e.g. from 1.0 to 20.0) /// Taken into an account when calculating viewport rect, but only if points count is == 1 - double m_zoomLevel; + double m_zoomLevel = 0.0; bool m_goBackOnBalloonClick = false; bool m_isValid = false; }; From c0163b8b41c99c4e104bde95c6ba22184a29536b Mon Sep 17 00:00:00 2001 From: Dmitry Yunitsky Date: Fri, 5 Aug 2016 19:28:32 +0300 Subject: [PATCH 6/8] [android] Retrieve parsed routing params and build route. --- android/jni/com/mapswithme/maps/Framework.cpp | 31 +++++++++++++++ .../src/com/mapswithme/maps/Framework.java | 5 +++ .../src/com/mapswithme/maps/MwmActivity.java | 25 +++++++++++- .../maps/api/ParsedRoutingData.java | 18 +++++++++ .../maps/api/ParsedUrlMwmRequest.java | 38 +++++++++++++++++++ .../com/mapswithme/maps/api/RoutePoint.java | 18 +++++++++ .../maps/routing/RoutingController.java | 13 +++++-- 7 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 android/src/com/mapswithme/maps/api/ParsedRoutingData.java create mode 100644 android/src/com/mapswithme/maps/api/ParsedUrlMwmRequest.java create mode 100644 android/src/com/mapswithme/maps/api/RoutePoint.java diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 9efab79d49..295a259809 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -540,6 +540,37 @@ Java_com_mapswithme_maps_Framework_nativeClearApiPoints(JNIEnv * env, jclass cla guard.m_controller.Clear(); } +JNIEXPORT jint JNICALL +Java_com_mapswithme_maps_Framework_nativeParseAndSetApiUrl(JNIEnv * env, jclass clazz, jstring url) +{ + return static_cast(frm()->ParseAndSetApiURL(jni::ToNativeString(env, url))); +} + +JNIEXPORT jobject JNICALL +Java_com_mapswithme_maps_Framework_nativeGetParsedRoutingData(JNIEnv * env, jclass clazz) +{ + using namespace url_scheme; + static jclass const pointClazz = jni::GetGlobalClassRef(env, "com/mapswithme/maps/api/RoutePoint"); + // Java signature : RoutePoint(double lat, double lon, String name) + static jmethodID const pointConstructor = jni::GetConstructorID(env, pointClazz, "(DDLjava/lang/String;)V"); + + static jclass const routeDataClazz = jni::GetGlobalClassRef(env, "com/mapswithme/maps/api/ParsedRoutingData"); + // Java signature : ParsedRoutingData(RoutePoint[] points, int routerType) { + static jmethodID const routeDataConstructor = jni::GetConstructorID(env, routeDataClazz, "([Lcom/mapswithme/maps/api/RoutePoint;I)V"); + + auto const & routingData = frm()->GetParsedRoutingData(); + jobjectArray points = jni::ToJavaArray(env, pointClazz, routingData.m_points, + [](JNIEnv * env, RoutePoint const & point) + { + jni::TScopedLocalRef const name(env, jni::ToJavaString(env, point.m_name)); + return env->NewObject(pointClazz, pointConstructor, + MercatorBounds::YToLat(point.m_org.y), + MercatorBounds::XToLon(point.m_org.x), name.get()); + }); + + return env->NewObject(routeDataClazz, routeDataConstructor, points, routingData.m_type); +} + JNIEXPORT void JNICALL Java_com_mapswithme_maps_Framework_nativeSetMapObjectListener(JNIEnv * env, jclass clazz, jobject jListener) { diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java index 36b26a9156..2929374217 100644 --- a/android/src/com/mapswithme/maps/Framework.java +++ b/android/src/com/mapswithme/maps/Framework.java @@ -8,6 +8,8 @@ import android.support.annotation.UiThread; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import com.mapswithme.maps.api.ParsedRoutingData; +import com.mapswithme.maps.api.ParsedUrlMwmRequest; import com.mapswithme.maps.bookmarks.data.DistanceAndAzimut; import com.mapswithme.maps.bookmarks.data.MapObject; import com.mapswithme.maps.routing.RoutingInfo; @@ -103,6 +105,9 @@ public class Framework public static native long nativeGetDataVersion(); public static native void nativeClearApiPoints(); + @ParsedUrlMwmRequest.ParsingResult + public static native int nativeParseAndSetApiUrl(String url); + public static native ParsedRoutingData nativeGetParsedRoutingData(); public static native void nativeDeactivatePopup(); diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index a96994b9ce..b880483150 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -26,6 +26,9 @@ import com.mapswithme.maps.Framework.MapObjectListener; import com.mapswithme.maps.activity.CustomNavigateUpListener; import com.mapswithme.maps.ads.LikesManager; import com.mapswithme.maps.api.ParsedMwmRequest; +import com.mapswithme.maps.api.ParsedRoutingData; +import com.mapswithme.maps.api.ParsedUrlMwmRequest; +import com.mapswithme.maps.api.RoutePoint; import com.mapswithme.maps.base.BaseMwmFragmentActivity; import com.mapswithme.maps.base.OnBackPressListener; import com.mapswithme.maps.bookmarks.BookmarkCategoriesActivity; @@ -1137,7 +1140,27 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override public boolean run(MwmActivity target) { - return MapFragment.nativeShowMapForUrl(mUrl); + final @ParsedUrlMwmRequest.ParsingResult int result = Framework.nativeParseAndSetApiUrl(mUrl); + switch (result) + { + case ParsedUrlMwmRequest.RESULT_INCORRECT: + // TODO handle error + break; + case ParsedUrlMwmRequest.RESULT_MAP: + return MapFragment.nativeShowMapForUrl(mUrl); + case ParsedUrlMwmRequest.RESULT_ROUTE: + final ParsedRoutingData data = Framework.nativeGetParsedRoutingData(); + RoutingController.get().setRouterType(data.mRouterType); + final RoutePoint from = data.mPoints[0]; + final RoutePoint to = data.mPoints[1]; + RoutingController.get().prepare(new MapObject(MapObject.API_POINT, from.mName, "", "", + from.mLat, from.mLon, ""), + new MapObject(MapObject.API_POINT, to.mName, "", "", + to.mLat, to.mLon, "")); + return true; + } + + return false; } } diff --git a/android/src/com/mapswithme/maps/api/ParsedRoutingData.java b/android/src/com/mapswithme/maps/api/ParsedRoutingData.java new file mode 100644 index 0000000000..206b46786e --- /dev/null +++ b/android/src/com/mapswithme/maps/api/ParsedRoutingData.java @@ -0,0 +1,18 @@ +package com.mapswithme.maps.api; + +import com.mapswithme.maps.Framework; + +/** + * Represents Framework::ParsedRoutingData from core. + */ +public class ParsedRoutingData +{ + public final RoutePoint[] mPoints; + @Framework.RouterType + public final int mRouterType; + + public ParsedRoutingData(RoutePoint[] points, int routerType) { + this.mPoints = points; + this.mRouterType = routerType; + } +} diff --git a/android/src/com/mapswithme/maps/api/ParsedUrlMwmRequest.java b/android/src/com/mapswithme/maps/api/ParsedUrlMwmRequest.java new file mode 100644 index 0000000000..84f35f4ec2 --- /dev/null +++ b/android/src/com/mapswithme/maps/api/ParsedUrlMwmRequest.java @@ -0,0 +1,38 @@ +package com.mapswithme.maps.api; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents url_scheme::ParsedMapApi::ParsingResult from core. + */ +public class ParsedUrlMwmRequest +{ + @Retention(RetentionPolicy.SOURCE) + @IntDef({RESULT_INCORRECT, RESULT_MAP, RESULT_ROUTE}) + public @interface ParsingResult {} + + public static final int RESULT_INCORRECT = 0; + public static final int RESULT_MAP = 1; + public static final int RESULT_ROUTE = 2; + + public final RoutePoint[] mRoutePoints; + public final String mGlobalUrl; + public final String mAppTitle; + public final int mVersion; + public final double mZoomLevel; + public final boolean mGoBackOnBalloonClick; + public final boolean mIsValid; + + public ParsedUrlMwmRequest(RoutePoint[] routePoints, String globalUrl, String appTitle, int version, double zoomLevel, boolean goBackOnBalloonClick, boolean isValid) { + this.mRoutePoints = routePoints; + this.mGlobalUrl = globalUrl; + this.mAppTitle = appTitle; + this.mVersion = version; + this.mZoomLevel = zoomLevel; + this.mGoBackOnBalloonClick = goBackOnBalloonClick; + this.mIsValid = isValid; + } +} diff --git a/android/src/com/mapswithme/maps/api/RoutePoint.java b/android/src/com/mapswithme/maps/api/RoutePoint.java new file mode 100644 index 0000000000..cad6c0ce68 --- /dev/null +++ b/android/src/com/mapswithme/maps/api/RoutePoint.java @@ -0,0 +1,18 @@ +package com.mapswithme.maps.api; + +/** + * Represents url_scheme::RoutePoint from core. + */ +public class RoutePoint +{ + public final double mLat; + public final double mLon; + public final String mName; + + public RoutePoint(double lat, double lon, String name) + { + mLat = lat; + mLon = lon; + mName = name; + } +} diff --git a/android/src/com/mapswithme/maps/routing/RoutingController.java b/android/src/com/mapswithme/maps/routing/RoutingController.java index 2d2a94d60b..fe8aa5cb82 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingController.java +++ b/android/src/com/mapswithme/maps/routing/RoutingController.java @@ -254,7 +254,7 @@ public class RoutingController Framework.nativeBuildRoute(mStartPoint.getLat(), mStartPoint.getLon(), mEndPoint.getLat(), mEndPoint.getLon()); } - private void showDisclaimer(final MapObject endPoint) + private void showDisclaimer(final MapObject startPoint, final MapObject endPoint) { StringBuilder builder = new StringBuilder(); for (int resId : new int[] { R.string.dialog_routing_disclaimer_priority, R.string.dialog_routing_disclaimer_precision, @@ -273,23 +273,28 @@ public class RoutingController public void onClick(DialogInterface dlg, int which) { Config.acceptRoutingDisclaimer(); - prepare(endPoint); + prepare(startPoint, endPoint); } }).show(); } public void prepare(@Nullable MapObject endPoint) + { + prepare(LocationHelper.INSTANCE.getMyPosition(), endPoint); + } + + public void prepare(@Nullable MapObject startPoint, @Nullable MapObject endPoint) { mLogger.d("prepare (" + (endPoint == null ? "route)" : "p2p)")); if (!Config.isRoutingDisclaimerAccepted()) { - showDisclaimer(endPoint); + showDisclaimer(startPoint, endPoint); return; } cancel(); - mStartPoint = LocationHelper.INSTANCE.getMyPosition(); + mStartPoint = startPoint; mEndPoint = endPoint; setState(State.PREPARE); From 66f49ace41e2e24c3e824f7aef04497ad07885c1 Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Mon, 8 Aug 2016 10:54:22 +0300 Subject: [PATCH 7/8] [coding_test] Fixed coding tests. --- coding/coding_tests/uri_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coding/coding_tests/uri_test.cpp b/coding/coding_tests/uri_test.cpp index 9a46296aac..c6676ea112 100644 --- a/coding/coding_tests/uri_test.cpp +++ b/coding/coding_tests/uri_test.cpp @@ -33,12 +33,13 @@ public: private: - void AddTestValue(string const & key, string const & value) + bool AddTestValue(string const & key, string const & value) { TEST(!m_keyValuePairs.empty(), ("Failed for uri = ", m_uri, "Passed KV = ", key, value)); TEST_EQUAL(m_keyValuePairs.front().first, key, ()); TEST_EQUAL(m_keyValuePairs.front().second, value, ()); m_keyValuePairs.pop(); + return true; } string m_uri, m_scheme, m_path; From e3ec8918d3e4fd17e1090bcd138e6b17b9e3153d Mon Sep 17 00:00:00 2001 From: Dmitry Yunitsky Date: Mon, 8 Aug 2016 13:26:39 +0300 Subject: [PATCH 8/8] [android] Build fix. --- android/src/com/mapswithme/maps/routing/RoutingController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/com/mapswithme/maps/routing/RoutingController.java b/android/src/com/mapswithme/maps/routing/RoutingController.java index fe8aa5cb82..7fb4d4443a 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingController.java +++ b/android/src/com/mapswithme/maps/routing/RoutingController.java @@ -660,7 +660,7 @@ public class RoutingController checkAndBuildRoute(); } - void setRouterType(@Framework.RouterType int router) + public void setRouterType(@Framework.RouterType int router) { mLogger.d("setRouterType: " + mLastRouterType + " -> " + router);