diff --git a/search/latlon_match.cpp b/search/latlon_match.cpp
index 95741a4cca..558047f6f1 100644
--- a/search/latlon_match.cpp
+++ b/search/latlon_match.cpp
@@ -1,278 +1,590 @@
 #include "search/latlon_match.hpp"
 
-#include "base/macros.hpp"
-
-#include <algorithm>
-#include <array>
-#include <cmath>
-#include <cstdlib>
-#include <cstring>
-#include <iterator>
+#include <memory>
 #include <string>
-#include <utility>
+#include <optional>
+#include <list>
+#include <array>
+#include <cstring>
+#include <functional>
 
-using namespace std;
-
-namespace
-{
-string const kSpaces = " \t";
-string const kCharsToSkip = " \n\t,;:.()";
-string const kDecimalMarks = ".,";
-
-bool IsDecimalMark(char c)
-{
-  return kDecimalMarks.find(c) != string::npos;
-}
-
-template <typename Char>
-void SkipSpaces(Char *& s)
-{
-  while (kSpaces.find(*s) != string::npos)
-    ++s;
-}
-
-template <typename Char>
-void Skip(Char *& s)
-{
-  while (kCharsToSkip.find(*s) != string::npos)
-    ++s;
-}
-
-bool MatchDMSArray(char const * & s, char const * arr[], size_t count)
-{
-  for (size_t i = 0; i < count; ++i)
+namespace {
+// The Check class is an auxiliary class, the purpose of which is to perform
+// various kinds of checks during the operation of the module.
+class Check {
+ public:
+  static std::size_t IsDegree(char const * pos)
   {
-    size_t const len = strlen(arr[i]);
-    if (strncmp(s, arr[i], len) == 0)
+    return CheckSymbol(m_degrees, pos);
+  }
+  static std::size_t IsMinute(char const * pos)
+  {
+    return CheckSymbol(m_minutes, pos);
+  }
+  static std::size_t IsSecond(char const * pos)
+  {
+    return CheckSymbol(m_seconds, pos);
+  }
+  // The IsCardDir function checks whether the character, pointed to by the |pos|,
+  // is symbol of cardinal direction. If so, the function returns true,
+  // otherwise false.
+  static bool IsCardDir(char const * pos)
+  {
+    return m_cardinalDirections.find(*pos) != std::string_view::npos;
+  }
+  // The IsNegative function checks whether the character, pointed to by the |pos|,
+  // is the beginning of a negative number. If so, the function returns true,
+  // otherwise false.
+  static bool IsNegative(char const * pos)
+  {
+    return *pos == '-' && std::isdigit(*(pos + 1));
+  }
+  // The IsInteger function checks whether the |value| has a fractional part.
+  // If so, the function returns false, otherwise true.
+  static bool IsInteger(double value)
+  {
+    return value == static_cast<int>(value);
+  }
+  // The Latitude function checks whether the |lat| variable meets the latitude
+  // requirements. If so, the function returns true, otherwise false.
+  static bool Latitude(double lat)
+  {
+    return (lat < -90.0 || lat > 90.0) ? false : true;
+  }
+  // The Longitude function checks whether the |lon| variable meets the longitude
+  // requirements. If so, the function returns true, otherwise false.
+  static bool Longitude(double lon)
+  {
+    return (lon < -180.0 || lon > 180.0) ? false : true;
+  }
+ private:
+  template<std::size_t Size>
+  using Array = std::array<char const *, Size>;
+  // The CheckSymbol function checks whether character, pointed to by |pos|, belongs
+  // to the set of characters, contained in the array |symbols|. The function returns
+  // the size of the character, if it is present, otherwise zero.
+  template<std::size_t Size>
+  static std::size_t CheckSymbol(Array<Size> const & symbols, char const * pos)
+  {
+    for (char const * symb : symbols)
     {
-      s += len;
+      size_t const len = strlen(symb);
+      if (strncmp(pos, symb, len) == 0)
+        return len;
+    }
+    return 0;
+  }
+  static constexpr Array<2> m_degrees = { "*", "°" };
+  static constexpr Array<3> m_minutes = { "\'", "’", "′" };
+  static constexpr Array<6> m_seconds = { "\"", "”", "″", "\'\'", "’’", "′′" };
+  static constexpr std::string_view m_cardinalDirections = "NnSsEeWw";
+};
+
+enum class Index
+{
+  kDegree,
+  kMinute,
+  kSecond,
+  kCardDir
+};
+
+// The CoordinatesFormat class is an abstract class that provides an interface for
+// interacting with its successors as well as functionality common to them. In order
+// to be successfully integrated, each new coordinate format must inherit this class.
+class CoordinatesFormat {
+  using CoordinatePair = std::optional<std::pair<double, double>>;
+ public:
+  CoordinatesFormat() : m_start(nullptr) { }
+  virtual ~CoordinatesFormat() = default;
+  // The SetStart function sets the starting point in the query for coordinate parser.
+  void SetStart(char const * start)
+  {
+    m_start = start;
+  }
+  // The InterpretNumber function interprets a floating point value in a byte string
+  // pointed to by |start|. The function returns two values: floating point value,
+  // corresponding to the contents of |start| and the number of characters that was
+  // interpreted.
+  std::pair<double, std::size_t> InterpretNumber(char const * const start)
+  {
+    char * end;
+    double number = std::strtod(start, &end);
+    if (*end == ',' && std::isdigit(*(end + 1)))
+    {
+      char* another_start = ++end;
+      double fractional_part = std::strtod(another_start, &end);
+      while (fractional_part >= 1)
+        fractional_part /= 10;
+      number += number > 0 ? fractional_part : -fractional_part;
+    }
+    return {number, end - start};
+  }
+  // The Parse function is pure virtual method, whose implementation in subclasses
+  // performs query analysis.
+  virtual void Parse() = 0;
+  // The Format function is pure virtual method, whose implementation in subclasses
+  // checks whether it is possible to analyze the byte string pointed to by the |pos|.
+  // The function returns true if the byte string pointed to by the |pos| corresponds
+  // to the characteristic features of the coordinate format, otherwise false.
+  virtual bool Format(char const * pos) = 0;
+  // The Results The function returns the results of the analysis in case of its
+  // successful completion, otherwise uninitialized std::optional object.
+  virtual CoordinatePair Results() = 0;
+ protected:
+  CoordinatePair Results(std::optional<double> lat, std::optional<double> lon)
+  {
+    if (lat && lon && Check::Latitude(lat.value()) && Check::Longitude(lon.value()))
+      return std::make_pair(lat.value(), lon.value());
+    else
+      return std::nullopt;
+  }
+  char const * StartPos()
+  {
+    return m_start;
+  }
+ private:
+  // The |m_start| field holds the position in the query with starting from which
+  // the analysis is performed.
+  char const * m_start;
+};
+
+
+// The DDFormat class provides functionality for parsing coordinates in Decimal
+// Degrees (DD) format. The implemented format has the following form:
+//
+//  dd, DD
+//
+//  -> dd is a latitude in a range [-90,90] (real number);
+//  -> DD is a longitude in a range [-180,180] or [0,360] (real number).
+// The longitude ranges are correlated as follows:
+// [   0, 360]:  180,  181, ..., 359, 360/0, 1, ..., 179, 180
+// [-180, 180]: -180, -179, ...,  -1,     0, 1, ..., 179, 180
+class DDFormat : public CoordinatesFormat {
+ public:
+  DDFormat() : m_lat(std::nullopt), m_lon(std::nullopt), m_in_process(&m_lat) { }
+  void Parse() override
+  {
+    for (auto iter = StartPos(); *iter != '\0';)
+    {
+      std::size_t shift = 1;
+      if (m_in_process)
+      {
+        if (std::isdigit(*iter) || Check::IsNegative(iter))
+          std::tie(*m_in_process, shift) = InterpretNumber(iter);
+        else if (*iter == ' ' || *iter == ',')
+          m_in_process = m_lon ? nullptr : &m_lon;
+        else if (std::size_t size = Check::IsDegree(iter); size != 0)
+          shift = size;
+        else if (!m_lat || !m_lon || Check::IsCardDir(iter) || Check::IsMinute(iter) || Check::IsSecond(iter))
+          return Reset();
+      } else if (std::isdigit(*iter))
+        return Reset();
+      iter += shift;
+    }
+    if (m_lon > 180.0)
+      m_lon.value() -= 360.0;
+  }
+  std::optional<std::pair<double, double>> Results() override
+  {
+    return CoordinatesFormat::Results(m_lat, m_lon);
+  }
+  bool Format(char const * pos) override
+  {
+    return Check::IsNegative(pos) || std::isdigit(*pos);
+  }
+ private:
+  void Reset()
+  {
+    std::tie(m_lat, m_lon) = std::tie(std::nullopt, std::nullopt);
+  }
+  std::optional<double> m_lat;  // The |m_lat| field holds the latitude value during parsing.
+  std::optional<double> m_lon;  // The |m_lon| field holds the longitude value dirung parsing.
+  std::optional<double> * m_in_process; // The |m_in_process| points to a coordinate that is
+                                        // in the process of parsing.
+};
+
+// The AbstractDmsDdm class is the abstract base class, providing functionality common to
+// parsers of Degrees, Minutes, Seconds (DMS) and Degrees, Decimal Minutes (DDM) coordinate
+// formates.
+class AbstractDmsDdm : public CoordinatesFormat {
+ protected:
+  // The Component class represents a separate component from which the DMS or DDM notations
+  // are built.
+  class Component {
+   public:
+    Component(Index index, int ratio, std::function<bool(double)>&& check)
+        : m_index(index), m_ratio(ratio), m_check(check) { }
+    virtual ~Component() = default;
+    bool HasValue() const
+    {
+      return m_value.has_value();
+    }
+    bool SetValue(double value)
+    {
+      if (!m_check(value))
+        return false;
+      m_value = std::make_optional(value);
       return true;
     }
+    double Compute() const
+    {
+      return m_value.value() / m_ratio;
+    }
+    bool operator==(Index const index) const
+    {
+      return index == m_index;
+    }
+   private:
+    // The |m_index| field holds the identifier of the component that is used to address
+    // this component during runtime.
+    Index const m_index;
+    // The |m_value| field holds the value of the component.
+    std::optional<double> m_value;
+    // The |m_ratio| field holds the value of the multiplier that is used in the calculation
+    // of the coordinate.
+    int const m_ratio;
+    // The |m_check| field holds a functor which checks whether component corresponds to
+    // a specific notation. If so, the functor returns true, otherwise false.
+    std::function<bool(double)> const m_check;
+  };
+ private:
+  // The Coordinate class builds individual instances of the Component class into
+  // a structure typical of DMS or DDM notation.
+  class Coordinate {
+    using CompPtr = std::unique_ptr<Component>;
+    using CompList = std::list<CompPtr>;
+   public:
+    template<typename...Ts>
+    Coordinate(Ts... ts)
+    {
+      (m_list.push_back(std::make_unique<Ts>(ts)), ...);
+    }
+    template<typename T>
+    bool SetValue(T value, Index index)
+    {
+      if constexpr (std::is_floating_point_v<T>) {
+        if (auto iter = Find(index); iter != m_list.end())
+        {
+          Complete(iter);
+          return (*iter)->SetValue(value);
+        }
+      } else
+        card_dir = std::make_optional(value);
+      return true;
+    }
+    // The HasValue function checks whether a component with the index |index| contains
+    // value. The function returns true if the component contains a value, otherwise
+    // false.
+    bool HasValue(Index index)
+    {
+      if (auto iter = Find(index); iter != m_list.end())
+        return (*iter)->HasValue();
+      return card_dir.has_value();
+    }
+    bool IsLatitude() const
+    {
+      return card_dir == 'N' || card_dir == 'S';
+    }
+    bool IsLongitude() const
+    {
+      return card_dir == 'W' || card_dir == 'E';
+    }
+    // The Calculate function, basing on the information obtained during the parsing,
+    // translates the coordinate into Decimal degrees format. The result is recorded
+    // in the |m_result| field.
+    void Calculate()
+    {
+      for (auto& ptr : m_list) {
+        if (ptr->HasValue())
+        {
+          if (m_result)
+            m_result.value() += m_result < 0 ? -ptr->Compute() : ptr->Compute();
+          else
+            m_result = ptr->Compute();
+        }
+      }
+      if (m_result && (card_dir == 'S' || card_dir == 'W'))
+        m_result.value() = -m_result.value();
+    }
+    std::optional<double> Result() const
+    {
+      return m_result;
+    }
+    // The |card_dir| field holds a symbol indicating the cardinal directions.
+    std::optional<char> card_dir;
+   private:
+    // The Complete function verifies that the components preceding the |comp|
+    // have a value. If the previous component does not have a value, then it is
+    // assigned zero. The function stops when a component that has a value is
+    // reached or if the first component of the coordinate is reached.
+    void Complete(CompList::iterator comp)
+    {
+      if (comp != m_list.begin())
+      {
+        for (auto iter = std::prev(comp); !(*iter)->HasValue();)
+        {
+          (*iter)->SetValue(0);
+          if (iter != m_list.begin())
+            --iter;
+        }
+      }
+    }
+    // The Find function searches for a coordinate component with an index |index|.
+    // If a component with the specified index is found, the function returns
+    // an iterator to it, otherwise an iterator, pointing to the end of the |m_list|.
+    CompList::iterator Find(Index const index)
+    {
+      auto predicate = [index] (CompPtr const & c) { return *c == index; };
+      return std::find_if(m_list.begin(), m_list.end(), std::move(predicate));
+    }
+    // The |m_list| field holds instances of successors of the Component class.
+    std::list<std::unique_ptr<Component>> m_list;
+    std::optional<double> m_result;
+  };
+ public:
+  template<typename... Ts>
+  AbstractDmsDdm(Ts... components) : m_number(0), m_first(Coordinate(components...)),
+      m_second(Coordinate(components...)), m_inProcess(&m_first) { }
+  void Parse() override
+  {
+    char const * iter = StartPos();
+    while (*iter != '\0') {
+      std::size_t shift = 1;
+      if (std::isdigit(*iter) || Check::IsNegative(iter))
+        std::tie(m_number, shift) = InterpretNumber(iter);
+      else if (*iter == ',' && *(iter + 1) == ' ' && m_inProcess == &m_first)
+        m_inProcess = &m_second;
+      else if ((*iter == ' ' || *iter == ',') && m_number)
+        return;
+      else if (Check::IsCardDir(iter)) {
+        if (!AssignValue(std::toupper(*iter), Index::kCardDir))
+          return;
+      } else if (auto is_format = FormatCheck(iter); is_format) {
+        Index index;
+        std::tie(shift, index) = is_format.value();
+        if (!AssignValue(m_number, index))
+          return;
+      } else if (Interrupt(iter))
+        return;
+      iter += shift;
+    }
+    if (m_number != 0)
+      return;
+    Process();
+  }
+  bool Format(char const * pos) override
+  {
+    return Check::IsCardDir(pos) || Check::IsNegative(pos) || std::isdigit(*pos);
+  }
+  std::optional<std::pair<double, double>> Results() override
+  {
+    return CoordinatesFormat::Results(m_first.Result(), m_second.Result());
+  }
+  void Process()
+  {
+    if (!m_first.card_dir && !m_second.card_dir) {
+      m_first.card_dir = 'N';
+      m_second.card_dir = 'E';
+    }
+    if (m_first.card_dir && m_second.card_dir) {
+      if (m_first.IsLongitude() && m_second.IsLatitude())
+        std::swap(m_first, m_second);
+      if (m_first.IsLatitude() && m_second.IsLongitude()) {
+        m_first.Calculate();
+        m_second.Calculate();
+      }
+    }
+  }
+ private:
+  // The AssignValue function template assigns value |value| to the component |index|
+  // of the coordinate. The function returns true if the value was assigned successfully,
+  // otherwise false.
+  template<typename T>
+  bool AssignValue(T value, Index index)
+  {
+    if (m_inProcess->HasValue(index))
+    {
+      if (m_inProcess == &m_first)
+        m_inProcess = &m_second;
+      else if (m_inProcess == &m_second) {
+        if constexpr (std::is_integral_v<T>)
+          return true;
+        return false;
+      }
+    }
+    if (!m_inProcess->SetValue(value, index))
+      return false;
+    if constexpr (std::is_floating_point_v<T>)
+      m_number = 0;
+    return true;
+  }
+  // The Interrupt function defines the conditions (if any) under which the parsing process
+  // should be terminated. Parsing stops if the function returns true, otherwise the operation
+  // continues.
+  virtual bool Interrupt(char const * pos) = 0;
+  // The FormatCheck function checks whether the character in the byte string pointed to by
+  // the |pos| corresponds to characters of the DMS or DDM notations. The function returns
+  // an uninitialized std::optional object if the symbol does not correspond to characters
+  // of the DMS or DDM notations, otherwise the function returns the size of the symbol and
+  // its index.
+  virtual std::optional<std::pair<std::size_t, Index>> FormatCheck(char const * pos) = 0;
+  // The |m_number| field holds the value of the coordinate component that is being processed.
+  double m_number;
+  // The |m_first| and |m_second| fields hold the information that is obtained during parsing.
+  Coordinate m_first;
+  Coordinate m_second;
+  Coordinate* m_inProcess;  // The |m_in_process| field holds an address of the Coordinate
+                            // object, to which the information is written.
+};
+
+// The DMSFormat class provides functionality for parsing coordinates in Degrees, Minutes,
+// Seconds (DMS) format. The DMS format has the following form:
+//
+//  dd* MM' SS" N, DD* MM' SS" E
+//
+//  -> dd is in a range [0,90] and DD is in a range [0,180] (both whole numbers);
+//  -> MM is in a range [0,59] (whole number);
+//  -> SS is in a range [0,60) (real number).
+class DMSFormat : public AbstractDmsDdm {
+  struct Degree : Component {
+    Degree() : Component(Index::kDegree, 1, [] (double v) { return v >= 0 && v <= 180 && Check::IsInteger(v); }) { }
+  };
+  struct Minute : Component {
+    Minute() : Component(Index::kMinute, 60, [] (double v) { return v >= 0 && v <= 59 && Check::IsInteger(v); }) { }
+  };
+  struct Second : Component {
+    Second() : Component(Index::kSecond, 3600, [] (double v) { return v >= 0 && v < 60; }) { }
+  };
+ public:
+  DMSFormat() : AbstractDmsDdm(Degree(), Minute(), Second()) { }
+ private:
+  bool Interrupt(char const *) override
+  {
+    return false;
+  }
+  std::optional<std::pair<std::size_t, Index>> FormatCheck(char const * pos) override
+  {
+    if (std::size_t length = Check::IsDegree(pos); length)
+      return std::make_pair(length, Index::kDegree);
+    else if (std::size_t length = Check::IsSecond(pos); length)
+      return std::make_pair(length, Index::kSecond);
+    else if (std::size_t length = Check::IsMinute(pos); length)
+      return std::make_pair(length, Index::kMinute);
+    else
+      return std::nullopt;
+  }
+};
+
+// The DDMFormat class provides functionality for parsing coordinates in Degrees, Decimal
+// Minutes (DMM) format. The DMM format has the following form:
+//
+//  dd* MM' N, DD* MM' E
+//
+//  -> dd is in a range [0,90] and DD is in a range [0,180] (both whole numbers);
+//  -> MM is in a range [0,60) (real number).
+class DDMFormat : public AbstractDmsDdm {
+  struct Degree : Component {
+    Degree() : Component(Index::kDegree, 1, [] (double v) { return v >= 0 && v <= 180 && Check::IsInteger(v); }) { }
+  };
+  struct Minute : Component {
+    Minute() : Component(Index::kMinute, 60, [] (double v) { return v >= 0 && v <= 60; }) { }
+  };
+ public:
+  DDMFormat() : AbstractDmsDdm(Degree(), Minute()) { }
+ private:
+  bool Interrupt(char const * pos) override
+  {
+    return Check::IsSecond(pos);
+  }
+  std::optional<std::pair<std::size_t, Index>> FormatCheck(char const * pos) override
+  {
+    if (std::size_t length = Check::IsDegree(pos); length)
+      return std::make_pair(length, Index::kDegree);
+    else if (std::size_t length = Check::IsMinute(pos); length)
+      return std::make_pair(length, Index::kMinute);
+    else
+      return std::nullopt;
+  }
+};
+
+// The Parser class selects the most appropriate coordinate format and parses the query
+// according to it. A class, extending the list of supported coordinate formats, should
+// be inherited from the CoordinateFormat class and added as an argument to the invocation
+// of CreateList funtion template.
+class Parser {
+  using FormatPtr = std::unique_ptr<CoordinatesFormat>;
+  using FormatsList = std::list<FormatPtr>;
+  template<typename... Fs>
+  FormatsList CreateList()
+  {
+    FormatsList lst;
+    (lst.push_back(std::make_unique<Fs>()), ...);
+    return lst;
+  }
+ public:
+  Parser(std::string const & query) : m_pos(query.c_str()), m_parser(nullptr),
+      m_formats(CreateList<DDFormat, DMSFormat, DDMFormat>()) { }
+  bool AssumeFormat()
+  {
+    if (m_parser && m_parser->Results())
+      return false;
+    while (!m_formats.empty() && IsAllowed(m_pos))
+    {
+      for (auto iter = m_formats.begin(); iter != m_formats.end(); ++iter)
+      {
+        if ((*iter)->Format(m_pos))
+        {
+          m_parser = std::move(*iter);
+          m_parser->SetStart(m_pos);
+          m_formats.erase(iter);
+          return true;
+        }
+      }
+      ++m_pos;
+    }
+    return false;
+  }
+  void Run()
+  {
+    m_parser->Parse();
+  }
+  std::optional<std::pair<double, double>> Results() const
+  {
+    return m_parser ? m_parser->Results() : std::nullopt;
+  }
+ private:
+  // The IsAllowed function checks whether the character pointed to by the |pos|
+  // can be part of the coordinate. If so, the function returns true, otherwise false.
+  static bool IsAllowed(char const * pos)
+  {
+    return std::ispunct(*pos) || std::isspace(*pos) || std::isdigit(*pos) || Check::IsCardDir(pos);
+  }
+  // The |m_pos| field holds an address of the beginning of the chunk of the query
+  // that should be parsed.
+  char const * m_pos;
+  // The |m_parser| field holds a pointer to the coordinate parser, using at the moment.
+  FormatPtr m_parser;
+  // The variable holds a list of coordinate parsers, using for the query processing.
+  FormatsList m_formats;
+};
+
+std::optional<std::pair<double, double>> MatchLatLonDegree(std::string const & query)
+{
+  Parser parser(query);
+  while (parser.AssumeFormat())
+    parser.Run();
+  return parser.Results();
+}
+} // end of unnamed namespace
+
+namespace search {
+bool MatchLatLonDegree(std::string const & query, double & lat, double & lon)
+{
+  if (auto success = ::MatchLatLonDegree(query); success)
+  {
+    std::tie(lat, lon) = success.value();
+    return true;
   }
   return false;
 }
-
-int GetDMSIndex(char const * & s)
-{
-  char const * arrDegree[] = { "*", "°" };
-  char const * arrMinutes[] = { "\'", "’", "′" };
-  char const * arrSeconds[] = { "\"", "”", "″", "\'\'", "’’", "′′" };
-
-  if (MatchDMSArray(s, arrDegree, ARRAY_SIZE(arrDegree)))
-      return 0;
-  if (MatchDMSArray(s, arrSeconds, ARRAY_SIZE(arrSeconds)))
-    return 2;
-  if (MatchDMSArray(s, arrMinutes, ARRAY_SIZE(arrMinutes)))
-    return 1;
-
-  return -1;
 }
-
-void SkipNSEW(char const * & s, char const * (&arrPos) [4])
-{
-  Skip(s);
-
-  int ind;
-  switch (*s)
-  {
-  case 'N': case 'n': ind = 0; break;
-  case 'S': case 's': ind = 1; break;
-  case 'E': case 'e': ind = 2; break;
-  case 'W': case 'w': ind = 3; break;
-  default: return;
-  }
-
-  arrPos[ind] = s++;
-
-  Skip(s);
-}
-
-// Attempts to read a double from the start of |str|
-// in one of what we assume are two most common forms
-// for lat/lon: decimal digits separated either
-// by a dot or by a comma, with digits on both sides
-// of the separator.
-// If the attempt fails, falls back to std::strtod.
-double EatDouble(char const * str, char ** strEnd)
-{
-  bool gotDigitBeforeMark = false;
-  bool gotMark = false;
-  bool gotDigitAfterMark = false;
-  char const * markPos = nullptr;
-  char const * p = str;
-  while (true)
-  {
-    if (IsDecimalMark(*p))
-    {
-      if (gotMark)
-        break;
-      gotMark = true;
-      markPos = p;
-    }
-    else if (isdigit(*p))
-    {
-      if (gotMark)
-        gotDigitAfterMark = true;
-      else
-        gotDigitBeforeMark = true;
-    }
-    else
-    {
-      break;
-    }
-    ++p;
-  }
-
-  if (gotDigitBeforeMark && gotMark && gotDigitAfterMark)
-  {
-    string const part1(str, markPos);
-    string const part2(markPos + 1, p);
-    *strEnd = const_cast<char *>(p);
-    auto const x1 = atof(part1.c_str());
-    auto const x2 = atof(part2.c_str());
-    return x1 + x2 * pow(10.0, -static_cast<double>(part2.size()));
-  }
-
-  return strtod(str, strEnd);
-}
-}  // namespace
-
-namespace search
-{
-bool MatchLatLonDegree(string const & query, double & lat, double & lon)
-{
-  // should be default initialization (0, false)
-  array<pair<double, bool>, 6> v;
-
-  int base = 0;
-
-  // Positions of N, S, E, W symbols
-  char const * arrPos[] = {nullptr, nullptr, nullptr, nullptr};
-  bool arrDegreeSymbol[] = { false, false };
-
-  char const * const startQuery = query.c_str();
-  char const * s = startQuery;
-  while (true)
-  {
-    char const * s1 = s;
-    SkipNSEW(s, arrPos);
-    if (!*s)
-    {
-      // End of the string - check matching.
-      break;
-    }
-
-    SkipSpaces(s);
-    char * s2;
-    double const x = EatDouble(s, &s2);
-    if (s == s2)
-    {
-      // invalid token
-      if (s == s1)
-      {
-        // Return error if there are no any delimiters.
-        return false;
-      }
-      else
-      {
-        // Check matching if token is delimited.
-        break;
-      }
-    }
-    else if (x < 0 && s == s1 && !(s == startQuery || kSpaces.find(*(s-1)) != string::npos))
-    {
-      // Skip input like "3-8"
-      return false;
-    }
-
-    s = s2;
-    SkipSpaces(s);
-
-    int i = GetDMSIndex(s);
-    bool degreeSymbol = true;
-    if (i == -1)
-    {
-      // try to assign next possible value mark
-      if (arrDegreeSymbol[base / 3])
-      {
-        if (!v[base + 1].second)
-          i = 1;
-        else
-          i = 2;
-      }
-      else
-      {
-        i = 0;
-        degreeSymbol = false;
-      }
-    }
-
-    if (i == 0) // degrees
-    {
-      if (v[base].second)
-      {
-        if (base == 0)
-        {
-          base += 3;
-        }
-        else
-        {
-          // too many degree values
-          return false;
-        }
-      }
-      arrDegreeSymbol[base / 3] = degreeSymbol;
-    }
-    else  // minutes or seconds
-    {
-      if (x < 0.0 || x > 60.0 ||            // minutes or seconds should be in [0, 60] range
-          v[base + i].second ||             // value already exists
-          !v[base].second ||                // no degrees found for value
-          (i == 2 && !v[base + 1].second))  // no minutes for seconds
-      {
-        return false;
-      }
-    }
-
-    v[base + i].first = x;
-    v[base + i].second = true;
-  }
-
-  if (!v[0].second || !v[3].second)
-  {
-    // degree should exist for both coordinates
-    return false;
-  }
-
-  if ((arrPos[0] && arrPos[1]) || (arrPos[2] && arrPos[3]))
-  {
-    // control symbols should match only once
-    return false;
-  }
-
-  // Calculate Lat, Lon with correct sign.
-  lat = fabs(v[0].first) + v[1].first / 60.0 + v[2].first / 3600.0;
-  if (v[0].first < 0.0) lat = -lat;
-
-  lon = fabs(v[3].first) + v[4].first / 60.0 + v[5].first / 3600.0;
-  if (v[3].first < 0.0) lon = -lon;
-
-  if (max(arrPos[0], arrPos[1]) > max(arrPos[2], arrPos[3]))
-    swap(lat, lon);
-
-  if (arrPos[1] != nullptr)
-    lat = -lat;
-  if (arrPos[3] != nullptr)
-    lon = -lon;
-
-  // Valid input ranges for longitude are: [0, 360] or [-180, 180].
-  // We normalize it to [-180, 180].
-  if (lon < -180.0 || lon > 360.0)
-    return false;
-
-  if (lon > 180.0)
-    lon -= 360.0;
-
-  return fabs(lat) <= 90.0;
-}
-}  // namespace search
diff --git a/search/search_tests/latlon_match_test.cpp b/search/search_tests/latlon_match_test.cpp
index 58f7f98290..37d55bb894 100644
--- a/search/search_tests/latlon_match_test.cpp
+++ b/search/search_tests/latlon_match_test.cpp
@@ -60,17 +60,10 @@ UNIT_TEST(LatLon_Degree_Match)
   TEST(!MatchLatLonDegree("34/31", lat, lon), ());
   TEST(!MatchLatLonDegree("34,31", lat, lon), ());
 
-  /// @todo 5E-5 eats as full double here. This is a very fancy case, but anyway ...
   TEST(!MatchLatLonDegree("N5E-5", lat, lon), ());
   TEST(!MatchLatLonDegree("5E-5", lat, lon), ());
-
-  TEST(MatchLatLonDegree("N5W-5", lat, lon), ());
-  TestAlmostEqual(lat, 5);
-  TestAlmostEqual(lon, 5);
-  // Same as "N5 E-5"
-  TEST(MatchLatLonDegree("5 E-5", lat, lon), ());
-  TestAlmostEqual(lat, 5);
-  TestAlmostEqual(lon, -5);
+  TEST(!MatchLatLonDegree("N5W-5", lat, lon), ());
+  TEST(!MatchLatLonDegree("5 E-5", lat, lon), ());
 
   TEST(!MatchLatLonDegree("., .", lat, lon), ());
   TEST(!MatchLatLonDegree("10, .", lat, lon), ());
@@ -85,33 +78,83 @@ UNIT_TEST(LatLon_Degree_Match)
 
   TEST(!MatchLatLonDegree("50* 40*, 30*", lat, lon), ());
 
-  TEST(MatchLatLonDegree("(-50°30\'30\" -49°59\'59\"", lat, lon), ());
+  TEST(MatchLatLonDegree("(S50°30\'30\" W49°59\'59\"", lat, lon), ());
   TestAlmostEqual(lat, -50.50833333333333);
   TestAlmostEqual(lon, -49.99972222222222);
 
-  TEST(!MatchLatLonDegree("50°, 30\"", lat, lon), ());
-  TEST(!MatchLatLonDegree("50\', -50°", lat, lon), ());
+  TEST(MatchLatLonDegree("60* 0\' 0\" N, 0* 0\' 30\" E", lat, lon), ()); // full form, case #1
+  TestAlmostEqual(lat, 60);
+  TestAlmostEqual(lon, 0.0083333333333333332);
+
+  TEST(MatchLatLonDegree("60°, 30\"", lat, lon), ()); // short form, case #1
+  TestAlmostEqual(lat, 60);
+  TestAlmostEqual(lon, 0.0083333333333333332);
+
+  TEST(MatchLatLonDegree("0* 50\' 0\" N, 50° 0\' 0\" E", lat, lon), ()); // full form, case #2
+  TestAlmostEqual(lat, 0.83333333333333337);
+  TestAlmostEqual(lon, 50);
+
+  TEST(MatchLatLonDegree("50\', 50°", lat, lon), ()); // short form, case #2
+  TestAlmostEqual(lat, 0.83333333333333337);
+  TestAlmostEqual(lon, 50);
+
+  TEST(!MatchLatLonDegree("60° 30\"", lat, lon), ()); // if short form is ambiguous, latitude and longitude
+                                                      // should be comma separated
+
   TEST(!MatchLatLonDegree("-90*50\'50\", -50°", lat, lon), ());
 
-  TEST(MatchLatLonDegree("(-89*, 360*)", lat, lon), ());
+  TEST(MatchLatLonDegree("-10,12 -20,12", lat, lon), ());
+  TestAlmostEqual(lat, -10.12);
+  TestAlmostEqual(lon, -20.12);
+
+  TEST(MatchLatLonDegree("-89, 180.5555", lat, lon), ());
+  TestAlmostEqual(lat, -89.0);
+  TestAlmostEqual(lon, -179.4445);
+
+  TEST(MatchLatLonDegree("-89, 181", lat, lon), ());
+  TestAlmostEqual(lat, -89.0);
+  TestAlmostEqual(lon, -179.0);
+
+  TEST(MatchLatLonDegree("-89, 359", lat, lon), ());
+  TestAlmostEqual(lat, -89.0);
+  TestAlmostEqual(lon, -1.0);
+
+
+  TEST(MatchLatLonDegree("-89, 359.5555", lat, lon), ());
+  TestAlmostEqual(lat, -89.0);
+  TestAlmostEqual(lon, -0.4445);
+
+  TEST(MatchLatLonDegree("-89, 359.5555", lat, lon), ());
+  TestAlmostEqual(lat, -89.0);
+  TestAlmostEqual(lon, -0.4445);
+
+  TEST(MatchLatLonDegree("-89, 360", lat, lon), ());
   TestAlmostEqual(lat, -89.0);
   TestAlmostEqual(lon, 0.0);
 
-  TEST(MatchLatLonDegree("-89*15.5\' N; 120*30\'50.5\" e", lat, lon), ());
-  TestAlmostEqual(lat, -89.25833333333333);
-  TestAlmostEqual(lon, 120.51402777777778);
+  // DMS edge cases
+  TEST(MatchLatLonDegree("N 89* 59\' 59.99990\", E 179* 59\' 59.99990\"", lat, lon), ());
+  TestAlmostEqual(lat, 89.9999999722222);
+  TestAlmostEqual(lon, 179.999999972222);
+
+  TEST(MatchLatLonDegree("N 89* 59\' 59.99999\", E 179* 59\' 59.99999\"", lat, lon), ());
+  TestAlmostEqual(lat, 89.9999999972222);
+  TestAlmostEqual(lon, 179.999999997222);
+
+  TEST(MatchLatLonDegree("N 90* 0\' 0\", E 180* 0\' 0\"", lat, lon), ());
+  TestAlmostEqual(lat, 90.0);
+  TestAlmostEqual(lon, 180.0);
+
+  // Fail if degree is negative and minute is floating point
+  TEST(!MatchLatLonDegree("-89*15.5\' N; 120*30\'50.5\" e", lat, lon), ());
 
   TEST(MatchLatLonDegree("N55°45′20.99″ E37°37′03.62″", lat, lon), ());
   TestAlmostEqual(lat, 55.755830555555556);
   TestAlmostEqual(lon, 37.617672222222222);
 
-  {
-    TEST(MatchLatLonDegree("N-55°45′20.99″ E-37°37′03.62″", lat, lon), ());
-    double lat1, lon1;
-    TEST(MatchLatLonDegree("S55°45′20.99″ W37°37′03.62″", lat1, lon1), ());
-    TestAlmostEqual(lat, lat1);
-    TestAlmostEqual(lon, lon1);
-  }
+  TEST(MatchLatLonDegree("S55°45′20.99″ W37°37′03.62″", lat, lon), ());
+  TestAlmostEqual(lat, -55.755830555555556);
+  TestAlmostEqual(lon, -37.617672222222222);
 
   TEST(MatchLatLonDegree("55°45’20.9916\"N, 37°37’3.6228\"E hsdfjgkdsjbv", lat, lon), ());
   TestAlmostEqual(lat, 55.755831);
@@ -137,21 +180,75 @@ UNIT_TEST(LatLon_Degree_Match)
   TEST(!MatchLatLonDegree("55°45′20.9916″W 37°37′3.6228″E", lat, lon), ());
   TEST(!MatchLatLonDegree("N55°45′20.9916″ S37°37′3.6228″", lat, lon), ());
 
-  TEST(MatchLatLonDegree("54° 25' 0N 1° 53' 46W", lat, lon), ());
-  TestAlmostEqual(lat, 54.41666666666667);
-  TestAlmostEqual(lon, -1.89611111111111);
+  TEST(!MatchLatLonDegree("54° 25' 0N 1° 53' 46W", lat, lon), ()); // incomplete form
 
-  TEST(MatchLatLonDegree("47.33471°N 8.53112°E", lat, lon), ());
+  TEST(MatchLatLonDegree("47.33471° 8.53112°", lat, lon), ());
   TestAlmostEqual(lat, 47.33471);
   TestAlmostEqual(lon, 8.53112);
 
-  TEST(MatchLatLonDegree("N 51* 33.217 E 11* 10.113", lat, lon), ());
-  TestAlmostEqual(lat, 51.55361666666667);
-  TestAlmostEqual(lon, 11.16855);
+  TEST(MatchLatLonDegree("N 51* 0\' 33.217\" E 11* 0\' 10.113\"", lat, lon), ());
+  TestAlmostEqual(lat, 51.009226944444443);
+  TestAlmostEqual(lon, 11.002809166666667);
 
   TEST(!MatchLatLonDegree("N 51* 33.217 E 11* 60.113", lat, lon), ());
   TEST(!MatchLatLonDegree("N 51* -33.217 E 11* 10.113", lat, lon), ());
   TEST(!MatchLatLonDegree("N 33.217\' E 11* 10.113", lat, lon), ());
   TEST(!MatchLatLonDegree("N 51* 33.217 E 11* 10.113\"", lat, lon), ());
+
+  // Degrees Decimal Minutes (DDM) tests
+  TEST(MatchLatLonDegree("N 51* 33.217\' E 11* 10.113\'", lat, lon), ());
+  TestAlmostEqual(lat, 51.55361666666667);
+  TestAlmostEqual(lon, 11.16855);
+
+  TEST(MatchLatLonDegree("57* 53.9748\' N, 59* 56.8122\' E", lat, lon), ());
+  TestAlmostEqual(lat, 57.89958);
+  TestAlmostEqual(lon, 59.94687);
+
+  TEST(MatchLatLonDegree("N 57* 53.9748\', E 59* 56.8122\'", lat, lon), ());
+  TestAlmostEqual(lat, 57.89958);
+  TestAlmostEqual(lon, 59.94687);
+
+  TEST(MatchLatLonDegree("S 57* 53.9748\', E 59* 56.8122\'", lat, lon), ());
+  TestAlmostEqual(lat, -57.89958);
+  TestAlmostEqual(lon, 59.94687);
+
+  TEST(MatchLatLonDegree("N 57* 53.9748\', W 59* 56.8122\'", lat, lon), ());
+  TestAlmostEqual(lat, 57.89958);
+  TestAlmostEqual(lon, -59.94687);
+
+  // DDM edge cases
+  TEST(MatchLatLonDegree("N 89* 59,9999990\', E 179* 59,9999990\'", lat, lon), ());
+  TestAlmostEqual(lat,  89.999999983333);
+  TestAlmostEqual(lon, 179.999999983333);
+
+  TEST(MatchLatLonDegree("N 89* 59,9999999\', E 179* 59,9999999\'", lat, lon), ());
+  TestAlmostEqual(lat,  89.999999998333);
+  TestAlmostEqual(lon, 179.999999998333);
+
+  TEST(MatchLatLonDegree("N 90* 0\', E 180* 0\'", lat, lon), ());
+  TestAlmostEqual(lat,  90.0);
+  TestAlmostEqual(lon, 180.0);
+
+
+  // Fail if cardinal direction marks is same
+  TEST(!MatchLatLonDegree("57* 53\' 58.488\"N, 59* 56\' 48.732\"N", lat, lon), ());
+  TEST(!MatchLatLonDegree("57* 53\' 58.488\"S, 59* 56\' 48.732\"S", lat, lon), ());
+  TEST(!MatchLatLonDegree("57* 53\' 58.488\"E, 59* 56\' 48.732\"E", lat, lon), ());
+  TEST(!MatchLatLonDegree("57* 53\' 58.488\"W, 59* 56\' 48.732\"W", lat, lon), ());
+
+
+  // Fail if degree value is floating point (DMS and DDM notation)
+  TEST(!MatchLatLonDegree("57.123* 53\' 58.488\"N, 59* 56\' 48.732\"E", lat, lon), ());
+  TEST(!MatchLatLonDegree("57.123* 58.488\'N, 59* 48.732\'E", lat, lon), ());
+
+  // Fail if minute value is floating point (for DMS notation)
+  TEST(!MatchLatLonDegree("57* 53.123\' 58.488\"N, 59* 56.123\' 48.732\"E", lat, lon), ());
+
+  // Fail if minute value is greater than 59 (for DMS notation)
+  TEST(!MatchLatLonDegree("57* 60\' 58.488\" N, 59* 56\' 48.732\" E", lat, lon), ());
+
+  // Fail if degree value is greater than 180 (DMS and DDM notation)
+  TEST(!MatchLatLonDegree("N 89* 10\' 3\", E 183* 3\' 3\"", lat, lon), ());
+  TEST(!MatchLatLonDegree("N 89* 10\', E 183* 3\'", lat, lon), ());
 }
 }  //  namespace