Skip to content

Commit

Permalink
Merge pull request #6 from vahancho/configurable-precision
Browse files Browse the repository at this point in the history
Make coordinates precision configurable.
  • Loading branch information
vahancho authored Feb 19, 2021
2 parents 620a285 + 29f6a7b commit aeddf24
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 52 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The implementation guarantees to conform with the results of the [Google Interac

## Installation

No installation required. Just compile *polylineencoder.h(.cpp)* in your project and use `gepaf::PolylineEncoder` class.
No installation required. Just compile *polylineencoder.h(.cpp)* in your project and use `gepaf::PolylineEncoder` class template.

## Prerequisites

Expand All @@ -20,7 +20,10 @@ For more details see the CI badges (*Travis CI & AppVeyor CI*).
All code is in `gepaf` namespace. `gepaf` stands for *Google Encoded Polyline Algorithm Format*.

```cpp
gepaf::PolylineEncoder encoder;
// Create an encoder with precision of 5 decimal places (default)
// In order to create objects with other precision use template parameter
// like: gepaf::PolylineEncoder<6>
gepaf::PolylineEncoder<> encoder;

// Poles and equator.
encoder.addPoint(-90.0, -180.0);
Expand All @@ -31,7 +34,7 @@ auto res = encoder.encode(); // "~bidP~fsia@_cidP_gsia@_cidP_gsia@"
encoder.clear(); // Clear the list of points.

// Decode a string using static function.
auto polyline = gepaf::PolylineEncoder::decode("~bidP~fsia@_cidP_gsia@_cidP_gsia@");
auto polyline = gepaf::PolylineEncoder<>::decode("~bidP~fsia@_cidP_gsia@_cidP_gsia@");

// Iterate over all points and print coordinates of each.
for (const auto &point : polyline) {
Expand All @@ -41,7 +44,7 @@ for (const auto &point : polyline) {

## Building and Testing

There are unit tests provided for `PolylineEncoder` class. You can find them in the *test/* directory.
There are unit tests provided for `PolylineEncoder` class template. You can find them in the *test/* directory.
To run them you have to build and run the test application (linking with Google Test library is required). For doing that you can invoke the following commands from the terminal, assuming that compiler and environment are already configured:

##### Linux (gcc)
Expand Down
47 changes: 29 additions & 18 deletions src/polylineencoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

#include "polylineencoder.h"

static const double s_presision = 100000.0;
static const int s_chunkSize = 5;
static const int s_asciiOffset = 63;
static const int s_5bitMask = 0x1f; // 0b11111 = 31
Expand All @@ -38,37 +37,44 @@ static const int s_6bitMask = 0x20; // 0b100000 = 32
namespace gepaf
{

PolylineEncoder::Point::Point(double latitude, double longitude)
: m_latitude(std::round(latitude * s_presision) / s_presision)
, m_longitude(std::round(longitude * s_presision) / s_presision)
template<int Digits>
PolylineEncoder<Digits>::Point::Point(double latitude, double longitude)
: m_latitude (std::round(latitude * Precision::Value) / Precision::Value)
, m_longitude(std::round(longitude * Precision::Value) / Precision::Value)
{
assert(latitude <= 90.0 && latitude >= -90.0);
assert(longitude <= 180.0 && longitude >= -180.0);
}

double PolylineEncoder::Point::latitude() const
template<int Digits>
double PolylineEncoder<Digits>::Point::latitude() const
{
return m_latitude;
}

double PolylineEncoder::Point::longitude() const
template<int Digits>
double PolylineEncoder<Digits>::Point::longitude() const
{
return m_longitude;
}

void PolylineEncoder::addPoint(double latitude, double longitude)
template<int Digits>
void PolylineEncoder<Digits>::addPoint(double latitude, double longitude)
{
m_polyline.emplace_back(latitude, longitude);
}

std::string PolylineEncoder::encode() const
template<int Digits>
std::string PolylineEncoder<Digits>::encode() const
{
return encode(m_polyline);
}

std::string PolylineEncoder::encode(double value)
template<int Digits>
std::string PolylineEncoder<Digits>::encode(double value)
{
int32_t e5 = std::round(value * s_presision); // (2)
int32_t e5 =
std::round(value * Precision::Value); // (2)

e5 <<= 1; // (4)

Expand Down Expand Up @@ -97,7 +103,8 @@ std::string PolylineEncoder::encode(double value)
return result;
}

std::string PolylineEncoder::encode(const PolylineEncoder::Polyline &polyline)
template<int Digits>
std::string PolylineEncoder<Digits>::encode(const PolylineEncoder::Polyline &polyline)
{
std::string result;

Expand All @@ -121,7 +128,8 @@ std::string PolylineEncoder::encode(const PolylineEncoder::Polyline &polyline)
return result;
}

double PolylineEncoder::decode(const std::string &coords, size_t &i)
template<int Digits>
double PolylineEncoder<Digits>::decode(const std::string &coords, size_t &i)
{
assert(i < coords.size());

Expand All @@ -144,13 +152,14 @@ double PolylineEncoder::decode(const std::string &coords, size_t &i)
}
result >>= 1; // (4)

// Convert to decimal value.
return result / s_presision; // (2)
// Convert to decimal value with the given precision.
return result / static_cast<double>(Precision::Value); // (2)
}

PolylineEncoder::Polyline PolylineEncoder::decode(const std::string &coords)
template<int Digits>
typename PolylineEncoder<Digits>::Polyline PolylineEncoder<Digits>::decode(const std::string &coords)
{
PolylineEncoder::Polyline polyline;
PolylineEncoder<Digits>::Polyline polyline;

size_t i = 0;
while (i < coords.size())
Expand Down Expand Up @@ -183,12 +192,14 @@ PolylineEncoder::Polyline PolylineEncoder::decode(const std::string &coords)
return polyline;
}

const PolylineEncoder::Polyline &PolylineEncoder::polyline() const
template<int Digits>
const typename PolylineEncoder<Digits>::Polyline &PolylineEncoder<Digits>::polyline() const
{
return m_polyline;
}

void PolylineEncoder::clear()
template<int Digits>
void PolylineEncoder<Digits>::clear()
{
m_polyline.clear();
}
Expand Down
33 changes: 27 additions & 6 deletions src/polylineencoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ namespace gepaf
For more details refer to the algorithm definition at
https://developers.google.com/maps/documentation/utilities/polylinealgorithm
The implementation guarantees to conform with the results of the Google Interactive
Polyline Encoder Utility (https://developers.google.com/maps/documentation/utilities/polylineutility)
Default implementation (precision of 5 decimal places) guarantees to conform
with the results of the Google Interactive Polyline Encoder Utility
(https://developers.google.com/maps/documentation/utilities/polylineutility)
*/
template<int Digits = 5>
class PolylineEncoder
{
public:
Expand All @@ -49,9 +51,9 @@ class PolylineEncoder
/// Creates a geodetic point with the given coordinates.
/*!
Both latitude and longitude will be rounded to a reasonable precision
of 5 decimal places.
\param latitude The latitude in decimal point degrees. The values are bounded by ±90.0°.
\param longitude The longitude in decimal point degrees. The values are bounded by ±180.0°.
of 5 decimal places (default) or to the number of digits specified by the template parameter..
\param latitude The latitude in decimal point degrees. The values are bounded by ±90.0°.
\param longitude The longitude in decimal point degrees. The values are bounded by ±180.0°.
*/
Point(double latitude, double longitude);

Expand All @@ -72,7 +74,10 @@ class PolylineEncoder
//! Adds new point with the given \p latitude and \p longitude for encoding.
/*!
Note: both latitude and longitude will be rounded to a reasonable precision
of 5 decimal places.
of 5 decimal places (default) or to the number of digits specified by the
template parameter.
\param latitude The latitude in decimal point degrees. The values are bounded by ±90.0°.
\param longitude The longitude in decimal point degrees. The values are bounded by ±180.0°.
*/
void addPoint(double latitude, double longitude);

Expand All @@ -94,6 +99,11 @@ class PolylineEncoder
//! Returns polyline decoded from the given \p coordinates string.
static Polyline decode(const std::string &coordinates);

enum Precision
{
Value = PolylineEncoder<Digits - 1>::Precision::Value * 10
};

private:
//! Encodes a single value according to the compression algorithm.
static std::string encode(double value);
Expand All @@ -105,6 +115,17 @@ class PolylineEncoder
Polyline m_polyline;
};

// A bogus class for compile-time precision calculations.
template<>
class PolylineEncoder<0>
{
public:
enum Precision
{
Value = 1 // 10^0 = 1
};
};

} // namespace

#endif // POLYLINEENCODER_H
Loading

0 comments on commit aeddf24

Please sign in to comment.