diff --git a/platform/measurement_utils.cpp b/platform/measurement_utils.cpp index b29d17f03d..5ba0a7cf5d 100644 --- a/platform/measurement_utils.cpp +++ b/platform/measurement_utils.cpp @@ -3,9 +3,12 @@ #include "geometry/mercator.hpp" -#include "base/string_utils.hpp" +#include "base/macros.hpp" #include "base/math.hpp" +#include "base/stl_add.hpp" +#include "base/string_utils.hpp" +#include "std/cstring.hpp" #include "std/iomanip.hpp" #include "std/sstream.hpp" @@ -189,4 +192,97 @@ string FormatSpeed(double metersPerSecond) return res; } +bool OSMDistanceToMeters(string const & osmRawValue, double & outMeters) +{ + char * stop; + char const * s = osmRawValue.c_str(); + outMeters = strtod(s, &stop); + + // Not a number, was not parsed at all. + if (s == stop) + return false; + + if (!isfinite(outMeters)) + return false; + + switch (*stop) + { + // Default units - meters. + case 0: return true; + + // Feet and probably inches. + case '\'': + { + outMeters = FeetToMeters(outMeters); + s = stop + 1; + double const inches = strtod(s, &stop); + if (s != stop && *stop == '"' && isfinite(inches)) + outMeters += InchesToMeters(inches); + return true; + } + break; + + // Inches. + case '\"': outMeters = InchesToMeters(outMeters); return true; + + // It's probably a range. Use maximum value (if possible) for a range. + case '-': + { + s = stop + 1; + double const newValue = strtod(s, &stop); + if (s != stop && isfinite(newValue)) + outMeters = newValue; + } + break; + + // It's probably a list. Use maximum value (if possible) for a list. + case ';': + do + { + s = stop + 1; + double const newValue = strtod(s, &stop); + if (s == stop) + break; + if (isfinite(newValue)) + outMeters = newValue; + } while (*stop && *stop == ';'); + break; + } + + while (*stop && isspace(*stop)) + ++stop; + + // Default units - meters. + if (*stop == 0) + return true; + + if (strstr(stop, "nmi") == stop) + outMeters = NauticalMilesToMeters(outMeters); + else if (strstr(stop, "mi") == stop) + outMeters = MilesToMeters(outMeters); + else if (strstr(stop, "ft") == stop) + outMeters = FeetToMeters(outMeters); + else if (strstr(stop, "feet") == stop) + outMeters = FeetToMeters(outMeters); + else if (strstr(stop, "km") == stop) + outMeters = outMeters * 1000; + + // Count all other cases as meters. + return true; +} + +string OSMDistanceToMetersString(string const & osmRawValue, + bool supportZeroAndNegativeValues, + int digitsAfterComma) +{ + double meters; + if (OSMDistanceToMeters(osmRawValue, meters)) + { + if (!supportZeroAndNegativeValues && meters <= 0) + return {}; + return strings::to_string_dac(meters, digitsAfterComma); + } + return {}; +} + } // namespace MeasurementUtils diff --git a/platform/measurement_utils.hpp b/platform/measurement_utils.hpp index 0fe8c4c257..1da8145ec7 100644 --- a/platform/measurement_utils.hpp +++ b/platform/measurement_utils.hpp @@ -12,6 +12,8 @@ inline double MilesToMeters(double mi) { return mi * 1609.344; } inline double MetersToFeet(double m) { return m * 3.2808399; } inline double FeetToMeters(double ft) { return ft * 0.3048; } inline double FeetToMiles(double ft) { return ft * 5280; } +inline double InchesToMeters(double in) { return in / 39.370; } +inline double NauticalMilesToMeters(double nmi) { return nmi * 1852; } /// Takes into an account user settings [metric, imperial] /// @param[in] m meters @@ -37,4 +39,13 @@ void FormatLatLon(double lat, double lon, string & latText, string & lonText, in string FormatMercator(m2::PointD const & mercator, int dac = 6); void FormatMercator(m2::PointD const & mercator, string & lat, string & lon, int dac = 6); +/// Converts OSM distance (height, ele etc.) to meters. +/// @returns false if fails. +bool OSMDistanceToMeters(string const & osmRawValue, double & outMeters); +/// Converts OSM distance (height, ele etc.) to meters string. +/// @returns empty string on failure. +string OSMDistanceToMetersString(string const & osmRawValue, + bool supportZeroAndNegativeValues = true, + int digitsAfterComma = 2); + } diff --git a/platform/platform_tests/measurement_tests.cpp b/platform/platform_tests/measurement_tests.cpp index ebca1c103a..6129601300 100644 --- a/platform/platform_tests/measurement_tests.cpp +++ b/platform/platform_tests/measurement_tests.cpp @@ -101,3 +101,58 @@ UNIT_TEST(FormatSpeed) TEST_EQUAL(FormatSpeed(10), "36km/h", ()); TEST_EQUAL(FormatSpeed(1), "3.6km/h", ()); } + +UNIT_TEST(OSMDistanceToMetersString) +{ + TEST_EQUAL(OSMDistanceToMetersString(""), "", ()); + TEST_EQUAL(OSMDistanceToMetersString("INF"), "", ()); + TEST_EQUAL(OSMDistanceToMetersString("NAN"), "", ()); + TEST_EQUAL(OSMDistanceToMetersString("not a number"), "", ()); + TEST_EQUAL(OSMDistanceToMetersString("10й"), "10", ()); + TEST_EQUAL(OSMDistanceToMetersString("0"), "0", ()); + TEST_EQUAL(OSMDistanceToMetersString("0.0"), "0", ()); + TEST_EQUAL(OSMDistanceToMetersString("0.0000000"), "0", ()); + TEST_EQUAL(OSMDistanceToMetersString("22.5"), "22.5", ()); + TEST_EQUAL(OSMDistanceToMetersString("+21.5"), "21.5", ()); + TEST_EQUAL(OSMDistanceToMetersString("1e+2"), "100", ()); + TEST_EQUAL(OSMDistanceToMetersString("5 метров"), "5", ()); + TEST_EQUAL(OSMDistanceToMetersString(" 4.4 "), "4.4", ()); + TEST_EQUAL(OSMDistanceToMetersString("8-15"), "15", ()); + TEST_EQUAL(OSMDistanceToMetersString("8-15 ft"), "4.57", ()); + TEST_EQUAL(OSMDistanceToMetersString("8-й километр"), "8", ()); + TEST_EQUAL(OSMDistanceToMetersString("8;9;10"), "10", ()); + TEST_EQUAL(OSMDistanceToMetersString("8;9;10 meters"), "10", ()); + TEST_EQUAL(OSMDistanceToMetersString("8;9;10 km"), "10000", ()); + TEST_EQUAL(OSMDistanceToMetersString("10;20!111"), "20", ()); + TEST_EQUAL(OSMDistanceToMetersString("10;20\""), "20", ()); + TEST_EQUAL(OSMDistanceToMetersString("-100.3"), "-100.3", ()); + TEST_EQUAL(OSMDistanceToMetersString("99.0000000"), "99", ()); + TEST_EQUAL(OSMDistanceToMetersString("8900.000023"), "8900", ()); + TEST_EQUAL(OSMDistanceToMetersString("-300.9999"), "-301", ()); + TEST_EQUAL(OSMDistanceToMetersString("-300.9"), "-300.9", ()); + TEST_EQUAL(OSMDistanceToMetersString("15 m"), "15", ()); + TEST_EQUAL(OSMDistanceToMetersString("15.9 m"), "15.9", ()); + TEST_EQUAL(OSMDistanceToMetersString("15.9m"), "15.9", ()); + TEST_EQUAL(OSMDistanceToMetersString("3000 ft"), "914.4", ()); + TEST_EQUAL(OSMDistanceToMetersString("3000ft"), "914.4", ()); + TEST_EQUAL(OSMDistanceToMetersString("100 feet"), "30.48", ()); + TEST_EQUAL(OSMDistanceToMetersString("100feet"), "30.48", ()); + TEST_EQUAL(OSMDistanceToMetersString("3 nmi"), "5556", ()); + TEST_EQUAL(OSMDistanceToMetersString("3 mi"), "4828.03", ()); + TEST_EQUAL(OSMDistanceToMetersString("3.3 km"), "3300", ()); + TEST_EQUAL(OSMDistanceToMetersString("105'"), "32", ()); + TEST_EQUAL(OSMDistanceToMetersString("11'"), "3.35", ()); + TEST_EQUAL(OSMDistanceToMetersString("11 3\""), "11", ()); + TEST_EQUAL(OSMDistanceToMetersString("11 3'"), "11", ()); + TEST_EQUAL(OSMDistanceToMetersString("11\"'"), "0.28", ()); + TEST_EQUAL(OSMDistanceToMetersString("11'\""), "3.35", ()); + TEST_EQUAL(OSMDistanceToMetersString("11'4\""), "3.45", ()); + TEST_EQUAL(OSMDistanceToMetersString("11\""), "0.28", ()); + TEST_EQUAL(OSMDistanceToMetersString("100 \""), "100", ()); + + TEST_EQUAL(OSMDistanceToMetersString("0", false), "", ()); + TEST_EQUAL(OSMDistanceToMetersString("-15", false), "", ()); + TEST_EQUAL(OSMDistanceToMetersString("15.12345", false, 1), "15.1", ()); + TEST_EQUAL(OSMDistanceToMetersString("15.123", false, 4), "15.123", ()); + TEST_EQUAL(OSMDistanceToMetersString("15.654321", true, 1), "15.7", ()); +}