diff --git a/editor/editorNmt.cpp b/editor/editorNmt.cpp index ce29f607..a71e0e2a 100644 --- a/editor/editorNmt.cpp +++ b/editor/editorNmt.cpp @@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/. #include #include +#include #include #include #include @@ -33,6 +34,12 @@ char const *const cache_folder{"editor/cache/nmt/grid1/"}; int constexpr worker_count{2}; // the service is quick, but isn't to be hammered int constexpr border{1}; // pixels of the neighbouring squares fetched around a square, so the heights interpolate across its edges float constexpr no_data{-1000.f}; // the model gives far lower values outside of the country +double constexpr retry_after{15.0}; // s, a square the service didn't give is asked for again + +double seconds() +{ + return std::chrono::duration(std::chrono::steady_clock::now().time_since_epoch()).count(); +} // the name of the model among the names the capabilities of the service list: the first which says it's a terrain model std::string coverage_of(std::string const &Capabilities) @@ -231,12 +238,17 @@ bool editor_nmt::request(glm::dvec2 const &Min, glm::dvec2 const &Max) auto const low{key_of(m_east - Max.x, m_north + Min.y)}; auto const high{key_of(m_east - Min.x, m_north + Max.y)}; auto const middle{key_of(m_east - 0.5 * (Min.x + Max.x), m_north + 0.5 * (Min.y + Max.y))}; + auto const now{seconds()}; for (auto e = low.first; e <= high.first; ++e) for (auto n = low.second; n <= high.second; ++n) { key const square{e, n}; - if (m_squares.contains(square)) - continue; + if (auto const found{m_squares.find(square)}; found != m_squares.end()) + { + if (false == found->second->failed || found->second->outside || now - found->second->failed_at < retry_after) + continue; + m_squares.erase(found); + } all = false; if (false == m_queued.contains(square)) wanted.push_back(square); @@ -265,10 +277,52 @@ std::size_t editor_nmt::waiting() const return m_queued.size(); } -std::string editor_nmt::error() const +bool editor_nmt::retrying(glm::dvec2 const &Min, glm::dvec2 const &Max) const { std::lock_guard lock{m_mutex}; - return m_error; + auto const low{key_of(m_east - Max.x, m_north + Min.y)}; + auto const high{key_of(m_east - Min.x, m_north + Max.y)}; + for (auto e = low.first; e <= high.first; ++e) + for (auto n = low.second; n <= high.second; ++n) + if (auto const found{m_squares.find({e, n})}; found != m_squares.end() && found->second->failed && false == found->second->outside) + return true; + return false; +} + +void editor_nmt::retry() +{ + std::lock_guard lock{m_mutex}; + std::erase_if(m_squares, [](auto const &Square) { return Square.second->failed && false == Square.second->outside; }); +} + +void editor_nmt::cancel() +{ + std::lock_guard lock{m_mutex}; + for (auto const &square : m_queue) + m_queued.erase(square); + m_queue.clear(); +} + +std::string editor_nmt::error(bool *Again) const +{ + std::lock_guard lock{m_mutex}; + std::string result; + bool outside{false}; + if (Again != nullptr) + *Again = false; + for (auto const &[key, square] : m_squares) + { + if (false == square->failed) + continue; + if (Again != nullptr && false == square->outside) + *Again = true; + if (result.empty() || (square->outside && false == outside)) + { + result = square->error; + outside = square->outside; + } + } + return result; } void editor_nmt::clear() @@ -300,7 +354,6 @@ void editor_nmt::work() continue; // cleared meanwhile m_queued.erase(square); m_squares[square] = std::move(fetched); - m_error = error; } } } @@ -324,6 +377,8 @@ std::shared_ptr editor_nmt::fetch(key const &Key, std: { Error = STRN("This build can't fetch from the internet"); result->failed = true; + result->outside = true; + result->error = Error; return result; } editor_http::client client; @@ -365,10 +420,18 @@ std::shared_ptr editor_nmt::fetch(key const &Key, std: } if (stop()) break; + if (std::string const text(data.begin(), data.end()); text.find("outside requested coverage") != std::string::npos) + { + Error = STRN("The place lies outside of the terrain model of Poland: check the location of the scenery"); + result->outside = true; + break; + } if (Error.empty()) Error = STRN("The service gave no terrain model"); } WriteLog("Editor: NMT square " + std::to_string(Key.first) + "," + std::to_string(Key.second) + " not fetched: " + Error, logtype::generic); result->failed = true; + result->error = Error; + result->failed_at = seconds(); return result; } diff --git a/editor/editorNmt.hpp b/editor/editorNmt.hpp index fdddac30..9d8fe330 100644 --- a/editor/editorNmt.hpp +++ b/editor/editorNmt.hpp @@ -46,8 +46,15 @@ class editor_nmt bool request(glm::dvec2 const &Min, glm::dvec2 const &Max); // squares asked for and not in yet std::size_t waiting() const; - // what the last fetch said went wrong, empty when it went right - std::string error() const; + // a square under the rectangle the service didn't give, which is asked for again after a while + bool retrying(glm::dvec2 const &Min, glm::dvec2 const &Max) const; + // the squares the service didn't give asked for again at once + void retry(); + // the squares waiting aren't fetched; those in stay + void cancel(); + // why squares in memory have no data, empty when all came; a place outside of the model is told first. Again: whether + // some of them are asked for again + std::string error(bool *Again = nullptr) const; // the squares dropped from the memory, and those waiting; the files on the disk stay void clear(); // whether this build can fetch from the service at all @@ -67,6 +74,9 @@ class editor_nmt int height{0}; std::vector heights; // rows from the north, columns from the west bool failed{false}; // the service gave nothing usable: the square has no data + bool outside{false}; // failed for it lies outside of the model: asked again, it gives nothing either + double failed_at{0.0}; // s + std::string error; }; key key_of(double const East, double const North) const; @@ -80,7 +90,6 @@ class editor_nmt std::set m_queued; std::map> m_squares; std::string m_coverage; // the name of the model in the service, read from its capabilities once - std::string m_error; double m_north{0.0}; double m_east{0.0}; bool m_stop{false};