[assessment-tool] Implemented addition of non-found results.

This commit is contained in:
Yuri Gorshenin 2017-05-26 17:09:49 +03:00
parent a580ccc0fd
commit 9af213dc58
19 changed files with 328 additions and 49 deletions

View file

@ -356,6 +356,9 @@ void MapWidget::mouseMoveEvent(QMouseEvent * e)
void MapWidget::mouseReleaseEvent(QMouseEvent * e)
{
if (e->button() == Qt::RightButton)
emit OnContextMenuRequested(e->globalPos());
QOpenGLWidget::mouseReleaseEvent(e);
if (IsLeftButton(e))
m_framework.TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_UP));

View file

@ -36,7 +36,10 @@ public:
void BindSlider(ScaleSlider & slider);
void CreateEngine();
public Q_SLOTS:
signals:
void OnContextMenuRequested(QPoint const & p);
public slots:
void ScalePlus();
void ScaleMinus();
void ScalePlusLight();

View file

@ -49,8 +49,6 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const
{
auto const j = m_goldenMatching[i];
// Some results weren't matched, so they weren't displayed to the
// assessor. But we want to keep them.
if (j == search::Matcher::kInvalidId)
{
auto const & entry = nonFoundEntries[k++];

View file

@ -2,6 +2,7 @@
#include "search/result.hpp"
#include "search/search_quality/assessment_tool/edits.hpp"
#include "search/search_quality/matcher.hpp"
#include "search/search_quality/sample.hpp"
#include "base/string_utils.hpp"
@ -23,6 +24,17 @@ struct Context
{
}
void AddNonFoundResult(search::Sample::Result const & result)
{
CHECK_EQUAL(m_goldenMatching.size(), m_sample.m_results.size(), ());
m_sample.m_results.push_back(result);
m_goldenMatching.push_back(search::Matcher::kInvalidId);
m_nonFoundResults.push_back(result);
m_nonFoundResultsEdits.Add(result.m_relevance);
}
bool HasChanges() const
{
if (!m_initialized)

View file

@ -3,29 +3,37 @@
#include "base/assert.hpp"
// Edits::RelevanceEditor --------------------------------------------------------------------------
Edits::RelevanceEditor::RelevanceEditor(Edits & parent, size_t index)
Edits::Editor::Editor(Edits & parent, size_t index)
: m_parent(parent), m_index(index)
{
}
bool Edits::RelevanceEditor::Set(Relevance relevance)
bool Edits::Editor::Set(Relevance relevance)
{
return m_parent.SetRelevance(m_index, relevance);
}
Edits::Relevance Edits::RelevanceEditor::Get() const
Edits::Relevance Edits::Editor::Get() const
{
return m_parent.Get(m_index).m_curr;
}
bool Edits::RelevanceEditor::HasChanges() const { return m_parent.HasChanges(m_index); }
bool Edits::Editor::HasChanges() const { return m_parent.HasChanges(m_index); }
Edits::Entry::Origin Edits::Editor::GetOrigin() const
{
return m_parent.Get(m_index).m_origin;
}
// Edits -------------------------------------------------------------------------------------------
void Edits::Apply()
{
WithObserver(Update::MakeAll(), [this]() {
for (auto & entry : m_entries)
{
entry.m_orig = entry.m_curr;
entry.m_origin = Entry::Origin::Loaded;
}
m_numEdits = 0;
});
}
@ -34,11 +42,13 @@ void Edits::Reset(std::vector<Relevance> const & relevances)
{
WithObserver(Update::MakeAll(), [this, &relevances]() {
m_entries.resize(relevances.size());
for (size_t i = 0; i < relevances.size(); ++i)
for (size_t i = 0; i < m_entries.size(); ++i)
{
m_entries[i].m_orig = relevances[i];
m_entries[i].m_curr = relevances[i];
m_entries[i].m_deleted = false;
auto & entry = m_entries[i];
entry.m_orig = relevances[i];
entry.m_curr = relevances[i];
entry.m_deleted = false;
entry.m_origin = Entry::Origin::Loaded;
}
m_numEdits = 0;
});
@ -61,6 +71,15 @@ bool Edits::SetRelevance(size_t index, Relevance relevance)
});
}
void Edits::Add(Relevance relevance)
{
auto const index = m_entries.size();
WithObserver(Update::MakeAdd(index), [&]() {
m_entries.emplace_back(relevance, Entry::Origin::Created);
++m_numEdits;
});
}
void Edits::Delete(size_t index)
{
return WithObserver(Update::MakeDelete(index), [this, index]() {
@ -69,10 +88,43 @@ void Edits::Delete(size_t index)
auto & entry = m_entries[index];
CHECK(!entry.m_deleted, ());
entry.m_deleted = true;
++m_numEdits;
switch (entry.m_origin)
{
case Entry::Origin::Loaded: ++m_numEdits; break;
case Entry::Origin::Created: --m_numEdits; break;
}
});
}
void Edits::Resurrect(size_t index)
{
return WithObserver(Update::MakeResurrect(index), [this, index]() {
CHECK_LESS(index, m_entries.size(), ());
auto & entry = m_entries[index];
CHECK(entry.m_deleted, ());
CHECK_GREATER(m_numEdits, 0, ());
entry.m_deleted = false;
switch (entry.m_origin)
{
case Entry::Origin::Loaded: --m_numEdits; break;
case Entry::Origin::Created: ++m_numEdits; break;
}
});
}
Edits::Entry & Edits::GetEntry(size_t index)
{
CHECK_LESS(index, m_entries.size(), ());
return m_entries[index];
}
Edits::Entry const & Edits::GetEntry(size_t index) const
{
CHECK_LESS(index, m_entries.size(), ());
return m_entries[index];
}
std::vector<Edits::Relevance> Edits::GetRelevances() const
{
std::vector<Relevance> relevances(m_entries.size());
@ -101,5 +153,6 @@ bool Edits::HasChanges(size_t index) const
{
CHECK_LESS(index, m_entries.size(), ());
auto const & entry = m_entries[index];
return entry.m_curr != entry.m_orig;
bool result = entry.m_curr != entry.m_orig;
return result;
}

View file

@ -17,11 +17,22 @@ public:
struct Entry
{
enum class Origin
{
Loaded,
Created
};
Entry() = default;
Entry(Relevance relevance, Origin origin)
: m_curr(relevance), m_orig(relevance), m_origin(origin)
{
}
Relevance m_curr = Relevance::Irrelevant;
Relevance m_orig = Relevance::Irrelevant;
bool m_deleted = false;
Origin m_origin = Origin::Loaded;
};
struct Update
@ -32,17 +43,19 @@ public:
{
Single,
All,
Delete
Add,
Delete,
Resurrect
};
Update() = default;
Update(Type type, size_t index): m_type(type), m_index(index) {}
static Update MakeAll() { return Update{}; }
static Update MakeSingle(size_t index) { return Update{Type::Single, index}; }
static Update MakeAdd(size_t index) { return Update{Type::Add, index}; }
static Update MakeDelete(size_t index) { return Update{Type::Delete, index}; }
static Update MakeResurrect(size_t index) { return Update{Type::Resurrect, index}; }
Type m_type = Type::All;
size_t m_index = kInvalidIndex;
@ -50,16 +63,17 @@ public:
using OnUpdate = std::function<void(Update const & update)>;
class RelevanceEditor
class Editor
{
public:
RelevanceEditor(Edits & parent, size_t index);
Editor(Edits & parent, size_t index);
// Sets relevance to |relevance|. Returns true iff |relevance|
// differs from the original one.
bool Set(Relevance relevance);
Relevance Get() const;
bool HasChanges() const;
Entry::Origin GetOrigin() const;
private:
Edits & m_parent;
@ -75,10 +89,19 @@ public:
// |relevance| differs from the original one.
bool SetRelevance(size_t index, Relevance relevance);
// Addes new entry.
void Add(Relevance relevance);
// Marks entry at |index| as deleted.
void Delete(size_t index);
// Resurrects previously deleted entry at |index|.
void Resurrect(size_t index);
std::vector<Entry> const & GetEntries() const { return m_entries; }
Entry & GetEntry(size_t index);
Entry const & GetEntry(size_t index) const;
size_t NumEntries() const { return m_entries.size(); }
std::vector<Relevance> GetRelevances() const;
Entry const & Get(size_t index) const;

View file

@ -31,6 +31,7 @@ using namespace std;
MainModel::MainModel(Framework & framework)
: m_framework(framework)
, m_index(m_framework.GetIndex())
, m_loader(m_index)
, m_contexts(
[this](size_t sampleIndex, Edits::Update const & update) {
OnUpdate(View::ResultType::Found, sampleIndex, update);
@ -92,10 +93,8 @@ void MainModel::SaveAs(string const & path)
CHECK(HasChanges(), ());
CHECK(!path.empty(), ());
search::FeatureLoader loader(m_index);
string contents;
search::Sample::SerializeToJSONLines(m_contexts.MakeSamples(loader), contents);
search::Sample::SerializeToJSONLines(m_contexts.MakeSamples(m_loader), contents);
{
ofstream ofs(path);
@ -147,6 +146,7 @@ void MainModel::OnSampleSelected(int index)
if (results.IsEndedNormal())
{
// Can't use m_loader here due to thread-safety issues.
search::FeatureLoader loader(m_index);
search::Matcher matcher(loader);
@ -224,19 +224,70 @@ void MainModel::OnShowPositionClicked()
bool MainModel::HasChanges() { return m_contexts.HasChanges(); }
bool MainModel::AlreadyInSamples(FeatureID const & id)
{
CHECK(m_selectedSample != kInvalidIndex, ());
CHECK(m_selectedSample < m_contexts.Size(), ());
bool found = false;
ForMatchingEntries(m_contexts[m_selectedSample], id, [&](Edits & edits, size_t index)
{
auto const & entry = edits.GetEntry(index);
if (!entry.m_deleted)
found = true;
});
return found;
}
void MainModel::AddNonFoundResult(FeatureID const & id)
{
CHECK(m_selectedSample != kInvalidIndex, ());
CHECK(m_selectedSample < m_contexts.Size(), ());
auto & context = m_contexts[m_selectedSample];
bool resurrected = false;
ForMatchingEntries(context, id, [&](Edits & edits, size_t index)
{
auto const & entry = edits.GetEntry(index);
CHECK(entry.m_deleted, ());
edits.Resurrect(index);
resurrected = true;
});
if (resurrected)
return;
{
FeatureType ft;
CHECK(m_loader.Load(id, ft), ("Can't load feature:", id));
auto const result = search::Sample::Result::Build(ft, search::Sample::Result::Relevance::Vital);
context.AddNonFoundResult(result);
}
}
void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update)
{
using Type = Edits::Update::Type;
CHECK_LESS(sampleIndex, m_contexts.Size(), ());
auto & context = m_contexts[sampleIndex];
if (update.m_type == Type::Add)
{
CHECK_EQUAL(type, View::ResultType::NonFound, ());
m_view->ShowNonFoundResults(context.m_nonFoundResults,
context.m_nonFoundResultsEdits.GetEntries());
m_view->SetEdits(m_selectedSample, context.m_foundResultsEdits, context.m_nonFoundResultsEdits);
}
m_view->OnResultChanged(sampleIndex, type, update);
m_view->OnSampleChanged(sampleIndex, context.HasChanges());
m_view->OnSamplesChanged(m_contexts.HasChanges());
if (update.m_type == Edits::Update::Type::Delete)
if (update.m_type == Type::Add || update.m_type == Type::Resurrect ||
update.m_type == Type::Delete)
{
CHECK(context.m_initialized, ());
CHECK_EQUAL(type, View::ResultType::NonFound, ());
ShowMarks(context);
}
@ -253,7 +304,7 @@ void MainModel::OnResults(uint64_t timestamp, size_t sampleIndex, search::Result
return;
CHECK_LESS_OR_EQUAL(m_numShownResults, results.GetCount(), ());
m_view->ShowFoundResults(results.begin() + m_numShownResults, results.end());
m_view->AddFoundResults(results.begin() + m_numShownResults, results.end());
m_numShownResults = results.GetCount();
auto & context = m_contexts[sampleIndex];
@ -287,6 +338,7 @@ void MainModel::OnResults(uint64_t timestamp, size_t sampleIndex, search::Result
context.m_initialized = true;
}
m_view->ShowNonFoundResults(context.m_nonFoundResults,
context.m_nonFoundResultsEdits.GetEntries());
ShowMarks(context);
@ -313,3 +365,33 @@ void MainModel::ShowMarks(Context const & context)
m_view->ShowNonFoundResultsMarks(context.m_nonFoundResults,
context.m_nonFoundResultsEdits.GetEntries());
}
template <typename Fn>
void MainModel::ForMatchingEntries(Context & context, FeatureID const & id, Fn && fn)
{
CHECK(context.m_initialized, ());
auto const & foundResults = context.m_foundResults;
CHECK_EQUAL(foundResults.GetCount(), context.m_foundResultsEdits.NumEntries(), ());
for (size_t i = 0; i < foundResults.GetCount(); ++i)
{
auto const & result = foundResults[i];
if (result.GetResultType() != search::Result::RESULT_FEATURE)
continue;
if (result.GetFeatureID() == id)
fn(context.m_foundResultsEdits, i);
}
FeatureType ft;
CHECK(m_loader.Load(id, ft), ("Can't load feature:", id));
search::Matcher matcher(m_loader);
auto const & nonFoundResults = context.m_nonFoundResults;
CHECK_EQUAL(nonFoundResults.size(), context.m_nonFoundResultsEdits.NumEntries(), ());
for (size_t i = 0; i < nonFoundResults.size(); ++i)
{
auto const & result = context.m_nonFoundResults[i];
if (matcher.Matches(result, ft))
fn(context.m_nonFoundResultsEdits, i);
}
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "search/engine.hpp"
#include "search/feature_loader.hpp"
#include "search/search_quality/assessment_tool/context.hpp"
#include "search/search_quality/assessment_tool/edits.hpp"
#include "search/search_quality/assessment_tool/model.hpp"
@ -37,6 +38,8 @@ public:
void OnShowViewportClicked() override;
void OnShowPositionClicked() override;
bool HasChanges() override;
bool AlreadyInSamples(FeatureID const & id) override;
void AddNonFoundResult(FeatureID const & id) override;
private:
static int constexpr kInvalidIndex = -1;
@ -51,8 +54,12 @@ private:
void ResetSearch();
void ShowMarks(Context const & context);
template <typename Fn>
void ForMatchingEntries(Context & context, FeatureID const & id, Fn && fn);
Framework & m_framework;
Index const & m_index;
search::FeatureLoader m_loader;
ContextList m_contexts;

View file

@ -10,6 +10,7 @@
#include "qt/qt_common/scale_slider.hpp"
#include "map/framework.hpp"
#include "map/place_page_info.hpp"
#include "geometry/mercator.hpp"
@ -43,6 +44,14 @@ MainView::MainView(Framework & framework) : m_framework(framework)
InitMapWidget();
InitDocks();
InitMenuBar();
m_framework.SetMapSelectionListeners(
[this](place_page::Info const & info) {
auto const & selectedFeature = info.GetID();
if (selectedFeature.IsValid())
m_selectedFeature = selectedFeature;
},
[this](bool /* switchFullScreenMode */) { m_selectedFeature = FeatureID(); });
}
MainView::~MainView()
@ -60,9 +69,17 @@ void MainView::SetSamples(ContextList::SamplesSlice const & samples)
m_sampleView->Clear();
}
void MainView::OnSearchStarted() { m_sampleView->OnSearchStarted(); }
void MainView::OnSearchStarted()
{
m_state = State::Search;
m_sampleView->OnSearchStarted();
}
void MainView::OnSearchCompleted() { m_sampleView->OnSearchCompleted(); }
void MainView::OnSearchCompleted()
{
m_state = State::AfterSearch;
m_sampleView->OnSearchCompleted();
}
void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample, bool positionAvailable,
bool hasEdits)
@ -77,9 +94,9 @@ void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample, boo
OnSampleChanged(sampleIndex, hasEdits);
}
void MainView::ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end)
void MainView::AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end)
{
m_sampleView->ShowFoundResults(begin, end);
m_sampleView->AddFoundResults(begin, end);
}
void MainView::ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
@ -266,6 +283,8 @@ void MainView::InitMapWidget()
{
auto * mapWidget = new qt::common::MapWidget(m_framework, false /* apiOpenGLES3 */, widget /* parent */);
connect(mapWidget, &qt::common::MapWidget::OnContextMenuRequested,
[this](QPoint const & p) { AddSelectedFeature(p); });
auto * toolBar = new QToolBar(widget /* parent */);
toolBar->setOrientation(Qt::Vertical);
toolBar->setIconSize(QSize(32, 32));
@ -384,6 +403,26 @@ MainView::SaveResult MainView::TryToSaveEdits(QString const & msg)
return SaveResult::Cancelled;
}
void MainView::AddSelectedFeature(QPoint const & p)
{
auto const selectedFeature = m_selectedFeature;
if (!selectedFeature.IsValid())
return;
if (m_state != State::AfterSearch)
return;
if (m_model->AlreadyInSamples(selectedFeature))
return;
QMenu menu;
auto const * action = menu.addAction("Add to non-found results");
connect(action, &QAction::triggered,
[this, selectedFeature]() { m_model->AddNonFoundResult(selectedFeature); });
menu.exec(p);
}
QDockWidget * MainView::CreateDock(QWidget & widget)
{
auto * dock = new QDockWidget(QString(), this /* parent */, Qt::Widget);

View file

@ -2,6 +2,8 @@
#include "search/search_quality/assessment_tool/view.hpp"
#include "indexer/feature_decl.hpp"
#include <QtWidgets/QMainWindow>
class Framework;
@ -33,7 +35,7 @@ public:
void ShowSample(size_t sampleIndex, search::Sample const & sample, bool positionAvailable,
bool hasEdits) override;
void ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) override;
void AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) override;
void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
std::vector<Edits::Entry> const & entries) override;
@ -67,6 +69,23 @@ private slots:
void OnNonFoundResultSelected(QItemSelection const & current);
private:
enum class State
{
BeforeSearch,
Search,
AfterSearch
};
friend string DebugPrint(State state)
{
switch (state)
{
case State::BeforeSearch: return "BeforeSearch";
case State::Search: return "Search";
case State::AfterSearch: return "AfterSearch";
}
}
enum class SaveResult
{
NoEdits,
@ -87,6 +106,8 @@ private:
void SetSampleDockTitle(bool hasEdits);
SaveResult TryToSaveEdits(QString const & msg);
void AddSelectedFeature(QPoint const & p);
QDockWidget * CreateDock(QWidget & widget);
Framework & m_framework;
@ -99,4 +120,7 @@ private:
QAction * m_save = nullptr;
QAction * m_saveAs = nullptr;
State m_state = State::BeforeSearch;
FeatureID m_selectedFeature;
};

View file

@ -3,6 +3,7 @@
#include <string>
class View;
struct FeatureID;
class Model
{
@ -22,6 +23,9 @@ public:
virtual void OnShowPositionClicked() = 0;
virtual bool HasChanges() = 0;
virtual bool AlreadyInSamples(FeatureID const & id) = 0;
virtual void AddNonFoundResult(FeatureID const & id) = 0;
protected:
View * m_view = nullptr;
};

View file

@ -63,9 +63,9 @@ ResultView::ResultView(search::Sample::Result const & result, QWidget & parent)
{
}
void ResultView::SetEditor(Edits::RelevanceEditor && editor)
void ResultView::SetEditor(Edits::Editor && editor)
{
m_editor = my::make_unique<Edits::RelevanceEditor>(std::move(editor));
m_editor = my::make_unique<Edits::Editor>(std::move(editor));
m_irrelevant->setChecked(false);
m_relevant->setChecked(false);
@ -83,10 +83,19 @@ void ResultView::SetEditor(Edits::RelevanceEditor && editor)
void ResultView::Update()
{
if (m_editor && m_editor->HasChanges())
setStyleSheet("#result {background: rgba(255, 255, 200, 50%)}");
if (m_editor)
{
if (m_editor->GetOrigin() == Edits::Entry::Origin::Created)
setStyleSheet("#result {background: rgba(173, 223, 173, 50%)}");
else if (m_editor->HasChanges())
setStyleSheet("#result {background: rgba(255, 255, 200, 50%)}");
else
setStyleSheet("");
}
else
{
setStyleSheet("");
}
}
void ResultView::Init()

View file

@ -24,7 +24,7 @@ public:
ResultView(search::Result const & result, QWidget & parent);
ResultView(search::Sample::Result const & result, QWidget & parent);
void SetEditor(Edits::RelevanceEditor && editor);
void SetEditor(Edits::Editor && editor);
void Update();
@ -47,5 +47,5 @@ private:
QRadioButton * m_relevant = nullptr;
QRadioButton * m_vital = nullptr;
std::unique_ptr<Edits::RelevanceEditor> m_editor;
std::unique_ptr<Edits::Editor> m_editor;
};

View file

@ -57,6 +57,12 @@ void ResultsView::Update(Edits::Update const & update)
result->Update();
break;
}
case Edits::Update::Type::Add:
{
CHECK_LESS(update.m_index, m_results.size(), ());
m_results[update.m_index]->Update();
break;
}
case Edits::Update::Type::Delete:
{
auto const index = update.m_index;
@ -64,6 +70,11 @@ void ResultsView::Update(Edits::Update const & update)
item(static_cast<int>(index))->setHidden(true);
break;
}
case Edits::Update::Type::Resurrect:
auto const index = update.m_index;
CHECK_LESS(index, Size(), ());
item(static_cast<int>(index))->setHidden(false);
break;
};
}

View file

@ -178,7 +178,7 @@ void SampleView::OnSearchStarted() { m_spinner->Show(); }
void SampleView::OnSearchCompleted() { m_spinner->Hide(); }
void SampleView::ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end)
void SampleView::AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end)
{
for (auto it = begin; it != end; ++it)
m_foundResults->Add(*it /* result */);
@ -194,6 +194,8 @@ void SampleView::ShowNonFoundResults(std::vector<search::Sample::Result> const &
guard.m_controller.SetIsVisible(true);
guard.m_controller.SetIsDrawable(true);
m_nonFoundResults->Clear();
bool allDeleted = true;
for (size_t i = 0; i < results.size(); ++i)
{
@ -278,7 +280,7 @@ void SampleView::SetEdits(ResultsView & results, Edits & edits)
size_t const numRelevances = edits.GetRelevances().size();
CHECK_EQUAL(results.Size(), numRelevances, ());
for (size_t i = 0; i < numRelevances; ++i)
results.Get(i).SetEditor(Edits::RelevanceEditor(edits, i));
results.Get(i).SetEditor(Edits::Editor(edits, i));
}
void SampleView::OnRemoveNonFoundResult(int row) { m_nonFoundResultsEdits->Delete(row); }

View file

@ -27,7 +27,7 @@ public:
void OnSearchStarted();
void OnSearchCompleted();
void ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end);
void AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end);
void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
std::vector<Edits::Entry> const & entries);

View file

@ -33,8 +33,8 @@ public:
virtual void ShowSample(size_t index, search::Sample const & sample, bool positionAvailable,
bool hasEdits) = 0;
virtual void ShowFoundResults(search::Results::ConstIter begin,
search::Results::ConstIter end) = 0;
virtual void AddFoundResults(search::Results::ConstIter begin,
search::Results::ConstIter end) = 0;
virtual void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
std::vector<Edits::Entry> const & entries) = 0;

View file

@ -50,17 +50,10 @@ void Matcher::Match(std::vector<Sample::Result> const & golden, std::vector<Resu
}
}
bool Matcher::Matches(Sample::Result const & golden, search::Result const & actual)
bool Matcher::Matches(Sample::Result const & golden, FeatureType & ft)
{
static double constexpr kToleranceMeters = 50;
if (actual.GetResultType() != Result::RESULT_FEATURE)
return false;
FeatureType ft;
if (!m_loader.Load(actual.GetFeatureID(), ft))
return false;
auto const houseNumber = ft.GetHouseNumber();
auto const center = feature::GetCenter(ft);
@ -84,4 +77,17 @@ bool Matcher::Matches(Sample::Result const & golden, search::Result const & actu
return nameMatches && golden.m_houseNumber == houseNumber &&
MercatorBounds::DistanceOnEarth(golden.m_pos, center) < kToleranceMeters;
}
bool Matcher::Matches(Sample::Result const & golden, search::Result const & actual)
{
if (actual.GetResultType() != Result::RESULT_FEATURE)
return false;
FeatureType ft;
if (!m_loader.Load(actual.GetFeatureID(), ft))
return false;
return Matches(golden, ft);
}
} // namespace search

View file

@ -7,6 +7,8 @@
#include <limits>
#include <vector>
class FeatureType;
namespace search
{
class FeatureLoader;
@ -21,9 +23,10 @@ public:
void Match(std::vector<Sample::Result> const & golden, std::vector<Result> const & actual,
std::vector<size_t> & goldenMatching, std::vector<size_t> & actualMatching);
private:
bool Matches(Sample::Result const & golden, FeatureType & ft);
bool Matches(Sample::Result const & golden, Result const & actual);
private:
FeatureLoader & m_loader;
};
} // namespace search