Parsing big url API

This commit is contained in:
Kirill Zhdanovich 2013-05-24 15:11:31 +03:00 committed by Alex Zolotarev
parent d58c23e529
commit 4ae7f95bad
5 changed files with 300 additions and 9 deletions

View file

@ -838,6 +838,7 @@ void Framework::DrawAdditionalInfo(shared_ptr<PaintEvent> 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<PaintEvent> const & e)
{
Navigator & navigator = GetNavigator();
InformationDisplay & informationDisplay = GetInformationDisplay();
// get viewport limit rect
m2::AnyRectD const & glbRect = navigator.Screen().GlobalRect();
Drawer * pDrawer = e->drawer();
vector<url_scheme::ApiPoint> 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<double>::max();
bool result = false;
vector <url_scheme::ApiPoint> 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;
}

View file

@ -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<PaintEvent> const & e);
public:
void MapApiSetUriAndParse(string const & url);
bool GetMapApiPoint(m2::PointD const & pxPoint, url_scheme::ApiPoint & point);
vector<url_scheme::ApiPoint> 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(); }
};

View file

@ -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 <url_scheme::ApiPoint> 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 <url_scheme::ApiPoint> 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);
}

View file

@ -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();
}

View file

@ -19,23 +19,26 @@ class ParsedMapApi
{
public:
ParsedMapApi(Uri const & uri);
ParsedMapApi();
void SetUriAndParse(string const & url);
bool IsValid() const;
vector<ApiPoint> 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<ApiPoint> 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<char> m_iconData;
*/
};
}