File contour_gen.hpp
File List > contour > contour_gen.hpp
Go to the documentation of this file
#pragma once
#include "config_input/config_input.hpp"
#include "contour.hpp"
#include "utilities/timer.hpp"
template <typename T>
GridGraph<std::set<double>> identify_contours(const GeoGrid<T>& grid, T contour_interval) {
TimeFunction timer("identifying contours");
GridGraph contour_heights = GridGraph<std::set<double>>(grid);
#pragma omp parallel for
for (size_t i = 0; i < grid.height(); i++) {
for (size_t j = 0; j < grid.width(); j++) {
Coordinate2D<size_t> coord = {j, i};
for (Direction2D dir : {Direction2D::DOWN, Direction2D::RIGHT}) {
LineCoord2D<size_t> line_coord = {coord, dir};
if (contour_heights.in_bounds(line_coord)) {
contour_heights[line_coord] = get_contour_heights(
{grid[line_coord.start()], grid[line_coord.end()]}, contour_interval);
}
}
}
}
return contour_heights;
}
inline std::vector<Contour> join_contours(std::vector<Contour> contours, double max_dist = 15.0) {
const double max_dist2 = max_dist * max_dist;
// Only consider the two cases that allow joining without reversal:
// a.back ↔ b.front (append) and a.front ↔ b.back (prepend)
struct JoiningOption {
bool use_front_a; // true = prepend to a.front, false = append to a.back
bool use_front_b; // true = b.front matches, false = b.back matches
};
static constexpr JoiningOption cases[] = {
{false, true}, // a.back ↔ b.front (append b forward)
{true, false}, // a.front ↔ b.back (prepend b forward)
};
bool did_any_join = true;
std::vector<Contour> next_round;
while (did_any_join) {
did_any_join = false;
next_round.clear();
for (auto& src : contours) {
const auto& b = src.points();
double best_d2 = max_dist2;
int best_idx = -1;
JoiningOption best_case{};
for (int i = 0; i < (int)next_round.size(); ++i) {
const auto& a = next_round[i].points();
for (JoiningOption c : cases) {
const auto& pa = (c.use_front_a ? a.front() : a.back());
const auto& pb = (c.use_front_b ? b.front() : b.back());
double d2 = (pa - pb).magnitude_sqd();
if (d2 < best_d2) {
best_d2 = d2;
best_idx = i;
best_case = c;
}
}
}
if (best_idx >= 0) {
auto& acc = next_round[best_idx].points();
// Join without reversing: only two cases are considered
if (best_case.use_front_a) {
// a.front ↔ b.back: prepend b forward (skip duplicate join point b.back)
if (b.size() > 1) {
acc.insert(acc.begin(), b.begin(), b.end() - 1);
}
} else {
// a.back ↔ b.front: append b forward (skip duplicate join point b.front)
if (b.size() > 1) {
acc.insert(acc.end(), b.begin() + 1, b.end());
}
}
did_any_join = true;
} else {
next_round.emplace_back(std::move(src));
}
}
contours = std::move(next_round);
}
return contours;
}
inline std::vector<Contour> trim_contours(const std::vector<Contour>& contours,
const Extent2D& bounds) {
std::vector<Contour> trimmed_contours;
for (const Contour& c : contours) {
Contour trimmed_contour(c.height(), std::vector<Coordinate2D<double>>{});
for (const Coordinate2D<double>& point : c.points()) {
if (bounds.contains(point.x(), point.y())) {
trimmed_contour.push_back(point);
} else if (trimmed_contour.points().size() > 0) {
trimmed_contours.emplace_back(std::move(trimmed_contour));
trimmed_contour = Contour(c.height(), std::vector<Coordinate2D<double>>{});
}
}
if (trimmed_contour.points().size() > 0) {
trimmed_contours.emplace_back(std::move(trimmed_contour));
}
}
return trimmed_contours;
}
template <typename T>
std::vector<Contour> generate_contours(const GeoGrid<T>& grid, const ContourConfigs& contour_config,
ProgressTracker progress_tracker) {
TimeFunction timer("generating contours", &progress_tracker);
GridGraph is_contour = identify_contours(grid, contour_config.min_interval);
std::vector<Contour> contours;
for (size_t i = 0; i < is_contour.height(); i++) {
for (size_t j = 0; j < is_contour.width(); j++) {
Coordinate2D<size_t> coord = {j, i};
for (Direction2D dir : {Direction2D::DOWN, Direction2D::RIGHT}) {
LineCoord2D<size_t> line_coord = {coord, dir};
if (is_contour.in_bounds(line_coord)) {
for (double height : std::set<double>(is_contour[line_coord])) {
Contour c = Contour::FromGridGraph(line_coord, height, grid, is_contour);
// TODO use length instead of number of points
if (c.points().size() > contour_config.pick_from_height(c.height()).min_points) {
contours.emplace_back(std::move(c));
}
}
}
}
}
}
progress_tracker.text_update("Generated " + std::to_string(contours.size()) + " contours");
return contours;
}
inline GeoGrid<std::optional<std::byte>> generate_naive_contours(const GeoGrid<double>& ground) {
GeoGrid<std::optional<std::byte>> naive_countours = GeoGrid<std::optional<std::byte>>(
ground.width(), ground.height(), GeoTransform(ground.transform()),
GeoProjection(ground.projection()));
double contour_interval = 2.5;
for (size_t i = 1; i < ground.height() - 1; i++) {
for (size_t j = 1; j < ground.width() - 1; j++) {
double z = ground[{i, j}];
double z_north = ground[{i - 1, j}];
double z_south = ground[{i + 1, j}];
double z_west = ground[{i, j - 1}];
double z_east = ground[{i, j + 1}];
bool is_countour = crosses_contour({z, z_north}, contour_interval) ||
crosses_contour({z, z_south}, contour_interval) ||
crosses_contour({z, z_west}, contour_interval) ||
crosses_contour({z, z_east}, contour_interval);
naive_countours[{i, j}] = is_countour ? std::optional<std::byte>{std::byte{0}} : std::nullopt;
}
}
return naive_countours;
}