Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
17
tools/poly_borders/CMakeLists.txt
Normal file
17
tools/poly_borders/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
project(poly_borders)
|
||||
|
||||
set(
|
||||
SRC
|
||||
borders_data.cpp
|
||||
borders_data.hpp
|
||||
help_structures.cpp
|
||||
help_structures.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} generator)
|
||||
|
||||
omim_add_test_subdirectory(poly_borders_tests)
|
||||
|
||||
omim_add_tool_subdirectory(poly_borders_tool)
|
||||
447
tools/poly_borders/borders_data.cpp
Normal file
447
tools/poly_borders/borders_data.cpp
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
#include "poly_borders/borders_data.hpp"
|
||||
|
||||
#include "poly_borders/help_structures.hpp"
|
||||
|
||||
#include "generator/borders.hpp"
|
||||
#include "generator/routing_city_boundaries_processor.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
#include "geometry/rect2d.hpp"
|
||||
#include "geometry/region2d.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
#include "base/thread_pool_computational.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace poly_borders;
|
||||
|
||||
void PrintWithSpaces(std::string const & str, size_t maxN)
|
||||
{
|
||||
std::cout << str;
|
||||
if (maxN <= str.size())
|
||||
return;
|
||||
|
||||
maxN -= str.size();
|
||||
for (size_t i = 0; i < maxN; ++i)
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
std::string RemoveIndexFromMwmName(std::string const & mwmName)
|
||||
{
|
||||
auto const pos = mwmName.find(BordersData::kBorderExtension);
|
||||
CHECK_NOT_EQUAL(pos, std::string::npos, ());
|
||||
auto const end = mwmName.begin() + pos + BordersData::kBorderExtension.size();
|
||||
std::string result(mwmName.cbegin(), end);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsReversedIntervals(size_t fromDst, size_t toDst, size_t fromSrc, size_t toSrc)
|
||||
{
|
||||
return (fromDst > toDst) != (fromSrc > toSrc);
|
||||
}
|
||||
|
||||
std::vector<m2::PointD> AppendPointsWithAnyDirection(std::vector<MarkedPoint> const & copyFrom, size_t from, size_t to)
|
||||
{
|
||||
std::vector<m2::PointD> result;
|
||||
if (from > to)
|
||||
std::swap(from, to);
|
||||
|
||||
for (size_t k = from; k <= to; ++k)
|
||||
result.emplace_back(copyFrom[k].m_point);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double AbsAreaDiff(std::vector<m2::PointD> const & firstPolygon, std::vector<m2::PointD> const & secondPolygon)
|
||||
{
|
||||
auto const firstArea = generator::AreaOnEarth(firstPolygon);
|
||||
auto const secondArea = generator::AreaOnEarth(secondPolygon);
|
||||
return std::abs(secondArea - firstArea);
|
||||
}
|
||||
|
||||
bool NeedReplace(std::vector<m2::PointD> const & curSubpolygon, std::vector<m2::PointD> const & anotherSubpolygon)
|
||||
{
|
||||
auto const areaDiff = AbsAreaDiff(curSubpolygon, anotherSubpolygon);
|
||||
double constexpr kMaxAreaDiffMetersSquared = 20000.0;
|
||||
if (areaDiff > kMaxAreaDiffMetersSquared)
|
||||
return false;
|
||||
|
||||
if (AlmostEqualAbs(areaDiff, 0.0, BordersData::kEqualityEpsilon))
|
||||
return false;
|
||||
|
||||
// We know that |curSize| is always greater than 1, because we construct it such way, but we know
|
||||
// nothing about |anotherSize|, and we do not want to replace current subpolygon of several points
|
||||
// with a subpolygon consisting of one point or consisting of too many points.
|
||||
CHECK_GREATER(curSubpolygon.size(), 1, ());
|
||||
return 1 < anotherSubpolygon.size() && anotherSubpolygon.size() < 10;
|
||||
}
|
||||
|
||||
bool ShouldLog(size_t i, size_t n)
|
||||
{
|
||||
return (i % 100 == 0) || (i + 1 == n);
|
||||
}
|
||||
|
||||
void Append(size_t from, size_t to, bool reversed, std::vector<MarkedPoint> const & copyFrom,
|
||||
std::vector<MarkedPoint> & copyTo)
|
||||
{
|
||||
if (!reversed)
|
||||
{
|
||||
for (size_t k = from; k <= to; ++k)
|
||||
copyTo.emplace_back(copyFrom[k].m_point);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t k = to;
|
||||
while (k >= from)
|
||||
{
|
||||
copyTo.emplace_back(copyFrom[k].m_point);
|
||||
if (k == 0)
|
||||
break;
|
||||
--k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m2::RectD BoundingBox(std::vector<MarkedPoint> const & points)
|
||||
{
|
||||
m2::RectD rect;
|
||||
for (auto const & point : points)
|
||||
rect.Add(point.m_point);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void SwapIfNeeded(size_t & a, size_t & b)
|
||||
{
|
||||
if (a > b)
|
||||
std::swap(a, b);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace poly_borders
|
||||
{
|
||||
|
||||
// BordersData -------------------------------------------------------------------------------------
|
||||
void BordersData::Init(std::string const & bordersDir)
|
||||
{
|
||||
LOG(LINFO, ("Borders path:", bordersDir));
|
||||
|
||||
// key - coordinates
|
||||
// value - {border idx, point idx}
|
||||
std::unordered_map<int64_t, std::vector<std::pair<size_t, size_t>>> index;
|
||||
|
||||
std::vector<std::string> files;
|
||||
Platform::GetFilesByExt(bordersDir, kBorderExtension, files);
|
||||
|
||||
LOG(LINFO, ("Start reading data from .poly files."));
|
||||
size_t prevIndex = 0;
|
||||
for (auto const & file : files)
|
||||
{
|
||||
auto const fullPath = base::JoinPath(bordersDir, file);
|
||||
size_t polygonId = 1;
|
||||
|
||||
borders::PolygonsList borders;
|
||||
borders::LoadBorders(fullPath, borders);
|
||||
for (auto & region : borders)
|
||||
{
|
||||
auto & points = region.MutableData();
|
||||
m_duplicatedPointsCount += RemoveDuplicatingPointImpl(points);
|
||||
CHECK_GREATER(points.size(), 1, (fullPath));
|
||||
|
||||
// Some mwms have several polygons. For example, for Japan_Kanto_Tokyo that has 2 polygons we
|
||||
// will write 2 files:
|
||||
// Japan_Kanto_Tokyo.poly1
|
||||
// Japan_Kanto_Tokyo.poly2
|
||||
auto const fileCopy = file + std::to_string(polygonId);
|
||||
|
||||
m_indexToPolyFileName[prevIndex] = fileCopy;
|
||||
m_polyFileNameToIndex[fileCopy] = prevIndex++;
|
||||
|
||||
size_t const borderIdx = m_bordersPolygons.size();
|
||||
for (size_t i = 0; i < points.size(); ++i)
|
||||
index[PointToInt64Obsolete(points[i], kPointCoordBits)].emplace_back(borderIdx, i);
|
||||
|
||||
++polygonId;
|
||||
m_bordersPolygons.emplace_back(region.GetRect(), points);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const & [_, v] : index)
|
||||
{
|
||||
for (size_t i = 0; i < v.size() - 1; ++i)
|
||||
for (size_t j = i + 1; j < v.size(); ++j)
|
||||
{
|
||||
m_bordersPolygons[v[i].first].m_points[v[i].second].AddLink(v[j].first, v[j].second);
|
||||
m_bordersPolygons[v[j].first].m_points[v[j].second].AddLink(v[i].first, v[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(LINFO, ("Removed:", m_duplicatedPointsCount, "from input data."));
|
||||
}
|
||||
|
||||
void BordersData::DumpPolyFiles(std::string const & targetDir)
|
||||
{
|
||||
size_t n = m_bordersPolygons.size();
|
||||
for (size_t i = 0; i < n;)
|
||||
{
|
||||
// Russia_Moscow.poly1 -> Russia_Moscow.poly
|
||||
auto name = RemoveIndexFromMwmName(m_indexToPolyFileName.at(i));
|
||||
|
||||
size_t j = i + 1;
|
||||
while (j < n && name == RemoveIndexFromMwmName(m_indexToPolyFileName.at(j)))
|
||||
++j;
|
||||
|
||||
std::vector<m2::RegionD> regions;
|
||||
for (; i < j; i++)
|
||||
{
|
||||
if (ShouldLog(i, n))
|
||||
LOG(LINFO, ("Dumping poly files:", i + 1, "/", n));
|
||||
|
||||
m2::RegionD region;
|
||||
for (auto const & markedPoint : m_bordersPolygons[i].m_points)
|
||||
region.AddPoint(markedPoint.m_point);
|
||||
|
||||
regions.emplace_back(std::move(region));
|
||||
}
|
||||
|
||||
CHECK(strings::ReplaceFirst(name, kBorderExtension, ""), (name));
|
||||
borders::DumpBorderToPolyFile(targetDir, name, regions);
|
||||
}
|
||||
}
|
||||
|
||||
size_t BordersData::RemoveDuplicatePoints()
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto & polygon : m_bordersPolygons)
|
||||
count += RemoveDuplicatingPointImpl(polygon.m_points);
|
||||
return count;
|
||||
}
|
||||
|
||||
void BordersData::PrintDiff()
|
||||
{
|
||||
using Info = std::tuple<double, std::string, size_t, size_t>;
|
||||
|
||||
std::set<Info> info;
|
||||
|
||||
size_t allNumberBeforeCount = 0;
|
||||
size_t maxMwmNameLength = 0;
|
||||
for (size_t i = 0; i < m_bordersPolygons.size(); ++i)
|
||||
{
|
||||
auto const & mwmName = m_indexToPolyFileName[i];
|
||||
|
||||
auto const all = static_cast<int32_t>(m_bordersPolygons[i].m_points.size());
|
||||
auto const allBefore = static_cast<int32_t>(m_prevCopy[i].m_points.size());
|
||||
|
||||
CHECK_GREATER_OR_EQUAL(allBefore, all, ());
|
||||
m_removedPointsCount += allBefore - all;
|
||||
allNumberBeforeCount += allBefore;
|
||||
|
||||
double area = 0.0;
|
||||
double constexpr kAreaEpsMetersSqr = 1e-4;
|
||||
if (m_additionalAreaMetersSqr[i] >= kAreaEpsMetersSqr)
|
||||
area = m_additionalAreaMetersSqr[i];
|
||||
|
||||
maxMwmNameLength = std::max(maxMwmNameLength, mwmName.size());
|
||||
info.emplace(area, mwmName, allBefore, all);
|
||||
}
|
||||
|
||||
for (auto const & [area, name, allBefore, all] : info)
|
||||
{
|
||||
size_t diff = allBefore - all;
|
||||
PrintWithSpaces(name, maxMwmNameLength + 1);
|
||||
PrintWithSpaces("-" + std::to_string(diff) + " points", 17);
|
||||
|
||||
std::cout << " total changed area: " << area << " m^2" << std::endl;
|
||||
}
|
||||
|
||||
CHECK_NOT_EQUAL(allNumberBeforeCount, 0, ("Empty input?"));
|
||||
std::cout << "Number of removed points: " << m_removedPointsCount << std::endl
|
||||
<< "Removed duplicate point: " << m_duplicatedPointsCount << std::endl
|
||||
<< "Total removed points: " << m_removedPointsCount + m_duplicatedPointsCount << std::endl;
|
||||
std::cout << "Points number before processing: " << allNumberBeforeCount << ", remove( "
|
||||
<< static_cast<double>(m_removedPointsCount + m_duplicatedPointsCount) / allNumberBeforeCount * 100.0
|
||||
<< "% )" << std::endl;
|
||||
}
|
||||
|
||||
void BordersData::RemoveEmptySpaceBetweenBorders()
|
||||
{
|
||||
LOG(LINFO, ("Start removing empty space between borders."));
|
||||
|
||||
for (size_t curBorderId = 0; curBorderId < m_bordersPolygons.size(); ++curBorderId)
|
||||
{
|
||||
LOG(LDEBUG, ("Get:", m_indexToPolyFileName[curBorderId]));
|
||||
|
||||
if (ShouldLog(curBorderId, m_bordersPolygons.size()))
|
||||
LOG(LINFO, ("Removing empty spaces:", curBorderId + 1, "/", m_bordersPolygons.size()));
|
||||
|
||||
auto & curPolygon = m_bordersPolygons[curBorderId];
|
||||
for (size_t curPointId = 0; curPointId < curPolygon.m_points.size(); ++curPointId)
|
||||
{
|
||||
if (curPolygon.IsFrozen(curPointId, curPointId) || !HasLinkAt(curBorderId, curPointId))
|
||||
continue;
|
||||
|
||||
size_t constexpr kMaxLookAhead = 5;
|
||||
for (size_t shift = 1; shift <= kMaxLookAhead; ++shift)
|
||||
{
|
||||
if (TryToReplace(curBorderId, curPointId /* curLeftPointId */, curPointId + shift /* curRightPointId */) ==
|
||||
base::ControlFlow::Break)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoReplace();
|
||||
}
|
||||
|
||||
base::ControlFlow BordersData::TryToReplace(size_t curBorderId, size_t & curLeftPointId, size_t curRightPointId)
|
||||
{
|
||||
auto & curPolygon = m_bordersPolygons[curBorderId];
|
||||
if (curRightPointId >= curPolygon.m_points.size())
|
||||
return base::ControlFlow::Break;
|
||||
|
||||
if (curPolygon.IsFrozen(curRightPointId, curRightPointId))
|
||||
{
|
||||
curLeftPointId = curRightPointId - 1;
|
||||
return base::ControlFlow::Break;
|
||||
}
|
||||
|
||||
auto & leftMarkedPoint = curPolygon.m_points[curLeftPointId];
|
||||
auto & rightMarkedPoint = curPolygon.m_points[curRightPointId];
|
||||
|
||||
auto op = rightMarkedPoint.GetLink(curBorderId);
|
||||
if (!op)
|
||||
return base::ControlFlow::Continue;
|
||||
|
||||
Link rightLink = *op;
|
||||
Link leftLink = *leftMarkedPoint.GetLink(curBorderId);
|
||||
|
||||
if (leftLink.m_borderId != rightLink.m_borderId)
|
||||
return base::ControlFlow::Continue;
|
||||
|
||||
auto const anotherBorderId = leftLink.m_borderId;
|
||||
auto const anotherLeftPointId = leftLink.m_pointId;
|
||||
auto const anotherRightPointId = rightLink.m_pointId;
|
||||
auto & anotherPolygon = m_bordersPolygons[anotherBorderId];
|
||||
|
||||
if (anotherPolygon.IsFrozen(std::min(anotherLeftPointId, anotherRightPointId),
|
||||
std::max(anotherLeftPointId, anotherRightPointId)))
|
||||
{
|
||||
return base::ControlFlow::Continue;
|
||||
}
|
||||
|
||||
auto const anotherSubpolygon =
|
||||
AppendPointsWithAnyDirection(anotherPolygon.m_points, anotherLeftPointId, anotherRightPointId);
|
||||
|
||||
auto const curSubpolygon = AppendPointsWithAnyDirection(curPolygon.m_points, curLeftPointId, curRightPointId);
|
||||
|
||||
if (!NeedReplace(curSubpolygon, anotherSubpolygon))
|
||||
return base::ControlFlow::Break;
|
||||
|
||||
// We want to decrease the amount of points in polygons. So we will replace the greater amounts of
|
||||
// points by smaller amounts of points.
|
||||
bool const curLenIsLess = curSubpolygon.size() < anotherSubpolygon.size();
|
||||
|
||||
size_t dstFrom = curLenIsLess ? anotherLeftPointId : curLeftPointId;
|
||||
size_t dstTo = curLenIsLess ? anotherRightPointId : curRightPointId;
|
||||
|
||||
size_t srcFrom = curLenIsLess ? curLeftPointId : anotherLeftPointId;
|
||||
size_t srcTo = curLenIsLess ? curRightPointId : anotherRightPointId;
|
||||
|
||||
size_t const borderIdWhereAreaWillBeChanged = curLenIsLess ? anotherBorderId : curBorderId;
|
||||
size_t const srcBorderId = curLenIsLess ? curBorderId : anotherBorderId;
|
||||
|
||||
bool const reversed = IsReversedIntervals(dstFrom, dstTo, srcFrom, srcTo);
|
||||
|
||||
m_additionalAreaMetersSqr[borderIdWhereAreaWillBeChanged] += AbsAreaDiff(curSubpolygon, anotherSubpolygon);
|
||||
|
||||
SwapIfNeeded(dstFrom, dstTo);
|
||||
SwapIfNeeded(srcFrom, srcTo);
|
||||
|
||||
// Save info for |borderIdWhereAreaWillBeChanged| - where from it should gets info about
|
||||
// replacement.
|
||||
m_bordersPolygons[borderIdWhereAreaWillBeChanged].AddReplaceInfo(dstFrom, dstTo, srcFrom, srcTo, srcBorderId,
|
||||
reversed);
|
||||
|
||||
// And say for |srcBorderId| that points in segment: [srcFrom, srcTo] are frozen and cannot
|
||||
// be used anywhere (because we use them to replace points in segment: [dstFrom, dstTo]).
|
||||
m_bordersPolygons[srcBorderId].MakeFrozen(srcFrom, srcTo);
|
||||
|
||||
CHECK_LESS(curLeftPointId, curRightPointId, ());
|
||||
curLeftPointId = curRightPointId - 1;
|
||||
return base::ControlFlow::Break;
|
||||
}
|
||||
|
||||
void BordersData::DoReplace()
|
||||
{
|
||||
LOG(LINFO, ("Start replacing intervals."));
|
||||
|
||||
std::vector<Polygon> newMwmsPolygons;
|
||||
|
||||
for (size_t borderId = 0; borderId < m_bordersPolygons.size(); ++borderId)
|
||||
{
|
||||
if (ShouldLog(borderId, m_bordersPolygons.size()))
|
||||
LOG(LINFO, ("Replacing:", borderId + 1, "/", m_bordersPolygons.size()));
|
||||
|
||||
auto & polygon = m_bordersPolygons[borderId];
|
||||
|
||||
newMwmsPolygons.emplace_back();
|
||||
auto & newPolygon = newMwmsPolygons.back();
|
||||
|
||||
for (size_t i = 0; i < polygon.m_points.size(); ++i)
|
||||
{
|
||||
auto const replaceDataIter = polygon.FindReplaceData(i);
|
||||
bool const noReplaceData = replaceDataIter == polygon.m_replaceData.cend();
|
||||
if (noReplaceData)
|
||||
{
|
||||
newPolygon.m_points.emplace_back(polygon.m_points[i].m_point);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const srcBorderId = replaceDataIter->m_srcBorderId;
|
||||
size_t const srcFrom = replaceDataIter->m_srcReplaceFrom;
|
||||
size_t const srcTo = replaceDataIter->m_srcReplaceTo;
|
||||
size_t const nextI = replaceDataIter->m_dstTo;
|
||||
bool const reversed = replaceDataIter->m_reversed;
|
||||
|
||||
CHECK_EQUAL(i, replaceDataIter->m_dstFrom, ());
|
||||
|
||||
auto const & srcPolygon = m_bordersPolygons[srcBorderId];
|
||||
|
||||
Append(srcFrom, srcTo, reversed, srcPolygon.m_points, newPolygon.m_points);
|
||||
|
||||
polygon.m_replaceData.erase(replaceDataIter);
|
||||
i = nextI - 1;
|
||||
}
|
||||
|
||||
newPolygon.m_rect = BoundingBox(newPolygon.m_points);
|
||||
}
|
||||
|
||||
m_prevCopy = std::move(m_bordersPolygons);
|
||||
m_bordersPolygons = std::move(newMwmsPolygons);
|
||||
RemoveDuplicatePoints();
|
||||
}
|
||||
|
||||
Polygon const & BordersData::GetBordersPolygonByName(std::string const & name) const
|
||||
{
|
||||
auto id = m_polyFileNameToIndex.at(name);
|
||||
return m_bordersPolygons.at(id);
|
||||
}
|
||||
|
||||
bool BordersData::HasLinkAt(size_t curBorderId, size_t pointId)
|
||||
{
|
||||
auto & leftMarkedPoint = m_bordersPolygons[curBorderId].m_points[pointId];
|
||||
return leftMarkedPoint.GetLink(curBorderId).has_value();
|
||||
}
|
||||
} // namespace poly_borders
|
||||
71
tools/poly_borders/borders_data.hpp
Normal file
71
tools/poly_borders/borders_data.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "poly_borders/help_structures.hpp"
|
||||
|
||||
#include "base/control_flow.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace poly_borders
|
||||
{
|
||||
/// \note All methods of this class are not thread-safe except |MarkPoint()| method.
|
||||
class BordersData
|
||||
{
|
||||
public:
|
||||
inline static double const kEqualityEpsilon = 1.0E-7;
|
||||
inline static std::string const kBorderExtension = ".poly";
|
||||
|
||||
void Init(std::string const & bordersDir);
|
||||
|
||||
void RemoveEmptySpaceBetweenBorders();
|
||||
|
||||
void DumpPolyFiles(std::string const & targetDir);
|
||||
Polygon const & GetBordersPolygonByName(std::string const & name) const;
|
||||
void PrintDiff();
|
||||
|
||||
private:
|
||||
/// \brief Some polygons can have sequentially same points - duplicates. This method removes such
|
||||
/// points and leaves only unique.
|
||||
size_t RemoveDuplicatePoints();
|
||||
|
||||
template <class PointsT>
|
||||
static size_t RemoveDuplicatingPointImpl(PointsT & points)
|
||||
{
|
||||
auto const equalFn = [](auto const & p1, auto const & p2) { return p1.EqualDxDy(p2, kEqualityEpsilon); };
|
||||
|
||||
auto const last = std::unique(points.begin(), points.end(), equalFn);
|
||||
size_t count = std::distance(last, points.end());
|
||||
points.erase(last, points.end());
|
||||
|
||||
while (points.size() > 1 && equalFn(points.front(), points.back()))
|
||||
{
|
||||
++count;
|
||||
points.pop_back();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// \brief Checks whether we can replace points from segment: [curLeftPointId, curRightPointId]
|
||||
/// of |curBorderId| to points from another border in order to get rid of empty space
|
||||
/// between curBorder and anotherBorder.
|
||||
base::ControlFlow TryToReplace(size_t curBorderId, size_t & curLeftPointId, size_t curRightPointId);
|
||||
|
||||
bool HasLinkAt(size_t curBorderId, size_t pointId);
|
||||
|
||||
/// \brief Replace points using |Polygon::ReplaceData| that is filled by
|
||||
/// |RemoveEmptySpaceBetweenBorders()|.
|
||||
void DoReplace();
|
||||
|
||||
size_t m_removedPointsCount = 0;
|
||||
size_t m_duplicatedPointsCount = 0;
|
||||
std::map<size_t, double> m_additionalAreaMetersSqr;
|
||||
|
||||
std::map<std::string, size_t> m_polyFileNameToIndex;
|
||||
std::map<size_t, std::string> m_indexToPolyFileName;
|
||||
std::vector<Polygon> m_bordersPolygons;
|
||||
std::vector<Polygon> m_prevCopy;
|
||||
};
|
||||
} // namespace poly_borders
|
||||
80
tools/poly_borders/help_structures.cpp
Normal file
80
tools/poly_borders/help_structures.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include "poly_borders/help_structures.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
|
||||
namespace poly_borders
|
||||
{
|
||||
// Link --------------------------------------------------------------------------------------------
|
||||
bool Link::operator<(Link const & rhs) const
|
||||
{
|
||||
return std::tie(m_borderId, m_pointId) < std::tie(rhs.m_borderId, rhs.m_pointId);
|
||||
}
|
||||
|
||||
// ReplaceData -------------------------------------------------------------------------------------
|
||||
bool ReplaceData::operator<(ReplaceData const & rhs) const
|
||||
{
|
||||
return std::tie(m_dstFrom, m_dstTo) < std::tie(rhs.m_dstFrom, rhs.m_dstTo);
|
||||
}
|
||||
|
||||
// MarkedPoint -------------------------------------------------------------------------------------
|
||||
void MarkedPoint::AddLink(size_t borderId, size_t pointId)
|
||||
{
|
||||
m_links.emplace(borderId, pointId);
|
||||
}
|
||||
|
||||
std::optional<Link> MarkedPoint::GetLink(size_t curBorderId) const
|
||||
{
|
||||
if (m_links.size() != 1)
|
||||
return std::nullopt;
|
||||
|
||||
size_t anotherBorderId = m_links.begin()->m_borderId;
|
||||
if (anotherBorderId == curBorderId)
|
||||
return std::nullopt;
|
||||
|
||||
return *m_links.begin();
|
||||
}
|
||||
|
||||
// Polygon -----------------------------------------------------------------------------------------
|
||||
void Polygon::MakeFrozen(size_t a, size_t b)
|
||||
{
|
||||
CHECK_LESS(a, b, ());
|
||||
|
||||
// Ends of intervals shouldn't be frozen, we freeze only inner points: (a, b).
|
||||
// This condition is needed to drop such cases: a = x, b = x + 1, when
|
||||
// a + 1 will be greater than b - 1.
|
||||
if (b - a + 1 > 2)
|
||||
m_replaced.AddInterval(a + 1, b - 1);
|
||||
}
|
||||
|
||||
bool Polygon::IsFrozen(size_t a, size_t b) const
|
||||
{
|
||||
// We use LESS_OR_EQUAL because we want sometimes to check if
|
||||
// point i (associated with interval: [i, i]) is frozen.
|
||||
CHECK_LESS_OR_EQUAL(a, b, ());
|
||||
|
||||
return m_replaced.Intersects(a, b);
|
||||
}
|
||||
|
||||
void Polygon::AddReplaceInfo(size_t dstFrom, size_t dstTo, size_t srcFrom, size_t srcTo, size_t srcBorderId,
|
||||
bool reversed)
|
||||
{
|
||||
CHECK_LESS_OR_EQUAL(dstFrom, dstTo, ());
|
||||
CHECK_LESS(srcFrom, srcTo, ());
|
||||
|
||||
CHECK(!IsFrozen(dstFrom, dstTo), ());
|
||||
MakeFrozen(dstFrom, dstTo);
|
||||
|
||||
m_replaceData.emplace(dstFrom, dstTo, srcFrom, srcTo, srcBorderId, reversed);
|
||||
}
|
||||
|
||||
std::set<ReplaceData>::const_iterator Polygon::FindReplaceData(size_t index)
|
||||
{
|
||||
for (auto it = m_replaceData.cbegin(); it != m_replaceData.cend(); ++it)
|
||||
if (it->m_dstFrom <= index && index <= it->m_dstTo)
|
||||
return it;
|
||||
|
||||
return m_replaceData.cend();
|
||||
}
|
||||
} // namespace poly_borders
|
||||
101
tools/poly_borders/help_structures.hpp
Normal file
101
tools/poly_borders/help_structures.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include "geometry/rect2d.hpp"
|
||||
|
||||
#include "base/non_intersecting_intervals.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace poly_borders
|
||||
{
|
||||
|
||||
struct Link
|
||||
{
|
||||
inline static auto constexpr kInvalidId = std::numeric_limits<size_t>::max();
|
||||
|
||||
Link() = default;
|
||||
Link(size_t borderId, size_t pointId) : m_borderId(borderId), m_pointId(pointId) {}
|
||||
|
||||
bool operator<(Link const & rhs) const;
|
||||
|
||||
size_t m_borderId = kInvalidId;
|
||||
size_t m_pointId = kInvalidId;
|
||||
};
|
||||
|
||||
/// \note Using next semantic here: [replaceFrom, replaceTo], [replaceFromSrc, replaceToSrc].
|
||||
struct ReplaceData
|
||||
{
|
||||
ReplaceData(size_t replaceFrom, size_t replaceTo, size_t replaceFromSrc, size_t replaceToSrc, size_t borderIdSrc,
|
||||
bool reversed)
|
||||
: m_dstFrom(replaceFrom)
|
||||
, m_dstTo(replaceTo)
|
||||
, m_srcReplaceFrom(replaceFromSrc)
|
||||
, m_srcReplaceTo(replaceToSrc)
|
||||
, m_srcBorderId(borderIdSrc)
|
||||
, m_reversed(reversed)
|
||||
{}
|
||||
|
||||
bool operator<(ReplaceData const & rhs) const;
|
||||
|
||||
size_t m_dstFrom;
|
||||
size_t m_dstTo;
|
||||
|
||||
size_t m_srcReplaceFrom;
|
||||
size_t m_srcReplaceTo;
|
||||
size_t m_srcBorderId;
|
||||
|
||||
// If |m_srcReplaceFrom| was greater than |m_srcReplaceTo|.
|
||||
bool m_reversed;
|
||||
};
|
||||
|
||||
struct MarkedPoint
|
||||
{
|
||||
MarkedPoint() = default;
|
||||
MarkedPoint(m2::PointD const & point) : m_point(point) {}
|
||||
|
||||
void AddLink(size_t borderId, size_t pointId);
|
||||
|
||||
std::optional<Link> GetLink(size_t curBorderId) const;
|
||||
|
||||
bool EqualDxDy(MarkedPoint const & p, double eps) const { return m_point.EqualDxDy(p.m_point, eps); }
|
||||
|
||||
m2::PointD m_point;
|
||||
std::set<Link> m_links;
|
||||
};
|
||||
|
||||
struct Polygon
|
||||
{
|
||||
Polygon() = default;
|
||||
Polygon(m2::RectD const & rect, std::vector<m2::PointD> const & points) : m_rect(rect)
|
||||
{
|
||||
m_points.assign(points.begin(), points.end());
|
||||
}
|
||||
Polygon(m2::RectD const & rect, std::vector<MarkedPoint> && points) : m_rect(rect), m_points(std::move(points)) {}
|
||||
|
||||
Polygon(Polygon &&) = default;
|
||||
Polygon & operator=(Polygon &&) noexcept = default;
|
||||
|
||||
// [a, b]
|
||||
// @{
|
||||
void MakeFrozen(size_t a, size_t b);
|
||||
bool IsFrozen(size_t a, size_t b) const;
|
||||
// @}
|
||||
|
||||
// [replaceFrom, replaceTo], [replaceFromSrc, replaceToSrc]
|
||||
void AddReplaceInfo(size_t replaceFrom, size_t replaceTo, size_t replaceFromSrc, size_t replaceToSrc,
|
||||
size_t borderIdSrc, bool reversed);
|
||||
|
||||
std::set<ReplaceData>::const_iterator FindReplaceData(size_t index);
|
||||
|
||||
m2::RectD m_rect;
|
||||
std::vector<MarkedPoint> m_points;
|
||||
base::NonIntersectingIntervals<size_t> m_replaced;
|
||||
std::set<ReplaceData> m_replaceData;
|
||||
};
|
||||
} // namespace poly_borders
|
||||
15
tools/poly_borders/poly_borders_tests/CMakeLists.txt
Normal file
15
tools/poly_borders/poly_borders_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
project(poly_borders_tests)
|
||||
|
||||
set(SRC
|
||||
mark_points_tests.cpp
|
||||
remove_empty_spaces_tests.cpp
|
||||
tools.cpp
|
||||
tools.hpp
|
||||
)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
poly_borders
|
||||
platform_tests_support
|
||||
)
|
||||
190
tools/poly_borders/poly_borders_tests/mark_points_tests.cpp
Normal file
190
tools/poly_borders/poly_borders_tests/mark_points_tests.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#include "poly_borders/poly_borders_tests/tools.hpp"
|
||||
|
||||
#include "testing/testing.hpp"
|
||||
|
||||
#include "poly_borders/borders_data.hpp"
|
||||
|
||||
#include "platform/platform_tests_support/scoped_dir.hpp"
|
||||
#include "platform/platform_tests_support/scoped_file.hpp"
|
||||
#include "platform/platform_tests_support/writable_dir_changer.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace platform::tests_support;
|
||||
using namespace platform;
|
||||
using namespace poly_borders;
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
static string const kTestDir = "borders_poly_dir";
|
||||
|
||||
void TestMarked(Polygon const & polygon, size_t i)
|
||||
{
|
||||
TEST(!polygon.m_points[i].m_links.empty(), (i, "th point point must be marked."));
|
||||
}
|
||||
|
||||
void TestNotMarked(Polygon const & polygon, size_t i)
|
||||
{
|
||||
TEST(polygon.m_points[i].m_links.empty(), (i, "th point must not be marked."));
|
||||
}
|
||||
|
||||
void CheckByMask(Polygon const & polygons, vector<bool> markedMask)
|
||||
{
|
||||
CHECK_EQUAL(polygons.m_points.size(), markedMask.size(), ());
|
||||
for (size_t i = 0; i < polygons.m_points.size(); ++i)
|
||||
if (markedMask[i])
|
||||
TestMarked(polygons, i);
|
||||
else
|
||||
TestNotMarked(polygons, i);
|
||||
}
|
||||
|
||||
UNIT_TEST(PolyBordersPostprocessor_MarkPoints_1)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(-1.0, -1.0);
|
||||
m2::PointD b(-1.0, 1.0);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, {1.0, 1.0}, {1.0, -1.0}}};
|
||||
|
||||
vector<vector<bool>> markedMask1 = {{true, true, false, false}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, b, {2.0, 1.0}, {5.0, -1.0}}};
|
||||
|
||||
vector<vector<bool>> markedMask2 = {{true, true, false, false}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
bordersData.Init(bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon1, markedMask1[0]);
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon2, markedMask2[0]);
|
||||
}
|
||||
|
||||
UNIT_TEST(PolyBordersPostprocessor_MarkPoints_2)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{{-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, -1.0}}};
|
||||
|
||||
vector<vector<bool>> markedMask1 = {{false, false, false, false}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{{-12.0, -1.0}, {-10.0, 1.0}, {2.0, 1.0}, {5.0, -1.0}}};
|
||||
|
||||
vector<vector<bool>> markedMask2 = {{false, false, false, false}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
bordersData.Init(bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon1, markedMask1[0]);
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon2, markedMask2[0]);
|
||||
}
|
||||
|
||||
UNIT_TEST(PolyBordersPostprocessor_MarkPoints_3)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(-2.0, 1.0);
|
||||
m2::PointD b(0.0, 3.0);
|
||||
m2::PointD c(3.0, -1.0);
|
||||
m2::PointD d(-1.0, -3.0);
|
||||
m2::PointD e(-4.0, 2.0);
|
||||
m2::PointD f(-1.0, 4.0);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, c, {1.0, -3.0}, d}};
|
||||
|
||||
vector<vector<bool>> markedMask1 = {{true, true, true, false, true}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{b, f, {2.0, 5.0}, {6.0, 3.0}, c}};
|
||||
|
||||
vector<vector<bool>> markedMask2 = {{true, true, false, false, true}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons3 = {{a, b, f, {-3.0, 4.0}, e}};
|
||||
|
||||
vector<vector<bool>> markedMask3 = {{true, true, true, false, true}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons4 = {{a, e, {-3.0, -1.0}, d}};
|
||||
|
||||
vector<vector<bool>> markedMask4 = {{true, true, false, true}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Third", polygons3));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Fourth", polygons4));
|
||||
|
||||
BordersData bordersData;
|
||||
bordersData.Init(bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon1, markedMask1[0]);
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon2, markedMask2[0]);
|
||||
|
||||
auto const & bordersPolygon3 = bordersData.GetBordersPolygonByName("Third" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon3, markedMask3[0]);
|
||||
|
||||
auto const & bordersPolygon4 = bordersData.GetBordersPolygonByName("Fourth" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon4, markedMask4[0]);
|
||||
}
|
||||
|
||||
UNIT_TEST(PolyBordersPostprocessor_MarkPoints_4)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(6.0, 2.0);
|
||||
m2::PointD b(6.0, 4.0);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{{-2.0, -2.0}, {-2.0, 2.0}, {2.0, 2.0}, {2.0, -2.0}},
|
||||
{{4.0, 2.0}, {4.0, 4.0}, a, b}};
|
||||
|
||||
vector<vector<bool>> markedMask1 = {{false, false, false, false}, {false, false, true, true}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, b, {8.0, 6.0}, {8.0, 0.0}}};
|
||||
|
||||
vector<vector<bool>> markedMask2 = {{true, true, false, false}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
bordersData.Init(bordersDir);
|
||||
|
||||
auto const & firstBordersPolygon1 =
|
||||
bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(firstBordersPolygon1, markedMask1[0]);
|
||||
|
||||
auto const & secondBordersPolygon1 =
|
||||
bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "2");
|
||||
CheckByMask(secondBordersPolygon1, markedMask1[1]);
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
CheckByMask(bordersPolygon2, markedMask2[0]);
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
#include "poly_borders/poly_borders_tests/tools.hpp"
|
||||
|
||||
#include "testing/testing.hpp"
|
||||
|
||||
#include "poly_borders/borders_data.hpp"
|
||||
|
||||
#include "platform/platform_tests_support/scoped_dir.hpp"
|
||||
#include "platform/platform_tests_support/scoped_file.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace remove_empty_spaces_tests
|
||||
{
|
||||
using namespace platform::tests_support;
|
||||
using namespace platform;
|
||||
using namespace poly_borders;
|
||||
using namespace std;
|
||||
|
||||
string const kTestDir = "borders_poly_dir";
|
||||
auto constexpr kSmallShift = 1e-9;
|
||||
auto constexpr kSmallPointShift = m2::PointD(kSmallShift, kSmallShift);
|
||||
|
||||
void Process(BordersData & bordersData, string const & bordersDir)
|
||||
{
|
||||
bordersData.Init(bordersDir);
|
||||
bordersData.RemoveEmptySpaceBetweenBorders();
|
||||
}
|
||||
|
||||
bool ConsistsOf(Polygon const & polygon, vector<m2::PointD> const & points)
|
||||
{
|
||||
CHECK_EQUAL(polygon.m_points.size(), points.size(), ());
|
||||
|
||||
set<size_t> used;
|
||||
for (auto const & point : points)
|
||||
{
|
||||
for (size_t i = 0; i < polygon.m_points.size(); ++i)
|
||||
{
|
||||
static double constexpr kEps = 1e-5;
|
||||
if (AlmostEqualAbs(point, polygon.m_points[i].m_point, kEps) && used.count(i) == 0)
|
||||
{
|
||||
used.emplace(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return used.size() == points.size();
|
||||
}
|
||||
|
||||
UNIT_TEST(PolyBordersPostprocessor_RemoveEmptySpaces_1)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(0.0, 0.0);
|
||||
m2::PointD b(1.0, 0.0);
|
||||
m2::PointD c(2.0, 0.0);
|
||||
m2::PointD d(3.0, 0.0);
|
||||
m2::PointD e(4.0, 0.0);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, c, d, e}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, b, c, d, e}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
Process(bordersData, bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon1, {a, b, c, d, e}), ());
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon2, {a, b, c, d, e}), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(PolyBordersPostprocessor_RemoveEmptySpaces_2)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(0.0, 0.0);
|
||||
m2::PointD b(1.0, 0.0);
|
||||
// We should make c.y small because in other case changed area
|
||||
// will be so great, that point |c| will not be removed.
|
||||
m2::PointD c(2.0, kSmallShift);
|
||||
m2::PointD d(3.0, 0.0);
|
||||
m2::PointD e(4.0, 0.0);
|
||||
|
||||
// Point |c| is absent from polygons2, algorithm should remove |c| from polygon1.
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, c, d, e}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, b, d, e}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
Process(bordersData, bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon1, {a, b, d, e}), ());
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon2, {a, b, d, e}), ());
|
||||
}
|
||||
|
||||
// Like |PolyBordersPostprocessor_RemoveEmptySpaces_2| but two points will be
|
||||
// added instead of one.
|
||||
UNIT_TEST(PolyBordersPostprocessor_RemoveEmptySpaces_3)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(0.0, 0.0);
|
||||
m2::PointD b(1.0, 0.0);
|
||||
// We should make c.y (and d.y) small because in other case changed area
|
||||
// will be so great, that point |c| (|d|) will not be removed.
|
||||
m2::PointD c(2.0, kSmallShift);
|
||||
m2::PointD d(2.5, kSmallShift);
|
||||
m2::PointD e(4.0, 0.0);
|
||||
m2::PointD f(5.0, 0.0);
|
||||
|
||||
// Point |c| and |d| is absent from polygons2, algorithm should remove |c| from polygon1.
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, c, d, e, f}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, b, e, f}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
Process(bordersData, bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon1, {a, b, e, f}), ());
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon2, {a, b, e, f}), ());
|
||||
}
|
||||
|
||||
// Do not remove point |c| because changed area is too big.
|
||||
UNIT_TEST(PolyBordersPostprocessor_RemoveEmptySpaces_4)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(0.0, 0.0);
|
||||
m2::PointD b(1.0, 0.0);
|
||||
m2::PointD c(2.0, 1.0);
|
||||
m2::PointD d(4.0, 0.0);
|
||||
m2::PointD e(5.0, 0.0);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, c, d, e}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, b, d, e}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
Process(bordersData, bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon1, {a, b, c, d, e}), ());
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon2, {a, b, d, e}), ());
|
||||
}
|
||||
|
||||
// Replace {c1, d1, e1} -> {c2, d2}.
|
||||
UNIT_TEST(PolyBordersPostprocessor_RemoveEmptySpaces_5)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(0.0, 0.0);
|
||||
m2::PointD b(9.0, 0.0);
|
||||
|
||||
m2::PointD c1(2.0, 3.0);
|
||||
m2::PointD d1(4.0, 4.0);
|
||||
m2::PointD e1(d1 + kSmallPointShift + kSmallPointShift);
|
||||
|
||||
m2::PointD c2(c1 + kSmallPointShift);
|
||||
m2::PointD d2(d1 + kSmallPointShift);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, c1, d1, e1, b}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, c2, d2, b}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
Process(bordersData, bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon1, {a, c2, d2, b}), ());
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon2, {a, c2, d2, b}), ());
|
||||
}
|
||||
|
||||
// Removes duplicates.
|
||||
UNIT_TEST(PolyBordersPostprocessor_RemoveEmptySpaces_6)
|
||||
{
|
||||
ScopedDir const scopedDir(kTestDir);
|
||||
string const & bordersDir = scopedDir.GetFullPath();
|
||||
|
||||
m2::PointD a(0.0, 0.0);
|
||||
m2::PointD b(1.0, 0.0);
|
||||
m2::PointD c(2.0, 1.0);
|
||||
m2::PointD d(4.0, 0.0);
|
||||
m2::PointD e(5.0, 0.0);
|
||||
|
||||
vector<vector<m2::PointD>> polygons1 = {{a, b, c, d, d, d, e, e, e}};
|
||||
|
||||
vector<vector<m2::PointD>> polygons2 = {{a, d, d, d, e}};
|
||||
|
||||
vector<shared_ptr<ScopedFile>> files;
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "First", polygons1));
|
||||
files.emplace_back(CreatePolyBorderFileByPolygon(kTestDir, "Second", polygons2));
|
||||
|
||||
BordersData bordersData;
|
||||
Process(bordersData, bordersDir);
|
||||
|
||||
auto const & bordersPolygon1 = bordersData.GetBordersPolygonByName("First" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon1, {a, b, c, d, e}), ());
|
||||
|
||||
auto const & bordersPolygon2 = bordersData.GetBordersPolygonByName("Second" + BordersData::kBorderExtension + "1");
|
||||
TEST(ConsistsOf(bordersPolygon2, {a, d, e}), ());
|
||||
}
|
||||
} // namespace remove_empty_spaces_tests
|
||||
45
tools/poly_borders/poly_borders_tests/tools.cpp
Normal file
45
tools/poly_borders/poly_borders_tests/tools.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "poly_borders/poly_borders_tests/tools.hpp"
|
||||
|
||||
#include "poly_borders/borders_data.hpp"
|
||||
|
||||
#include "generator/borders.hpp"
|
||||
|
||||
#include "geometry/region2d.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace platform::tests_support;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<m2::RegionD> ConvertFromPointsVector(std::vector<std::vector<m2::PointD>> const & polygons)
|
||||
{
|
||||
std::vector<m2::RegionD> res;
|
||||
res.reserve(polygons.size());
|
||||
for (auto const & polygon : polygons)
|
||||
res.emplace_back(polygon);
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace poly_borders
|
||||
{
|
||||
std::shared_ptr<ScopedFile> CreatePolyBorderFileByPolygon(std::string const & relativeDirPath, std::string const & name,
|
||||
std::vector<std::vector<m2::PointD>> const & polygons)
|
||||
{
|
||||
std::string path = base::JoinPath(relativeDirPath, name + BordersData::kBorderExtension);
|
||||
|
||||
auto file = std::make_shared<ScopedFile>(path, ScopedFile::Mode::Create);
|
||||
|
||||
auto const targetDir = base::GetDirectory(file->GetFullPath());
|
||||
|
||||
auto const regions = ConvertFromPointsVector(polygons);
|
||||
borders::DumpBorderToPolyFile(targetDir, name, regions);
|
||||
|
||||
return file;
|
||||
}
|
||||
} // namespace poly_borders
|
||||
16
tools/poly_borders/poly_borders_tests/tools.hpp
Normal file
16
tools/poly_borders/poly_borders_tests/tools.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "platform/platform_tests_support/scoped_file.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace poly_borders
|
||||
{
|
||||
std::shared_ptr<platform::tests_support::ScopedFile> CreatePolyBorderFileByPolygon(
|
||||
std::string const & relativeDirPath, std::string const & name,
|
||||
std::vector<std::vector<m2::PointD>> const & polygons);
|
||||
} // namespace poly_borders
|
||||
10
tools/poly_borders/poly_borders_tool/CMakeLists.txt
Normal file
10
tools/poly_borders/poly_borders_tool/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
project(poly_borders_tool)
|
||||
|
||||
set(SRC poly_borders_tool.cpp)
|
||||
|
||||
omim_add_executable(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
poly_borders
|
||||
gflags::gflags
|
||||
)
|
||||
64
tools/poly_borders/poly_borders_tool/poly_borders_tool.cpp
Normal file
64
tools/poly_borders/poly_borders_tool/poly_borders_tool.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include "poly_borders/borders_data.hpp"
|
||||
#include "poly_borders/help_structures.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_string(borders_path, "", "Path to directory with *.poly files.");
|
||||
DEFINE_string(output_path, "", "Path to target directory where the output *.poly files will be placed.");
|
||||
|
||||
using namespace poly_borders;
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
gflags::SetUsageMessage(
|
||||
"\n\n\tThe tool is used to process *.poly borders files. We use such files\n"
|
||||
"\tto cut the planet into mwms in generator. The problem is that we have\n"
|
||||
"\tempty spaces between neighbouring borders. This tool creates new borders\n"
|
||||
"\tbased on input data by removing points from borders in such a way that the\n"
|
||||
"\tchanged area of each border will not be too large.\n"
|
||||
"\tArguments:\n"
|
||||
"\t\t--borders_path=/path/to/directory/with/borders\n"
|
||||
"\t\t--output_path=/path/to/directory/where/new/borders/will/be/placed\n");
|
||||
|
||||
if (FLAGS_borders_path.empty() || FLAGS_output_path.empty())
|
||||
{
|
||||
gflags::ShowUsageWithFlags("poly_borders_postprocessor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
CHECK_NOT_EQUAL(FLAGS_borders_path, FLAGS_output_path, ());
|
||||
CHECK(Platform::IsDirectory(FLAGS_borders_path), ("Cannot find directory:", FLAGS_borders_path));
|
||||
CHECK(Platform::IsDirectory(FLAGS_output_path), ("Cannot find directory:", FLAGS_output_path));
|
||||
|
||||
try
|
||||
{
|
||||
BordersData data;
|
||||
data.Init(FLAGS_borders_path);
|
||||
data.RemoveEmptySpaceBetweenBorders();
|
||||
data.PrintDiff();
|
||||
data.DumpPolyFiles(FLAGS_output_path);
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LERROR, ("Core exception:", e.Msg()));
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
LOG(LERROR, ("Std exception:", e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(LERROR, ("Unknown exception."));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue