Skip to content

Commit

Permalink
[search] Try to parse coordinates from URLs pasted to the search bar.
Browse files Browse the repository at this point in the history
  • Loading branch information
mpimenov authored and tatiana-yan committed Feb 17, 2020
1 parent 0f6a6bf commit d1ab27b
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 47 deletions.
73 changes: 51 additions & 22 deletions coding/url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

#include "geometry/mercator.hpp"

#include <cstddef>
#include <sstream>
#include <regex>

#include "base/assert.hpp"
#include "base/string_utils.hpp"

#include <algorithm>
#include <cstddef>
#include <regex>
#include <sstream>
#include <vector>

using namespace std;

namespace
Expand All @@ -27,57 +29,59 @@ double const kMaxZoom = 17.0;
class LatLonParser
{
public:
explicit LatLonParser(url::GeoURLInfo & info)
LatLonParser(url::Url const & url, url::GeoURLInfo & info)
: m_info(info)
, m_url(url)
, m_regexp("-?\\d+\\.{1}\\d*, *-?\\d+\\.{1}\\d*")
, m_latPriority(-1)
, m_lonPriority(-1)
{
}

url::Url const & GetUrl() const { return m_url; }

bool IsValid() const
{
return m_latPriority == m_lonPriority && m_latPriority != -1;
}

bool operator()(url::Param const & param)
void operator()(url::Param const & param)
{
if (param.m_name == "z" || param.m_name == "zoom")
{
double x;
if (strings::to_double(param.m_value, x))
m_info.SetZoom(x);
return true;
return;
}

int const priority = GetCoordinatesPriority(param.m_name);
if (priority == -1 || priority < m_latPriority || priority < m_lonPriority)
return false;
return;

if (priority != kLatLonPriority)
if (priority != kXYPriority && priority != kLatLonPriority)
{
strings::ForEachMatched(param.m_value, m_regexp, AssignCoordinates(*this, priority));
return true;
return;
}

double x;
if (strings::to_double(param.m_value, x))
{
if (param.m_name == "lat")
if (param.m_name == "lat" || param.m_name == "y")
{
if (!m_info.SetLat(x))
return false;
return;
m_latPriority = priority;
}
else
{
ASSERT_EQUAL(param.m_name, "lon", ());
ASSERT(param.m_name == "lon" || param.m_name == "x", (param.m_name));
if (!m_info.SetLon(x))
return false;
return;
m_lonPriority = priority;
}
}
return true;
}

private:
Expand All @@ -103,19 +107,39 @@ class LatLonParser
return;
VERIFY(strings::to_double(token.substr(n, token.size() - n), lon), ());

SwapIfNeeded(lat, lon);

if (m_parser.m_info.SetLat(lat) && m_parser.m_info.SetLon(lon))
{
m_parser.m_latPriority = m_priority;
m_parser.m_lonPriority = m_priority;
}
}

void SwapIfNeeded(double & lat, double & lon) const
{
vector<string> const kSwappingProviders = {"2gis", "yandex"};
for (auto const & s : kSwappingProviders)
{
if (m_parser.GetUrl().GetPath().find(s) != string::npos)
{
swap(lat, lon);
break;
}
}
}

private:
LatLonParser & m_parser;
int m_priority;
};

inline static int const kLatLonPriority = 5;
// Usually (lat, lon), but some providers use (lon, lat).
inline static int const kLLPriority = 5;
// We do not try to guess the projection and do not interpret (x, y)
// as Mercator coordinates in URLs. We simply use (y, x) for (lat, lon).
inline static int const kXYPriority = 6;
inline static int const kLatLonPriority = 7;

// Priority for accepting coordinates if we have many choices.
// -1 - not initialized
Expand All @@ -125,21 +149,26 @@ class LatLonParser
{
if (token.empty())
return 0;
else if (token == "q")
if (token == "q" || token == "m")
return 1;
else if (token == "daddr")
if (token == "saddr" || token == "daddr")
return 2;
else if (token == "point")
if (token == "sll")
return 3;
else if (token == "ll")
if (token.find("point") != string::npos)
return 4;
else if (token == "lat" || token == "lon")
if (token == "ll")
return kLLPriority;
if (token == "x" || token == "y")
return kXYPriority;
if (token == "lat" || token == "lon")
return kLatLonPriority;

return -1;
}

url::GeoURLInfo & m_info;
url::Url const & m_url;
regex m_regexp;
int m_latPriority;
int m_lonPriority;
Expand Down Expand Up @@ -323,7 +352,7 @@ GeoURLInfo::GeoURLInfo(string const & s)
return;
}

LatLonParser parser(*this);
LatLonParser parser(url, *this);
parser(url::Param(string(), url.GetPath()));
url.ForEachParam(ref(parser));

Expand Down
1 change: 1 addition & 0 deletions feature_list/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ omim_link_libraries(
kml
editor
indexer
ge0
platform
mwm_diff
bsdiff
Expand Down
2 changes: 1 addition & 1 deletion map/map_integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ omim_add_test(${PROJECT_NAME} ${SRC})
omim_link_libraries(
${PROJECT_NAME}
map
ge0
web_api
search_tests_support
editor_tests_support
Expand All @@ -23,6 +22,7 @@ omim_link_libraries(
stats_client
editor
indexer
ge0
metrics
platform
mwm_diff
Expand Down
49 changes: 41 additions & 8 deletions search/processor.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "search/processor.hpp"

#include "ge0/parser.hpp"

#include "search/common.hpp"
#include "search/cuisine_filter.hpp"
#include "search/dummy_rank_table.hpp"
Expand Down Expand Up @@ -35,15 +37,17 @@
#include "indexer/search_string_utils.hpp"
#include "indexer/trie_reader.hpp"

#include "geometry/mercator.hpp"

#include "platform/mwm_traits.hpp"
#include "platform/mwm_version.hpp"
#include "platform/preferred_languages.hpp"

#include "coding/compressed_bit_vector.hpp"
#include "coding/reader_wrapper.hpp"
#include "coding/string_utf8_multilang.hpp"
#include "coding/url.hpp"

#include "geometry/latlon.hpp"
#include "geometry/mercator.hpp"

#include "base/assert.hpp"
#include "base/logging.hpp"
Expand All @@ -53,6 +57,8 @@
#include "base/string_utils.hpp"

#include <algorithm>
#include <set>
#include <sstream>

#include "3party/Alohalytics/src/alohalytics.h"
#include "3party/open-location-code/openlocationcode.h"
Expand Down Expand Up @@ -540,12 +546,39 @@ void Processor::Search(SearchParams const & params)

void Processor::SearchCoordinates()
{
double lat, lon;
if (!MatchLatLonDegree(m_query, lat, lon))
return;
m_emitter.AddResultNoChecks(m_ranker.MakeResult(RankerResult(lat, lon), true /* needAddress */,
true /* needHighlighting */));
m_emitter.Emit();
set<ms::LatLon> seen;
auto const emitUnique = [&](double lat, double lon) {
if (seen.emplace(lat, lon).second)
{
m_emitter.AddResultNoChecks(m_ranker.MakeResult(
RankerResult(lat, lon), true /* needAddress */, true /* needHighlighting */));
m_emitter.Emit();
}
};

{
double lat;
double lon;
if (MatchLatLonDegree(m_query, lat, lon))
emitUnique(lat, lon);
}

istringstream iss(m_query);
string token;
while (iss >> token)
{
double lat;
double lon;
string unusedName;
double unusedZoomLevel;
ge0::Ge0Parser parser;
if (parser.Parse(token, lat, lon, unusedName, unusedZoomLevel))
emitUnique(lat, lon);

url::GeoURLInfo info(token);
if (info.IsValid())
emitUnique(info.m_lat, info.m_lon);
}
}

void Processor::SearchPlusCode()
Expand Down
1 change: 1 addition & 0 deletions search/search_integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ omim_link_libraries(
stats_client
editor
indexer
ge0
platform
mwm_diff
bsdiff
Expand Down
80 changes: 64 additions & 16 deletions search/search_integration_tests/processor_test.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
#include "testing/testing.hpp"

#include "search/cities_boundaries_table.hpp"
#include "search/features_layer_path_finder.hpp"
#include "search/retrieval.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"

#include "generator/feature_builder.hpp"

#include "search/search_tests_support/helpers.hpp"
#include "search/search_tests_support/test_results_matching.hpp"
#include "search/search_tests_support/test_search_request.hpp"

#include "search/cities_boundaries_table.hpp"
#include "search/features_layer_path_finder.hpp"
#include "search/retrieval.hpp"
#include "search/token_range.hpp"
#include "search/token_slice.hpp"

#include "generator/feature_builder.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"

#include "editor/editable_data_source.hpp"

#include "indexer/feature.hpp"
Expand All @@ -31,6 +33,7 @@

#include <cstdint>
#include <string>
#include <tuple>
#include <vector>

using namespace feature;
Expand Down Expand Up @@ -959,17 +962,62 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch)

UNIT_CLASS_TEST(ProcessorTest, TestCoords)
{
auto request = MakeRequest("51.681644 39.183481");
auto const & results = request->Results();
TEST_EQUAL(results.size(), 1, ());
vector<tuple<string, double, double>> tests = {
{"51.681644 39.183481", 51.681644, 39.183481},

{"https://maps.apple.com/maps?ll=30.3345,-81.6648&q=30.3345,-81.6648", 30.3345, -81.6648},
{"https://maps.apple.com/maps?ll=30.3345,-81.6648&q=10.0,10.0", 30.3345, -81.6648},
{"https://maps.apple.com/maps?q=10.0,10.0&ll=30.3345,-81.6648", 30.3345, -81.6648},
{"https://maps.apple.com/maps?q=10.0,10.0&ll=10.0,10.0", 10.0, 10.0},

// The first pair of coordinates in this URL belongs to the selected feature but is harder to
// parse. The second pair (in "m=") is the viewport center.
{"https://2gis.ru/moscow/geo/4504338361754075/"
"37.326747%2C55.481637?m=37.371024%2C55.523592%2F9.69",
55.523592, 37.371024},
{"https://2gis.com.cy/cyprus?m=32.441559%2C34.767296%2F14.58", 34.767296, 32.441559},
{"https://2gis.com.cy/cyprus/geo/70030076127247109/"
"32.431259%2C34.771945?m=32.433265%2C34.770793%2F17.21",
34.770793, 32.433265},

{"https://yandex.ru/maps/?ll=158.828916%2C52.931098&z=9.1", 52.931098, 158.828916},
{"https://yandex.ru/maps/78/petropavlovsk/?ll=158.657810%2C53.024529&z=12.99", 53.024529,
158.657810},
{"https://yandex.ru/maps/78/petropavlovsk/"
"?ll=158.643359%2C53.018729&mode=whatshere&whatshere%5Bpoint%5D=158.643270%2C53.021174&"
"whatshere%5Bzoom%5D=16.07&z=15.65",
53.018729, 158.643359},
{"https://yandex.com.tr/harita/115707/fatih/?ll=28.967470%2C41.008857&z=10", 41.008857,
28.967470},

{"http://ge0.me/kyuh76X_vf/Borgo_Maggiore", 43.941187, 12.447423},
{"ge0://kyuh76X_vf/Borgo_Maggiore", 43.941187, 12.447423},
{"Check out Ospedale di Stato My Places • Hospital "
"http://ge0.me/syujRR7Xgi/Ospedale_di_Stato ge0://syujRR7Xgi/Ospedale_di_Stato",
43.950255, 12.455579},

{"https://en.mapy.cz/zakladni?x=37.5516243&y=55.7638088&z=12", 55.7638088, 37.5516243},
{"https://en.mapy.cz/"
"turisticka?moje-mapy&x=37.6575394&y=55.7253036&z=13&m3d=1&height=10605&yaw=0&pitch=-90&l=0&"
"cat=mista-trasy",
55.7253036, 37.6575394},
};

auto const & result = results[0];
TEST_EQUAL(result.GetResultType(), Result::Type::LatLon, ());
TEST(result.HasPoint(), ());
for (auto const & [query, lat, lon] : tests)
{
auto request = MakeRequest(query);
auto const & results = request->Results();
TEST_EQUAL(results.size(), 1, ());

m2::PointD const expected = mercator::FromLatLon(51.681644, 39.183481);
auto const actual = result.GetFeatureCenter();
TEST(mercator::DistanceOnEarth(expected, actual) <= 1.0, ());
auto const & result = results[0];
TEST_EQUAL(result.GetResultType(), Result::Type::LatLon, ());
TEST(result.HasPoint(), ());

m2::PointD const expected = mercator::FromLatLon(lat, lon);
auto const actual = result.GetFeatureCenter();
auto const dist = mercator::DistanceOnEarth(actual, expected);
TEST(dist <= 1.0, (actual, expected, dist));
}
}

UNIT_CLASS_TEST(ProcessorTest, HotelsFiltering)
Expand Down
Loading

0 comments on commit d1ab27b

Please sign in to comment.