forked from organicmaps/organicmaps
Add geo://... url scheme parser.
This commit is contained in:
parent
325591f08e
commit
7860b5c951
6 changed files with 455 additions and 1 deletions
140
map/ge0_parser.cpp
Normal file
140
map/ge0_parser.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include "ge0_parser.hpp"
|
||||
#include "url_api.hpp"
|
||||
#include "../api/internal/c/api-client-internals.h"
|
||||
#include "../coding/url_encode.hpp"
|
||||
#include "../base/math.hpp"
|
||||
|
||||
static const int NAME_POSITON_IN_URL = 17;
|
||||
static const int ZOOM_POSITION = 6;
|
||||
|
||||
url_api::Ge0Parser::Ge0Parser()
|
||||
{
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
m_base64ReverseCharTable[i] = 255;
|
||||
for (uint8_t i = 0; i < 64; ++i)
|
||||
{
|
||||
char c = MapsWithMe_Base64Char(i);
|
||||
m_base64ReverseCharTable[static_cast<uint8_t>(c)] = i;
|
||||
}
|
||||
}
|
||||
|
||||
bool url_api::Ge0Parser::Parse(string const & url, Request & request)
|
||||
{
|
||||
// URL format:
|
||||
//
|
||||
// +------------------ 1 byte: zoom level
|
||||
// |+-------+--------- 9 bytes: lat,lon
|
||||
// || | +--+---- Variable number of bytes: point name
|
||||
// || | | |
|
||||
// ge0://ZCoordba64/Name
|
||||
|
||||
request.Clear();
|
||||
if (url.size() < 16 || url.substr(0, 6) != "ge0://")
|
||||
return false;
|
||||
|
||||
uint8_t const zoomI = DecodeBase64Char(url[ZOOM_POSITION]);
|
||||
if (zoomI > 63)
|
||||
return false;
|
||||
request.m_viewportZoomLevel = DecodeZoom(zoomI);
|
||||
|
||||
request.m_points.push_back(url_api::Point());
|
||||
url_api::Point & newPt = request.m_points.back();
|
||||
|
||||
DecodeLatLon(url.substr(7,9), newPt.m_lat, newPt.m_lon);
|
||||
|
||||
request.m_viewportLat = newPt.m_lat;
|
||||
request.m_viewportLon = newPt.m_lon;
|
||||
|
||||
if (url.size() >= NAME_POSITON_IN_URL)
|
||||
newPt.m_name = DecodeName(url.substr(NAME_POSITON_IN_URL, url.size() - NAME_POSITON_IN_URL));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t url_api::Ge0Parser::DecodeBase64Char(char const c)
|
||||
{
|
||||
return m_base64ReverseCharTable[static_cast<uint8_t>(c)];
|
||||
}
|
||||
|
||||
double url_api::Ge0Parser::DecodeZoom(uint8_t const zoomByte)
|
||||
{
|
||||
//Coding zoom - int newZoom = ((oldZoom - 4) * 4)
|
||||
return static_cast<double>(zoomByte) / 4 + 4;
|
||||
}
|
||||
|
||||
void url_api::Ge0Parser::DecodeLatLon(string const & url, double & lat, double & lon)
|
||||
{
|
||||
int latInt = 0, lonInt = 0;
|
||||
DecodeLatLonToInt(url, latInt, lonInt, url.size());
|
||||
lat = DecodeLatFromInt(latInt, (1 << MAPSWITHME_MAX_COORD_BITS) - 1);
|
||||
lon = DecodeLonFromInt(lonInt, (1 << MAPSWITHME_MAX_COORD_BITS) - 1);
|
||||
}
|
||||
|
||||
void url_api::Ge0Parser::DecodeLatLonToInt(string const & url, int & lat, int & lon, int const bytes)
|
||||
{
|
||||
for(int i = 0, shift = MAPSWITHME_MAX_COORD_BITS - 3; i < bytes; ++i, shift -= 3)
|
||||
{
|
||||
const uint8_t a = DecodeBase64Char(url[i]);
|
||||
const int lat1 = (((a >> 5) & 1) << 2 |
|
||||
((a >> 3) & 1) << 1 |
|
||||
((a >> 1) & 1));
|
||||
const int lon1 = (((a >> 4) & 1) << 2 |
|
||||
((a >> 2) & 1) << 1 |
|
||||
(a & 1));
|
||||
lat |= lat1 << shift;
|
||||
lon |= lon1 << shift;
|
||||
}
|
||||
const double middleOfSquare = 1 << (3 * (MAPSWITHME_MAX_POINT_BYTES - bytes) - 1);
|
||||
lat += middleOfSquare;
|
||||
lon += middleOfSquare;
|
||||
}
|
||||
|
||||
double url_api::Ge0Parser::DecodeLatFromInt(int const lat, int const maxValue)
|
||||
{
|
||||
return static_cast<double>(lat) / maxValue * 180 - 90;
|
||||
}
|
||||
|
||||
double url_api::Ge0Parser::DecodeLonFromInt(int const lon, int const maxValue)
|
||||
{
|
||||
return static_cast<double>(lon) / (maxValue + 1.0) * 360.0 - 180;
|
||||
}
|
||||
|
||||
string url_api::Ge0Parser::DecodeName(string const & name)
|
||||
{
|
||||
string resultName = name;
|
||||
ValidateName(resultName);
|
||||
resultName = UrlDecode(resultName);
|
||||
SpacesToUnderscore(resultName);
|
||||
return resultName;
|
||||
}
|
||||
|
||||
void url_api::Ge0Parser::SpacesToUnderscore(string & name)
|
||||
{
|
||||
for (size_t i = 0; i < name.size(); ++i)
|
||||
if (name[i] == ' ')
|
||||
name[i] = '_';
|
||||
else if (name[i] == '_')
|
||||
name[i] = ' ';
|
||||
}
|
||||
|
||||
void url_api::Ge0Parser::ValidateName(string & name)
|
||||
{
|
||||
if (name.empty())
|
||||
return;
|
||||
for (size_t i = 0; i + 2 < name.size(); ++i)
|
||||
{
|
||||
if (name[i] == '%' && (!IsHexChar(name[i + 1]) || !IsHexChar(name[i + 2])))
|
||||
{
|
||||
name.resize(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (name[name.size() - 1] == '%')
|
||||
name.resize(name.size() - 1);
|
||||
else if (name.size() > 1 && name[name.size() - 2] == '%')
|
||||
name.resize(name.size() - 2);
|
||||
}
|
||||
|
||||
bool url_api::Ge0Parser::IsHexChar(char const a)
|
||||
{
|
||||
return ((a >= '0' && a <= '9') || (a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f'));
|
||||
}
|
33
map/ge0_parser.hpp
Normal file
33
map/ge0_parser.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include "../base/base.hpp"
|
||||
#include "../std/string.hpp"
|
||||
|
||||
namespace url_api
|
||||
{
|
||||
|
||||
class Request;
|
||||
|
||||
class Ge0Parser
|
||||
{
|
||||
public:
|
||||
Ge0Parser();
|
||||
|
||||
bool Parse(string const & url, Request & request);
|
||||
|
||||
protected:
|
||||
uint8_t DecodeBase64Char(char const c);
|
||||
static double DecodeZoom(uint8_t const zoomByte);
|
||||
void DecodeLatLon(string const & url, double & lat, double & lon);
|
||||
void DecodeLatLonToInt(string const & url, int & lat, int & lon, int const bytes);
|
||||
double DecodeLatFromInt(int const lat, int const maxValue);
|
||||
double DecodeLonFromInt(int const lon, int const maxValue);
|
||||
string DecodeName(string const & name);
|
||||
void SpacesToUnderscore(string & name);
|
||||
void ValidateName(string & name);
|
||||
static bool IsHexChar(char const a);
|
||||
|
||||
private:
|
||||
uint8_t m_base64ReverseCharTable[256];
|
||||
};
|
||||
|
||||
} // namespace url_api
|
|
@ -56,6 +56,8 @@ HEADERS += \
|
|||
area_info.hpp \
|
||||
geometry_processors.hpp \
|
||||
bookmark_manager.hpp \
|
||||
ge0_parser.hpp \
|
||||
url_api.hpp \
|
||||
|
||||
SOURCES += \
|
||||
feature_vec_model.cpp \
|
||||
|
@ -102,6 +104,8 @@ SOURCES += \
|
|||
feature_info.cpp \
|
||||
geometry_processors.cpp \
|
||||
bookmark_manager.cpp \
|
||||
ge0_parser.cpp \
|
||||
../api/src/c/api-client.c \
|
||||
|
||||
!iphone*:!bada*:!android* {
|
||||
HEADERS += qgl_render_context.hpp
|
||||
|
|
248
map/map_tests/ge0_parser_tests.cpp
Normal file
248
map/map_tests/ge0_parser_tests.cpp
Normal file
|
@ -0,0 +1,248 @@
|
|||
#include "../../testing/testing.hpp"
|
||||
|
||||
#include "../ge0_parser.hpp"
|
||||
#include "../url_api.hpp"
|
||||
|
||||
#include "../../api/internal/c/api-client-internals.h"
|
||||
#include "../../api/src/c/api-client.h"
|
||||
|
||||
#include "../../base/macros.hpp"
|
||||
|
||||
|
||||
using url_api::Ge0Parser;
|
||||
using url_api::Request;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class Ge0ParserForTest : public Ge0Parser
|
||||
{
|
||||
public:
|
||||
using Ge0Parser::DecodeBase64Char;
|
||||
using Ge0Parser::DecodeZoom;
|
||||
using Ge0Parser::DecodeLatLon;
|
||||
};
|
||||
|
||||
double GetLatEpsilon(int coordBytes)
|
||||
{
|
||||
// Should be / 2.0 but probably because of accumulates loss of precision, 1.77 works but 2.0 doesn't.
|
||||
double infelicity = 1 << ((MAPSWITHME_MAX_POINT_BYTES - coordBytes) * 3);
|
||||
return infelicity / ((1 << MAPSWITHME_MAX_COORD_BITS) - 1) * 180 / 1.77;
|
||||
}
|
||||
|
||||
double GetLonEpsilon(int coordBytes)
|
||||
{
|
||||
// Should be / 2.0 but probably because of accumulates loss of precision, 1.77 works but 2.0 doesn't.
|
||||
double infelicity = 1 << ((MAPSWITHME_MAX_POINT_BYTES - coordBytes) * 3);
|
||||
return (infelicity / ((1 << MAPSWITHME_MAX_COORD_BITS) - 1)) * 360 / 1.77;
|
||||
}
|
||||
|
||||
void TestSuccess(char const * s, double lat, double lon, double zoom, char const * name)
|
||||
{
|
||||
Ge0Parser parser;
|
||||
Request request;
|
||||
bool const result = parser.Parse(s, request);
|
||||
|
||||
TEST(result, (s, zoom, lat, lon, name));
|
||||
|
||||
TEST_EQUAL(request.m_points.size(), 1, (s, zoom, lat, lon, name));
|
||||
TEST_EQUAL(request.m_points[0].m_name, string(name), (s));
|
||||
TEST_EQUAL(request.m_points[0].m_id, string(), (s));
|
||||
double const latEps = GetLatEpsilon(9);
|
||||
double const lonEps = GetLonEpsilon(9);
|
||||
TEST(fabs(request.m_points[0].m_lat - lat) <= latEps, (s, zoom, lat, lon, name));
|
||||
TEST(fabs(request.m_points[0].m_lon - lon) <= lonEps, (s, zoom, lat, lon, name));
|
||||
|
||||
TEST(fabs(request.m_viewportLat - lat) <= latEps, (s, zoom, lat, lon, name));
|
||||
TEST(fabs(request.m_viewportLon - lon) <= lonEps, (s, zoom, lat, lon, name));
|
||||
TEST_ALMOST_EQUAL(request.m_viewportZoomLevel, zoom, (s, zoom, lat, lon, name));
|
||||
}
|
||||
|
||||
void TestFailure(char const * s)
|
||||
{
|
||||
Ge0Parser parser;
|
||||
Request request;
|
||||
bool const result = parser.Parse(s, request);
|
||||
TEST_EQUAL(result, false, (s));
|
||||
}
|
||||
|
||||
bool ConvergenceTest(double lat, double lon, double latEps, double lonEps)
|
||||
{
|
||||
double tmpLat = lat, tmpLon = lon;
|
||||
Ge0ParserForTest parser;
|
||||
for (size_t i = 0; i < 100000; ++i)
|
||||
{
|
||||
char urlPrefix[] = "Coord6789";
|
||||
MapsWithMe_LatLonToString(tmpLat, tmpLon, urlPrefix + 0, 9);
|
||||
parser.DecodeLatLon(urlPrefix, tmpLat, tmpLon);
|
||||
}
|
||||
if (abs(lat - tmpLat) <= latEps && abs(lon - tmpLon) <= lonEps)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
UNIT_TEST(Base64DecodingWorksForAValidChar)
|
||||
{
|
||||
Ge0ParserForTest parser;
|
||||
for (size_t i = 0; i < 64; ++i)
|
||||
{
|
||||
char c = MapsWithMe_Base64Char(i);
|
||||
int i1 = parser.DecodeBase64Char(c);
|
||||
TEST_EQUAL(i, i1, (c));
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Base64DecodingReturns255ForInvalidChar)
|
||||
{
|
||||
Ge0ParserForTest parser;
|
||||
TEST_EQUAL(parser.DecodeBase64Char(' '), 255, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Base64DecodingDoesNotCrashForAllChars)
|
||||
{
|
||||
Ge0ParserForTest parser;
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
parser.DecodeBase64Char(static_cast<char>(i));
|
||||
}
|
||||
|
||||
UNIT_TEST(Base64DecodingCharFrequency)
|
||||
{
|
||||
vector<int> charCounts(256, 0);
|
||||
Ge0ParserForTest parser;
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
++charCounts[parser.DecodeBase64Char(static_cast<char>(i))];
|
||||
sort(charCounts.begin(), charCounts.end());
|
||||
TEST_EQUAL(charCounts[255], 256 - 64, ());
|
||||
TEST_EQUAL(charCounts[254], 1, ());
|
||||
TEST_EQUAL(charCounts[254 - 63], 1, ());
|
||||
TEST_EQUAL(charCounts[254 - 64], 0, ());
|
||||
TEST_EQUAL(charCounts[0], 0, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(UrlSchemaValidationFailed)
|
||||
{
|
||||
TestFailure("trali vali");
|
||||
TestFailure("trali vali tili tili eto my prohodili");
|
||||
}
|
||||
|
||||
UNIT_TEST(DecodeZoomLevel)
|
||||
{
|
||||
TEST_EQUAL(Ge0ParserForTest::DecodeZoom(0), 4, ());
|
||||
TEST_EQUAL(Ge0ParserForTest::DecodeZoom(4), 5, ());
|
||||
TEST_EQUAL(Ge0ParserForTest::DecodeZoom(6), 5.5, ());
|
||||
TEST_EQUAL(Ge0ParserForTest::DecodeZoom(53), 17.25, ());
|
||||
TEST_EQUAL(Ge0ParserForTest::DecodeZoom(60), 19, ());
|
||||
TEST_EQUAL(Ge0ParserForTest::DecodeZoom(63), 19.75, ());
|
||||
TestFailure("ge0://!wAAAAAAAA/Name");
|
||||
TestFailure("ge0:///wAAAAAAAA/Name");
|
||||
}
|
||||
|
||||
UNIT_TEST(LatLonConvergence)
|
||||
{
|
||||
double const latEps = GetLatEpsilon(9);
|
||||
double const lonEps = GetLonEpsilon(9);
|
||||
TEST(ConvergenceTest(0, 0, latEps, lonEps), ());
|
||||
TEST(ConvergenceTest(1.111111, 2.11111, latEps, lonEps), ());
|
||||
TEST(ConvergenceTest(-1.111111, -2.11111, latEps, lonEps), ());
|
||||
TEST(ConvergenceTest(-90, -179.999999, latEps, lonEps), ());
|
||||
TEST(ConvergenceTest(-88.12313, 80.4532999999, latEps, lonEps), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ZoomDecoding)
|
||||
{
|
||||
TestSuccess("ge0://8wAAAAAAAA/Name", 0, 0, 19, "Name");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Name", 0, 0, 4, "Name");
|
||||
TestSuccess("ge0://BwAAAAAAAA/Name", 0, 0, 4.25, "Name");
|
||||
}
|
||||
|
||||
UNIT_TEST(LatLonDecoding)
|
||||
{
|
||||
TestSuccess("ge0://Byqqqqqqqq/Name", 45, 0, 4.25, "Name");
|
||||
TestSuccess("ge0://B6qqqqqqqq/Name", 90, 0, 4.25, "Name");
|
||||
TestSuccess("ge0://BVVVVVVVVV/Name", -90, 179.999999, 4.25, "Name");
|
||||
TestSuccess("ge0://BP________/Name", -0.000001, -0.000001, 4.25, "Name");
|
||||
TestSuccess("ge0://B_________/Name", 90, 179.999999, 4.25, "Name");
|
||||
TestSuccess("ge0://Bqqqqqqqqq/Name", 90, -180, 4.25, "Name");
|
||||
TestSuccess("ge0://BAAAAAAAAA/Name", -90, -180, 4.25, "Name");
|
||||
}
|
||||
|
||||
UNIT_TEST(NameDecoding)
|
||||
{
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super_Poi", 0, 0, 4, "Super Poi");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5FPoi", 0, 0, 4, "Super Poi");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5fPoi", 0, 0, 4, "Super Poi");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super Poi", 0, 0, 4, "Super_Poi");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%20Poi", 0, 0, 4, "Super_Poi");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Name%", 0, 0, 4, "Name");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Name%2", 0, 0, 4, "Name");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Hello%09World%0A", 0, 0, 4, "Hello\tWorld\n");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Hello%%%%%%%%%", 0, 0, 4, "Hello");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Hello%%%%%%%%%World", 0, 0, 4, "Hello");
|
||||
TestSuccess("ge0://AwAAAAAAAA/%d0%9c%d0%b8%d0%bd%d1%81%d0%ba_%d1%83%d0%bb._%d0%9b%d0%b5%d0%bd%d0%b8%d0%bd%d0%b0_9", 0, 0, 4, "Минск ул. Ленина 9");
|
||||
TestSuccess("ge0://AwAAAAAAAA/z%c3%bcrich_bahnhofstrasse", 0, 0, 4, "zürich bahnhofstrasse");
|
||||
TestSuccess("ge0://AwAAAAAAAA/%e5%8c%97%e4%ba%ac_or_B%c4%9bij%c4%abng%3F", 0, 0, 4, "北京 or Běijīng?");
|
||||
TestSuccess("ge0://AwAAAAAAAA/%d0%9a%d0%b0%d0%ba_%d0%b2%d1%8b_%d1%81%d1%87%d0%b8%d1%82%d0%b0%d0%b5%d1%82%d0%b5%2C_%d0%bd%d0%b0%d0%b4%d0%be_%d0%bb%d0%b8_%d0%bf%d0%b8%d1%81%d0%b0%d1%82%d1%8c_const_%d0%b4%d0%bb%d1%8f_%d0%bf%d0%b0%d1%80%d0%b0%d0%bc%d0%b5%d1%82%d1%80%d0%be%d0%b2%2C_%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5_%d0%bf%d0%b5%d1%80%d0%b5%d0%b4%d0%b0%d1%8e%d1%82%d1%81%d1%8f_%d0%b2_%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d1%8e_%d0%bf%d0%be_%d0%b7%d0%bd%d0%b0%d1%87%d0%b5%d0%bd%d0%b8%d1%8e%3F",
|
||||
0, 0, 4, "Как вы считаете, надо ли писать const для параметров, которые передаются в функцию по значению?");
|
||||
TestSuccess("ge0://AwAAAAAAAA/\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xba\xd0\xb0_\xd0\xb2_\xd1\x8e\xd1\x82\xd1\x84-8", 0, 0, 4, "строка в ютф-8");
|
||||
}
|
||||
|
||||
UNIT_TEST(LatLonFullAndClippedCoordinates)
|
||||
{
|
||||
double maxLatDiffForCoordSize[10] = { 0 };
|
||||
double maxLonDiffForCoordSize[10] = { 0 };
|
||||
for (double lat = -90; lat <= 90; lat += 0.7)
|
||||
{
|
||||
for (double lon = -180; lon < 180; lon += 0.7)
|
||||
{
|
||||
char buf[20] = { 0 };
|
||||
MapsWithMe_GenShortShowMapUrl(lat, lon, 4, "", buf, ARRAY_SIZE(buf));
|
||||
for (int i = 9; i >= 1; --i)
|
||||
{
|
||||
string const str = string(buf).substr(7, i);
|
||||
int const coordSize = str.size();
|
||||
Ge0ParserForTest parser;
|
||||
double latTmp, lonTmp;
|
||||
parser.DecodeLatLon(str, latTmp, lonTmp);
|
||||
double const epsLat = GetLatEpsilon(coordSize);
|
||||
double const epsLon = GetLonEpsilon(coordSize);
|
||||
double const difLat = fabs(lat - latTmp);
|
||||
double const difLon = fabs(lon - lonTmp);
|
||||
TEST(difLat <= epsLat, (str, lat, latTmp, lon, lonTmp, difLat, epsLat));
|
||||
TEST(difLon <= epsLon, (str, lat, latTmp, lon, lonTmp, difLon, epsLon));
|
||||
maxLatDiffForCoordSize[coordSize] = max(maxLatDiffForCoordSize[coordSize], difLat);
|
||||
maxLonDiffForCoordSize[coordSize] = max(maxLonDiffForCoordSize[coordSize], difLon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int coordSize = 1; coordSize <= 8; ++coordSize)
|
||||
{
|
||||
TEST(maxLatDiffForCoordSize[coordSize] > maxLatDiffForCoordSize[coordSize + 1], (coordSize));
|
||||
TEST(maxLonDiffForCoordSize[coordSize] > maxLonDiffForCoordSize[coordSize + 1], (coordSize));
|
||||
|
||||
TEST(maxLatDiffForCoordSize[coordSize] <= GetLatEpsilon(coordSize), (coordSize));
|
||||
TEST(maxLonDiffForCoordSize[coordSize] <= GetLonEpsilon(coordSize), (coordSize));
|
||||
|
||||
TEST(maxLatDiffForCoordSize[coordSize] > GetLatEpsilon(coordSize + 1), (coordSize));
|
||||
TEST(maxLonDiffForCoordSize[coordSize] > GetLonEpsilon(coordSize + 1), (coordSize));
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ClippedName)
|
||||
{
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5fPoi", 0, 0, 4, "Super Poi");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5fPo" , 0, 0, 4, "Super Po");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5fP" , 0, 0, 4, "Super P");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5f" , 0, 0, 4, "Super ");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%5" , 0, 0, 4, "Super");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super%" , 0, 0, 4, "Super");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Super" , 0, 0, 4, "Super");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Supe" , 0, 0, 4, "Supe");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Sup" , 0, 0, 4, "Sup");
|
||||
TestSuccess("ge0://AwAAAAAAAA/Su" , 0, 0, 4, "Su");
|
||||
TestSuccess("ge0://AwAAAAAAAA/S" , 0, 0, 4, "S");
|
||||
TestSuccess("ge0://AwAAAAAAAA/" , 0, 0, 4, "");
|
||||
TestSuccess("ge0://AwAAAAAAAA" , 0, 0, 4, "");
|
||||
}
|
|
@ -29,4 +29,5 @@ SOURCES += \
|
|||
geourl_test.cpp \
|
||||
measurement_tests.cpp \
|
||||
mwm_url_tests.cpp \
|
||||
feature_processor_test.cpp
|
||||
feature_processor_test.cpp \
|
||||
ge0_parser_tests.cpp \
|
||||
|
|
28
map/url_api.hpp
Normal file
28
map/url_api.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "../std/vector.hpp"
|
||||
|
||||
namespace url_api
|
||||
{
|
||||
|
||||
struct Point
|
||||
{
|
||||
Point() : m_lat(0), m_lon(0) {}
|
||||
|
||||
double m_lat;
|
||||
double m_lon;
|
||||
string m_name;
|
||||
string m_id;
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
vector<Point> m_points;
|
||||
double m_viewportLat, m_viewportLon, m_viewportZoomLevel;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_points.clear();
|
||||
m_viewportLat = m_viewportLon = m_viewportZoomLevel = 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace url_api
|
Loading…
Add table
Reference in a new issue