Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
187
libs/ge0/parser.cpp
Normal file
187
libs/ge0/parser.cpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#include "ge0/parser.hpp"
|
||||
|
||||
#include "ge0/url_generator.hpp"
|
||||
|
||||
#include "coding/url.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/math.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace ge0
|
||||
{
|
||||
Ge0Parser::Ge0Parser()
|
||||
{
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
m_base64ReverseCharTable[i] = 255;
|
||||
for (uint8_t i = 0; i < 64; ++i)
|
||||
{
|
||||
char c = Base64Char(i);
|
||||
m_base64ReverseCharTable[static_cast<uint8_t>(c)] = i;
|
||||
}
|
||||
}
|
||||
|
||||
bool Ge0Parser::Parse(std::string const & url, Result & result)
|
||||
{
|
||||
// Original URL format:
|
||||
//
|
||||
// +------------------ 1 byte: zoom level
|
||||
// |+-------+--------- 9 bytes: lat,lon
|
||||
// || | +--+---- Variable number of bytes: point name
|
||||
// || | | |
|
||||
// cm://ZCoordba64/Name
|
||||
|
||||
// Alternative format (differs only in the prefix):
|
||||
// http://comaps.app/ZCoordba64/Name
|
||||
|
||||
for (auto prefix : kGe0Prefixes)
|
||||
if (url.starts_with(prefix))
|
||||
return ParseAfterPrefix(url, prefix.size(), result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Ge0Parser::ParseAfterPrefix(std::string const & url, size_t from, Result & result)
|
||||
{
|
||||
size_t constexpr kEncodedZoomAndCoordinatesLength = 10;
|
||||
if (url.size() < from + kEncodedZoomAndCoordinatesLength)
|
||||
return false;
|
||||
|
||||
size_t constexpr kMaxNameLength = 256;
|
||||
|
||||
size_t const posZoom = from;
|
||||
size_t const posLatLon = posZoom + 1;
|
||||
size_t const posName = from + kEncodedZoomAndCoordinatesLength + 1;
|
||||
size_t const lengthLatLon = posName - posLatLon - 1;
|
||||
|
||||
uint8_t const zoomI = DecodeBase64Char(url[posZoom]);
|
||||
if (zoomI >= 64)
|
||||
return false;
|
||||
result.m_zoomLevel = DecodeZoom(zoomI);
|
||||
|
||||
if (!DecodeLatLon(url.substr(posLatLon, lengthLatLon), result.m_lat, result.m_lon))
|
||||
return false;
|
||||
|
||||
ASSERT(mercator::ValidLat(result.m_lat), (result.m_lat));
|
||||
ASSERT(mercator::ValidLon(result.m_lon), (result.m_lon));
|
||||
|
||||
if (url.size() >= posName)
|
||||
{
|
||||
CHECK_GREATER(posName, 0, ());
|
||||
if (url[posName - 1] != '/')
|
||||
return false;
|
||||
result.m_name = DecodeName(url.substr(posName, std::min(url.size() - posName, kMaxNameLength)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t Ge0Parser::DecodeBase64Char(char const c)
|
||||
{
|
||||
return m_base64ReverseCharTable[static_cast<uint8_t>(c)];
|
||||
}
|
||||
|
||||
double Ge0Parser::DecodeZoom(uint8_t const zoomByte)
|
||||
{
|
||||
// Coding zoom - int newZoom = ((oldZoom - 4) * 4)
|
||||
return static_cast<double>(zoomByte) / 4 + 4;
|
||||
}
|
||||
|
||||
bool Ge0Parser::DecodeLatLon(std::string const & s, double & lat, double & lon)
|
||||
{
|
||||
int latInt = 0;
|
||||
int lonInt = 0;
|
||||
if (!DecodeLatLonToInt(s, latInt, lonInt))
|
||||
return false;
|
||||
|
||||
lat = DecodeLatFromInt(latInt, (1 << kMaxCoordBits) - 1);
|
||||
lon = DecodeLonFromInt(lonInt, (1 << kMaxCoordBits) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ge0Parser::DecodeLatLonToInt(std::string const & s, int & lat, int & lon)
|
||||
{
|
||||
int shift = kMaxCoordBits - 3;
|
||||
for (size_t i = 0; i < s.size(); ++i, shift -= 3)
|
||||
{
|
||||
uint8_t const a = DecodeBase64Char(s[i]);
|
||||
if (a >= 64)
|
||||
return false;
|
||||
|
||||
int const lat1 = (((a >> 5) & 1) << 2 | ((a >> 3) & 1) << 1 | ((a >> 1) & 1));
|
||||
int const lon1 = (((a >> 4) & 1) << 2 | ((a >> 2) & 1) << 1 | (a & 1));
|
||||
lat |= lat1 << shift;
|
||||
lon |= lon1 << shift;
|
||||
}
|
||||
double const middleOfSquare = 1 << (3 * (kMaxPointBytes - s.size()) - 1);
|
||||
lat += middleOfSquare;
|
||||
lon += middleOfSquare;
|
||||
return true;
|
||||
}
|
||||
|
||||
double Ge0Parser::DecodeLatFromInt(int const lat, int const maxValue)
|
||||
{
|
||||
return static_cast<double>(lat) / maxValue * 180 - 90;
|
||||
}
|
||||
|
||||
double Ge0Parser::DecodeLonFromInt(int const lon, int const maxValue)
|
||||
{
|
||||
return static_cast<double>(lon) / (maxValue + 1.0) * 360.0 - 180;
|
||||
}
|
||||
|
||||
std::string Ge0Parser::DecodeName(std::string name)
|
||||
{
|
||||
ValidateName(name);
|
||||
name = url::UrlDecode(name);
|
||||
SpacesToUnderscore(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
void Ge0Parser::SpacesToUnderscore(std::string & name)
|
||||
{
|
||||
for (size_t i = 0; i < name.size(); ++i)
|
||||
if (name[i] == ' ')
|
||||
name[i] = '_';
|
||||
else if (name[i] == '_')
|
||||
name[i] = ' ';
|
||||
}
|
||||
|
||||
void Ge0Parser::ValidateName(std::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 Ge0Parser::IsHexChar(char const a)
|
||||
{
|
||||
return ((a >= '0' && a <= '9') || (a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f'));
|
||||
}
|
||||
|
||||
std::string DebugPrint(Ge0Parser::Result const & r)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "ParseResult [";
|
||||
oss << "zoom=" << r.m_zoomLevel << ", ";
|
||||
oss << "lat=" << r.m_lat << ", ";
|
||||
oss << "lon=" << r.m_lon << ", ";
|
||||
oss << "name=" << r.m_name << "]";
|
||||
return oss.str();
|
||||
}
|
||||
} // namespace ge0
|
||||
Loading…
Add table
Add a link
Reference in a new issue