Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
|
|
@ -0,0 +1,94 @@
|
|||
#include "topography_generator/marching_squares/contours_builder.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
namespace topography_generator
|
||||
{
|
||||
ContoursBuilder::ContoursBuilder(size_t levelsCount, std::string const & debugId)
|
||||
: m_levelsCount(levelsCount)
|
||||
, m_finalizedContours(levelsCount)
|
||||
, m_activeContours(levelsCount)
|
||||
, m_debugId(debugId)
|
||||
{}
|
||||
|
||||
void ContoursBuilder::AddSegment(size_t levelInd, ms::LatLon const & beginPos, ms::LatLon const & endPos)
|
||||
{
|
||||
if (beginPos.EqualDxDy(endPos, mercator::kPointEqualityEps))
|
||||
return;
|
||||
|
||||
CHECK_LESS(levelInd, m_levelsCount, (m_debugId));
|
||||
|
||||
auto contourItBefore = FindContourWithEndPoint(levelInd, beginPos);
|
||||
auto contourItAfter = FindContourWithStartPoint(levelInd, endPos);
|
||||
auto const connectStart = contourItBefore != m_activeContours[levelInd].end();
|
||||
auto const connectEnd = contourItAfter != m_activeContours[levelInd].end();
|
||||
|
||||
if (connectStart && connectEnd && contourItBefore != contourItAfter)
|
||||
{
|
||||
std::move(contourItAfter->m_countour.begin(), contourItAfter->m_countour.end(),
|
||||
std::back_inserter(contourItBefore->m_countour));
|
||||
contourItBefore->m_active = true;
|
||||
m_activeContours[levelInd].erase(contourItAfter);
|
||||
}
|
||||
else if (connectStart)
|
||||
{
|
||||
contourItBefore->m_countour.push_back(endPos);
|
||||
contourItBefore->m_active = true;
|
||||
}
|
||||
else if (connectEnd)
|
||||
{
|
||||
contourItAfter->m_countour.push_front(beginPos);
|
||||
contourItAfter->m_active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_activeContours[levelInd].emplace_back(ContourRaw({beginPos, endPos}));
|
||||
}
|
||||
}
|
||||
|
||||
void ContoursBuilder::BeginLine()
|
||||
{
|
||||
for (auto & contoursList : m_activeContours)
|
||||
for (auto & activeContour : contoursList)
|
||||
activeContour.m_active = false;
|
||||
}
|
||||
|
||||
void ContoursBuilder::EndLine(bool finalLine)
|
||||
{
|
||||
for (size_t levelInd = 0; levelInd < m_levelsCount; ++levelInd)
|
||||
{
|
||||
auto & contours = m_activeContours[levelInd];
|
||||
auto it = contours.begin();
|
||||
while (it != contours.end())
|
||||
{
|
||||
if (!it->m_active || finalLine)
|
||||
{
|
||||
m_finalizedContours[levelInd].push_back(std::move(it->m_countour));
|
||||
it = contours.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContoursBuilder::ActiveContourIter ContoursBuilder::FindContourWithStartPoint(size_t levelInd, ms::LatLon const & pos)
|
||||
{
|
||||
auto & contours = m_activeContours[levelInd];
|
||||
for (auto it = contours.begin(); it != contours.end(); ++it)
|
||||
if (it->m_countour.front().EqualDxDy(pos, mercator::kPointEqualityEps))
|
||||
return it;
|
||||
return contours.end();
|
||||
}
|
||||
|
||||
ContoursBuilder::ActiveContourIter ContoursBuilder::FindContourWithEndPoint(size_t levelInd, ms::LatLon const & pos)
|
||||
{
|
||||
auto & contours = m_activeContours[levelInd];
|
||||
for (auto it = contours.begin(); it != contours.end(); ++it)
|
||||
if (it->m_countour.back().EqualDxDy(pos, mercator::kPointEqualityEps))
|
||||
return it;
|
||||
return contours.end();
|
||||
}
|
||||
} // namespace topography_generator
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "topography_generator/utils/contours.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace topography_generator
|
||||
{
|
||||
class ContoursBuilder
|
||||
{
|
||||
public:
|
||||
ContoursBuilder(size_t levelsCount, std::string const & debugId);
|
||||
|
||||
void AddSegment(size_t levelInd, ms::LatLon const & beginPos, ms::LatLon const & endPos);
|
||||
void BeginLine();
|
||||
void EndLine(bool finalLine);
|
||||
|
||||
template <typename ValueType>
|
||||
void GetContours(ValueType minValue, ValueType valueStep,
|
||||
std::unordered_map<ValueType, std::vector<Contour>> & contours)
|
||||
{
|
||||
contours.clear();
|
||||
for (size_t i = 0; i < m_finalizedContours.size(); ++i)
|
||||
{
|
||||
auto const levelValue = minValue + i * valueStep;
|
||||
auto const & contoursList = m_finalizedContours[i];
|
||||
for (auto const & contour : contoursList)
|
||||
{
|
||||
Contour contourMerc;
|
||||
contourMerc.reserve(contour.size());
|
||||
std::transform(contour.begin(), contour.end(), std::back_inserter(contourMerc),
|
||||
[](ms::LatLon const & pt) { return mercator::FromLatLon(pt); });
|
||||
|
||||
contours[levelValue].emplace_back(std::move(contourMerc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using ContourRaw = std::deque<ms::LatLon>;
|
||||
using ContoursList = std::list<ContourRaw>;
|
||||
|
||||
struct ActiveContour
|
||||
{
|
||||
explicit ActiveContour(ContourRaw && isoline) : m_countour(std::move(isoline)) {}
|
||||
|
||||
ContourRaw m_countour;
|
||||
bool m_active = true;
|
||||
};
|
||||
using ActiveContoursList = std::list<ActiveContour>;
|
||||
using ActiveContourIter = ActiveContoursList::iterator;
|
||||
|
||||
ActiveContourIter FindContourWithStartPoint(size_t levelInd, ms::LatLon const & pos);
|
||||
ActiveContourIter FindContourWithEndPoint(size_t levelInd, ms::LatLon const & pos);
|
||||
|
||||
size_t const m_levelsCount;
|
||||
|
||||
std::vector<ContoursList> m_finalizedContours;
|
||||
std::vector<ActiveContoursList> m_activeContours;
|
||||
|
||||
std::string m_debugId;
|
||||
};
|
||||
} // namespace topography_generator
|
||||
128
tools/topography_generator/marching_squares/marching_squares.hpp
Normal file
128
tools/topography_generator/marching_squares/marching_squares.hpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#pragma once
|
||||
|
||||
#include "topography_generator/marching_squares/contours_builder.hpp"
|
||||
#include "topography_generator/marching_squares/square.hpp"
|
||||
#include "topography_generator/utils/contours.hpp"
|
||||
#include "topography_generator/utils/values_provider.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
namespace topography_generator
|
||||
{
|
||||
template <typename ValueType>
|
||||
class MarchingSquares
|
||||
{
|
||||
public:
|
||||
MarchingSquares(ms::LatLon const & leftBottom, ms::LatLon const & rightTop, double step, ValueType valueStep,
|
||||
ValuesProvider<ValueType> & valuesProvider, std::string const & debugId)
|
||||
: m_leftBottom(leftBottom)
|
||||
, m_rightTop(rightTop)
|
||||
, m_step(step)
|
||||
, m_valueStep(valueStep)
|
||||
, m_valuesProvider(valuesProvider)
|
||||
, m_debugId(debugId)
|
||||
{
|
||||
CHECK_GREATER(m_rightTop.m_lon, m_leftBottom.m_lon, ());
|
||||
CHECK_GREATER(m_rightTop.m_lat, m_leftBottom.m_lat, ());
|
||||
|
||||
m_stepsCountLon = static_cast<size_t>((m_rightTop.m_lon - m_leftBottom.m_lon) / step);
|
||||
m_stepsCountLat = static_cast<size_t>((m_rightTop.m_lat - m_leftBottom.m_lat) / step);
|
||||
|
||||
CHECK_GREATER(m_stepsCountLon, 0, ());
|
||||
CHECK_GREATER(m_stepsCountLat, 0, ());
|
||||
}
|
||||
|
||||
void GenerateContours(Contours<ValueType> & result)
|
||||
{
|
||||
std::vector<ValueType> grid((m_stepsCountLat + 1) * (m_stepsCountLon + 1));
|
||||
|
||||
ScanValuesInRect(result, grid);
|
||||
result.m_valueStep = m_valueStep;
|
||||
|
||||
auto const levelsCount = static_cast<size_t>(result.m_maxValue - result.m_minValue) / m_valueStep;
|
||||
if (levelsCount == 0)
|
||||
{
|
||||
LOG(LINFO, ("Contours can't be generated: min and max values are equal:", result.m_minValue));
|
||||
return;
|
||||
}
|
||||
|
||||
ContoursBuilder contoursBuilder(levelsCount, m_debugId);
|
||||
Square<ValueType> square(result.m_minValue, m_valueStep, m_debugId);
|
||||
|
||||
for (size_t i = 0; i < m_stepsCountLat; ++i)
|
||||
{
|
||||
contoursBuilder.BeginLine();
|
||||
for (size_t j = 0; j < m_stepsCountLon; ++j)
|
||||
{
|
||||
// This point should be calculated _exact_ the same way as in ScanValuesInRect.
|
||||
// leftBottom + m_step doesn't work due to different floating results.
|
||||
|
||||
square.Init(m_leftBottom.m_lon + m_step * j, // Left
|
||||
m_leftBottom.m_lat + m_step * i, // Bottom
|
||||
m_leftBottom.m_lon + m_step * (j + 1), // Right
|
||||
m_leftBottom.m_lat + m_step * (i + 1), // Top
|
||||
|
||||
grid[Idx(i, j)], // LB
|
||||
grid[Idx(i, j + 1)], // RB
|
||||
grid[Idx(i + 1, j)], // LT
|
||||
grid[Idx(i + 1, j + 1)], // RT
|
||||
|
||||
m_valuesProvider.GetInvalidValue());
|
||||
|
||||
square.GenerateSegments(contoursBuilder);
|
||||
}
|
||||
|
||||
contoursBuilder.EndLine(i == m_stepsCountLat - 1 /* finalLine */);
|
||||
}
|
||||
|
||||
contoursBuilder.GetContours(result.m_minValue, result.m_valueStep, result.m_contours);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Idx(size_t iLat, size_t jLon) const { return iLat * (m_stepsCountLon + 1) + jLon; }
|
||||
|
||||
void ScanValuesInRect(Contours<ValueType> & res, std::vector<ValueType> & grid) const
|
||||
{
|
||||
res.m_minValue = res.m_maxValue = m_valuesProvider.GetValue(m_leftBottom);
|
||||
res.m_invalidValuesCount = 0;
|
||||
|
||||
for (size_t i = 0; i <= m_stepsCountLat; ++i)
|
||||
{
|
||||
for (size_t j = 0; j <= m_stepsCountLon; ++j)
|
||||
{
|
||||
ms::LatLon const pos(m_leftBottom.m_lat + m_step * i, m_leftBottom.m_lon + m_step * j);
|
||||
auto const value = m_valuesProvider.GetValue(pos);
|
||||
grid[Idx(i, j)] = value;
|
||||
|
||||
if (value == m_valuesProvider.GetInvalidValue())
|
||||
{
|
||||
++res.m_invalidValuesCount;
|
||||
continue;
|
||||
}
|
||||
if (value < res.m_minValue)
|
||||
res.m_minValue = value;
|
||||
if (value > res.m_maxValue)
|
||||
res.m_maxValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (res.m_invalidValuesCount > 0)
|
||||
LOG(LWARNING, ("Tile", m_debugId, "contains", res.m_invalidValuesCount, "invalid values."));
|
||||
|
||||
Square<ValueType>::ToLevelsRange(m_valueStep, res.m_minValue, res.m_maxValue);
|
||||
|
||||
CHECK_GREATER_OR_EQUAL(res.m_maxValue, res.m_minValue, (m_debugId));
|
||||
}
|
||||
|
||||
ms::LatLon const m_leftBottom;
|
||||
ms::LatLon const m_rightTop;
|
||||
double const m_step;
|
||||
ValueType const m_valueStep;
|
||||
ValuesProvider<ValueType> & m_valuesProvider;
|
||||
|
||||
size_t m_stepsCountLon;
|
||||
size_t m_stepsCountLat;
|
||||
|
||||
std::string m_debugId;
|
||||
};
|
||||
} // namespace topography_generator
|
||||
222
tools/topography_generator/marching_squares/square.hpp
Normal file
222
tools/topography_generator/marching_squares/square.hpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#pragma once
|
||||
|
||||
#include "topography_generator/marching_squares/contours_builder.hpp"
|
||||
|
||||
namespace topography_generator
|
||||
{
|
||||
template <typename ValueType>
|
||||
class Square
|
||||
{
|
||||
public:
|
||||
Square(ValueType minValue, ValueType valueStep, std::string const & debugId)
|
||||
: m_minValue(minValue)
|
||||
, m_valueStep(valueStep)
|
||||
, m_debugId(debugId)
|
||||
{
|
||||
static_assert(std::is_integral<ValueType>::value && std::is_signed<ValueType>::value);
|
||||
}
|
||||
|
||||
void Init(double left, double bottom, double right, double top, ValueType lb, ValueType rb, ValueType lt,
|
||||
ValueType rt, ValueType invalid)
|
||||
{
|
||||
m_isValid = true;
|
||||
|
||||
m_left = left;
|
||||
m_bottom = bottom;
|
||||
m_right = right;
|
||||
m_top = top;
|
||||
|
||||
m_valueLB = GetValue(lb, invalid);
|
||||
m_valueRB = GetValue(rb, invalid);
|
||||
m_valueLT = GetValue(lt, invalid);
|
||||
m_valueRT = GetValue(rt, invalid);
|
||||
}
|
||||
|
||||
void GenerateSegments(ContoursBuilder & builder) const
|
||||
{
|
||||
if (!m_isValid)
|
||||
return;
|
||||
|
||||
ValueType minVal = std::min(m_valueLB, std::min(m_valueLT, std::min(m_valueRT, m_valueRB)));
|
||||
ValueType maxVal = std::max(m_valueLB, std::max(m_valueLT, std::max(m_valueRT, m_valueRB)));
|
||||
|
||||
ToLevelsRange(m_valueStep, minVal, maxVal);
|
||||
|
||||
CHECK_GREATER_OR_EQUAL(minVal, m_minValue, (m_debugId));
|
||||
|
||||
for (auto val = minVal; val < maxVal; val += m_valueStep)
|
||||
AddSegments(val, (val - m_minValue) / m_valueStep, builder);
|
||||
}
|
||||
|
||||
static void ToLevelsRange(ValueType step, ValueType & minVal, ValueType & maxVal)
|
||||
{
|
||||
if (minVal > 0)
|
||||
minVal = step * ((minVal + step - 1) / step);
|
||||
else
|
||||
minVal = step * (minVal / step);
|
||||
|
||||
if (maxVal > 0)
|
||||
maxVal = step * ((maxVal + step) / step);
|
||||
else
|
||||
maxVal = step * ((maxVal + 1) / step);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Rib
|
||||
{
|
||||
None,
|
||||
Left,
|
||||
Top,
|
||||
Right,
|
||||
Bottom,
|
||||
Unclear,
|
||||
};
|
||||
|
||||
ValueType GetValue(ValueType val, ValueType invalid)
|
||||
{
|
||||
// If a contour goes right through the corner of the square false segments can be generated.
|
||||
// Shift the value slightly from the corner.
|
||||
if (val == invalid)
|
||||
{
|
||||
// LOG(LWARNING, ("Invalid value at the position", pos, m_debugId));
|
||||
m_isValid = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
if (abs(val) % m_valueStep == 0)
|
||||
return val + 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
void AddSegments(ValueType val, uint16_t ind, ContoursBuilder & builder) const
|
||||
{
|
||||
// Segment is a vector directed so that higher values is on the right.
|
||||
static std::pair<Rib, Rib> const intersectedRibs[] = {
|
||||
{Rib::None, Rib::None}, // 0000
|
||||
{Rib::Left, Rib::Bottom}, // 0001
|
||||
{Rib::Top, Rib::Left}, // 0010
|
||||
{Rib::Top, Rib::Bottom}, // 0011
|
||||
{Rib::Right, Rib::Top}, // 0100
|
||||
{Rib::Unclear, Rib::Unclear}, // 0101
|
||||
{Rib::Right, Rib::Left}, // 0110
|
||||
{Rib::Right, Rib::Bottom}, // 0111
|
||||
{Rib::Bottom, Rib::Right}, // 1000
|
||||
{Rib::Left, Rib::Right}, // 1001
|
||||
{Rib::Unclear, Rib::Unclear}, // 1010
|
||||
{Rib::Top, Rib::Right}, // 1011
|
||||
{Rib::Bottom, Rib::Top}, // 1100
|
||||
{Rib::Left, Rib::Top}, // 1101
|
||||
{Rib::Bottom, Rib::Left}, // 1110
|
||||
{Rib::None, Rib::None}, // 1111
|
||||
};
|
||||
|
||||
uint8_t const pattern = (m_valueLB > val ? 1u : 0u) | ((m_valueLT > val ? 1u : 0u) << 1u) |
|
||||
((m_valueRT > val ? 1u : 0u) << 2u) | ((m_valueRB > val ? 1u : 0u) << 3u);
|
||||
|
||||
auto const ribs = intersectedRibs[pattern];
|
||||
|
||||
if (ribs.first == Rib::None)
|
||||
return;
|
||||
|
||||
if (ribs.first != Rib::Unclear)
|
||||
{
|
||||
builder.AddSegment(ind, InterpolatePoint(ribs.first, val), InterpolatePoint(ribs.second, val));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const leftPos = InterpolatePoint(Rib::Left, val);
|
||||
auto const rightPos = InterpolatePoint(Rib::Right, val);
|
||||
auto const bottomPos = InterpolatePoint(Rib::Bottom, val);
|
||||
auto const topPos = InterpolatePoint(Rib::Top, val);
|
||||
|
||||
ValueType const avVal = (m_valueLB + m_valueLT + m_valueRT + m_valueRB) / 4;
|
||||
if (avVal > val)
|
||||
{
|
||||
if (m_valueLB > val)
|
||||
{
|
||||
builder.AddSegment(ind, leftPos, topPos);
|
||||
builder.AddSegment(ind, rightPos, bottomPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddSegment(ind, bottomPos, leftPos);
|
||||
builder.AddSegment(ind, topPos, rightPos);
|
||||
}
|
||||
}
|
||||
else if (m_valueLB > val)
|
||||
{
|
||||
builder.AddSegment(ind, leftPos, bottomPos);
|
||||
builder.AddSegment(ind, rightPos, topPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddSegment(ind, topPos, leftPos);
|
||||
builder.AddSegment(ind, bottomPos, rightPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ms::LatLon InterpolatePoint(Square::Rib rib, ValueType val) const
|
||||
{
|
||||
double val1;
|
||||
double val2;
|
||||
double lat;
|
||||
double lon;
|
||||
|
||||
switch (rib)
|
||||
{
|
||||
case Rib::Left:
|
||||
val1 = static_cast<double>(m_valueLB);
|
||||
val2 = static_cast<double>(m_valueLT);
|
||||
lon = m_left;
|
||||
break;
|
||||
case Rib::Right:
|
||||
val1 = static_cast<double>(m_valueRB);
|
||||
val2 = static_cast<double>(m_valueRT);
|
||||
lon = m_right;
|
||||
break;
|
||||
case Rib::Top:
|
||||
val1 = static_cast<double>(m_valueLT);
|
||||
val2 = static_cast<double>(m_valueRT);
|
||||
lat = m_top;
|
||||
break;
|
||||
case Rib::Bottom:
|
||||
val1 = static_cast<double>(m_valueLB);
|
||||
val2 = static_cast<double>(m_valueRB);
|
||||
lat = m_bottom;
|
||||
break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
CHECK_NOT_EQUAL(val, val2, (m_debugId));
|
||||
double const coeff = (val1 - val) / (val - val2);
|
||||
|
||||
switch (rib)
|
||||
{
|
||||
case Rib::Left:
|
||||
case Rib::Right: lat = (m_bottom + m_top * coeff) / (1 + coeff); break;
|
||||
case Rib::Bottom:
|
||||
case Rib::Top: lon = (m_left + m_right * coeff) / (1 + coeff); break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
return {lat, lon};
|
||||
}
|
||||
|
||||
double m_left;
|
||||
double m_right;
|
||||
double m_bottom;
|
||||
double m_top;
|
||||
|
||||
ValueType m_valueLB;
|
||||
ValueType m_valueLT;
|
||||
ValueType m_valueRT;
|
||||
ValueType m_valueRB;
|
||||
|
||||
ValueType m_minValue;
|
||||
ValueType m_valueStep;
|
||||
std::string m_debugId;
|
||||
|
||||
bool m_isValid;
|
||||
};
|
||||
} // namespace topography_generator
|
||||
Loading…
Add table
Add a link
Reference in a new issue