Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -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

View file

@ -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

View 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

View 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