mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-05 15:42:31 +01:00
feat(editor): a map of Poland to pick a place on, shared by the scenery wizard and the terrain model
This commit is contained in:
@@ -204,6 +204,7 @@ set(SOURCES
|
|||||||
"editor/editorOrthophoto.cpp"
|
"editor/editorOrthophoto.cpp"
|
||||||
"editor/editorHttp.cpp"
|
"editor/editorHttp.cpp"
|
||||||
"editor/editorNmt.cpp"
|
"editor/editorNmt.cpp"
|
||||||
|
"editor/editorGeoMap.cpp"
|
||||||
"editor/editorPreviews.cpp"
|
"editor/editorPreviews.cpp"
|
||||||
"editor/editorIcons.cpp"
|
"editor/editorIcons.cpp"
|
||||||
"editor/editorClips.cpp"
|
"editor/editorClips.cpp"
|
||||||
|
|||||||
327
editor/editorGeoMap.cpp
Normal file
327
editor/editorGeoMap.cpp
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "editor/editorGeoMap.hpp"
|
||||||
|
#include "editor/editorFormat.hpp"
|
||||||
|
#include "editor/editorOrthophoto.hpp"
|
||||||
|
#include "utilities/translation.h"
|
||||||
|
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include "rendering/renderer.h"
|
||||||
|
#include "stb/stb_image.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
double constexpr kSemiMajor{6378137.0};
|
||||||
|
double constexpr kFlattening{1.0 / 298.257222101};
|
||||||
|
double constexpr kScale{0.9993};
|
||||||
|
double constexpr kFalseEasting{500000.0};
|
||||||
|
double constexpr kFalseNorthing{-5300000.0};
|
||||||
|
double constexpr kCentralMeridian{19.0};
|
||||||
|
glm::dvec2 const kPolandLow{140000.0, 120000.0};
|
||||||
|
glm::dvec2 const kPolandHigh{890000.0, 800000.0};
|
||||||
|
std::string const kMapService{"https://mapy.geoportal.gov.pl/wss/ext/OSM/BaseMap/service"};
|
||||||
|
|
||||||
|
double seconds()
|
||||||
|
{
|
||||||
|
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace editor_geo
|
||||||
|
{
|
||||||
|
|
||||||
|
glm::dvec2 to_puwg(double const Latitude, double const Longitude)
|
||||||
|
{
|
||||||
|
auto const e2{2.0 * kFlattening - kFlattening * kFlattening};
|
||||||
|
auto const ep2{e2 / (1.0 - e2)};
|
||||||
|
auto const phi{glm::radians(Latitude)};
|
||||||
|
auto const sine{std::sin(phi)};
|
||||||
|
auto const cosine{std::cos(phi)};
|
||||||
|
auto const n{kSemiMajor / std::sqrt(1.0 - e2 * sine * sine)};
|
||||||
|
auto const t{std::tan(phi) * std::tan(phi)};
|
||||||
|
auto const c{ep2 * cosine * cosine};
|
||||||
|
auto const a{glm::radians(Longitude - kCentralMeridian) * cosine};
|
||||||
|
auto const m{kSemiMajor * ((1.0 - e2 / 4.0 - 3.0 * e2 * e2 / 64.0 - 5.0 * e2 * e2 * e2 / 256.0) * phi - (3.0 * e2 / 8.0 + 3.0 * e2 * e2 / 32.0 + 45.0 * e2 * e2 * e2 / 1024.0) * std::sin(2.0 * phi) +
|
||||||
|
(15.0 * e2 * e2 / 256.0 + 45.0 * e2 * e2 * e2 / 1024.0) * std::sin(4.0 * phi) - (35.0 * e2 * e2 * e2 / 3072.0) * std::sin(6.0 * phi))};
|
||||||
|
auto const x{kScale * n * (a + (1.0 - t + c) * std::pow(a, 3) / 6.0 + (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * ep2) * std::pow(a, 5) / 120.0)};
|
||||||
|
auto const y{kScale * (m + n * std::tan(phi) * (a * a / 2.0 + (5.0 - t + 9.0 * c + 4.0 * c * c) * std::pow(a, 4) / 24.0 + (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * ep2) * std::pow(a, 6) / 720.0))};
|
||||||
|
return {kFalseEasting + x, y + kFalseNorthing};
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::dvec2 to_degrees(glm::dvec2 const &Point)
|
||||||
|
{
|
||||||
|
auto const e2{2.0 * kFlattening - kFlattening * kFlattening};
|
||||||
|
auto const ep2{e2 / (1.0 - e2)};
|
||||||
|
auto const x{Point.x - kFalseEasting};
|
||||||
|
auto const m{(Point.y - kFalseNorthing) / kScale};
|
||||||
|
auto const mu{m / (kSemiMajor * (1.0 - e2 / 4.0 - 3.0 * e2 * e2 / 64.0 - 5.0 * e2 * e2 * e2 / 256.0))};
|
||||||
|
auto const e1{(1.0 - std::sqrt(1.0 - e2)) / (1.0 + std::sqrt(1.0 - e2))};
|
||||||
|
auto const phi1{mu + (3.0 * e1 / 2.0 - 27.0 * std::pow(e1, 3) / 32.0) * std::sin(2.0 * mu) + (21.0 * e1 * e1 / 16.0 - 55.0 * std::pow(e1, 4) / 32.0) * std::sin(4.0 * mu) +
|
||||||
|
(151.0 * std::pow(e1, 3) / 96.0) * std::sin(6.0 * mu) + (1097.0 * std::pow(e1, 4) / 512.0) * std::sin(8.0 * mu)};
|
||||||
|
auto const sine{std::sin(phi1)};
|
||||||
|
auto const c1{ep2 * std::cos(phi1) * std::cos(phi1)};
|
||||||
|
auto const t1{std::tan(phi1) * std::tan(phi1)};
|
||||||
|
auto const n1{kSemiMajor / std::sqrt(1.0 - e2 * sine * sine)};
|
||||||
|
auto const r1{kSemiMajor * (1.0 - e2) / std::pow(1.0 - e2 * sine * sine, 1.5)};
|
||||||
|
auto const d{x / (n1 * kScale)};
|
||||||
|
auto const phi{phi1 - (n1 * std::tan(phi1) / r1) * (d * d / 2.0 - (5.0 + 3.0 * t1 + 10.0 * c1 - 4.0 * c1 * c1 - 9.0 * ep2) * std::pow(d, 4) / 24.0 +
|
||||||
|
(61.0 + 90.0 * t1 + 298.0 * c1 + 45.0 * t1 * t1 - 252.0 * ep2 - 3.0 * c1 * c1) * std::pow(d, 6) / 720.0)};
|
||||||
|
auto const lambda{(d - (1.0 + 2.0 * t1 + c1) * std::pow(d, 3) / 6.0 + (5.0 - 2.0 * c1 + 28.0 * t1 - 3.0 * c1 * c1 + 8.0 * ep2 + 24.0 * t1 * t1) * std::pow(d, 5) / 120.0) / std::cos(phi1)};
|
||||||
|
return {glm::degrees(phi), kCentralMeridian + glm::degrees(lambda)};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_poland(glm::dvec2 const &Point)
|
||||||
|
{
|
||||||
|
return Point.x >= kPolandLow.x && Point.x <= kPolandHigh.x && Point.y >= kPolandLow.y && Point.y <= kPolandHigh.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_place(std::string const &Text, glm::dvec2 &Point)
|
||||||
|
{
|
||||||
|
// the letters and the signs around the numbers out, as of "52.2297° N, 21.0122° E" or "X: 456787 Y: 579897"
|
||||||
|
std::string text;
|
||||||
|
for (unsigned char const letter : Text)
|
||||||
|
text += std::isdigit(letter) || letter == '.' || letter == ',' || letter == '-' ? static_cast<char>(letter) : ' ';
|
||||||
|
std::vector<std::string> words;
|
||||||
|
for (std::size_t at{0}; at < text.size();)
|
||||||
|
{
|
||||||
|
auto const begin{text.find_first_not_of(" ,", at)};
|
||||||
|
if (begin == std::string::npos)
|
||||||
|
break;
|
||||||
|
auto end{text.find(' ', begin)};
|
||||||
|
if (end == std::string::npos)
|
||||||
|
end = text.size();
|
||||||
|
auto word{text.substr(begin, end - begin)};
|
||||||
|
while (false == word.empty() && word.back() == ',')
|
||||||
|
word.pop_back();
|
||||||
|
words.push_back(word);
|
||||||
|
at = end;
|
||||||
|
}
|
||||||
|
std::vector<std::string> numbers;
|
||||||
|
if (words.size() == 1)
|
||||||
|
{
|
||||||
|
// one word: the comma parts the numbers, or the numbers and their decimals
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
for (std::size_t at{0};;)
|
||||||
|
{
|
||||||
|
auto const comma{words[0].find(',', at)};
|
||||||
|
parts.push_back(words[0].substr(at, comma == std::string::npos ? std::string::npos : comma - at));
|
||||||
|
if (comma == std::string::npos)
|
||||||
|
break;
|
||||||
|
at = comma + 1;
|
||||||
|
}
|
||||||
|
if (parts.size() == 2)
|
||||||
|
numbers = parts;
|
||||||
|
else if (parts.size() == 4)
|
||||||
|
numbers = {parts[0] + "." + parts[1], parts[2] + "." + parts[3]};
|
||||||
|
}
|
||||||
|
else if (words.size() == 2)
|
||||||
|
for (auto word : words)
|
||||||
|
{
|
||||||
|
std::replace(word.begin(), word.end(), ',', '.');
|
||||||
|
numbers.push_back(word);
|
||||||
|
}
|
||||||
|
if (numbers.size() != 2)
|
||||||
|
return false;
|
||||||
|
double values[2];
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
char *end{nullptr};
|
||||||
|
values[i] = std::strtod(numbers[i].c_str(), &end);
|
||||||
|
if (end == numbers[i].c_str() || *end != '\0')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto const latitude = [](double const Value) { return Value >= 48.0 && Value <= 56.0; };
|
||||||
|
auto const longitude = [](double const Value) { return Value >= 13.0 && Value <= 25.0; };
|
||||||
|
if (latitude(values[0]) && longitude(values[1]))
|
||||||
|
Point = to_puwg(values[0], values[1]);
|
||||||
|
else if (longitude(values[0]) && latitude(values[1]))
|
||||||
|
Point = to_puwg(values[1], values[0]);
|
||||||
|
else if (values[0] > 1000.0 && values[1] > 1000.0)
|
||||||
|
Point = {values[1], values[0]};
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return in_poland(Point);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace editor_geo
|
||||||
|
|
||||||
|
editor_map::~editor_map()
|
||||||
|
{
|
||||||
|
if (m_request.valid())
|
||||||
|
m_request.wait();
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_map::release()
|
||||||
|
{
|
||||||
|
if (m_texture != 0 && GfxRenderer && GfxRenderer->GetImguiRenderer() != nullptr)
|
||||||
|
GfxRenderer->GetImguiRenderer()->Release_Image(m_texture);
|
||||||
|
m_texture = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_map::pick(glm::dvec2 const &Point)
|
||||||
|
{
|
||||||
|
m_point = Point;
|
||||||
|
m_picked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_map::show(glm::dvec2 const &Point, double const Scale)
|
||||||
|
{
|
||||||
|
m_fitted = true;
|
||||||
|
m_centre = Point;
|
||||||
|
m_scale = std::min(m_scale, Scale);
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool editor_map::render(glm::vec2 const &Size)
|
||||||
|
{
|
||||||
|
upload();
|
||||||
|
auto const origin{ImGui::GetCursorScreenPos()};
|
||||||
|
if (false == m_fitted)
|
||||||
|
{
|
||||||
|
m_fitted = true;
|
||||||
|
m_centre = (kPolandLow + kPolandHigh) * 0.5;
|
||||||
|
m_scale = std::max((kPolandHigh.x - kPolandLow.x) / Size.x, (kPolandHigh.y - kPolandLow.y) / Size.y);
|
||||||
|
}
|
||||||
|
ImGui::InvisibleButton("##map", ImVec2(Size.x, Size.y));
|
||||||
|
auto const hovered{ImGui::IsItemHovered()};
|
||||||
|
auto const active{ImGui::IsItemActive()};
|
||||||
|
auto const &io{ImGui::GetIO()};
|
||||||
|
glm::vec2 const mouse{io.MousePos.x, io.MousePos.y};
|
||||||
|
glm::vec2 const middle{origin.x + Size.x * 0.5f, origin.y + Size.y * 0.5f};
|
||||||
|
auto const to_world = [&](glm::vec2 const &Screen) { return glm::dvec2{m_centre.x + (Screen.x - middle.x) * m_scale, m_centre.y - (Screen.y - middle.y) * m_scale}; };
|
||||||
|
auto const to_screen = [&](glm::dvec2 const &World) { return ImVec2(middle.x + static_cast<float>((World.x - m_centre.x) / m_scale), middle.y - static_cast<float>((World.y - m_centre.y) / m_scale)); };
|
||||||
|
|
||||||
|
if (hovered && io.MouseWheel != 0.f)
|
||||||
|
{
|
||||||
|
auto const under{to_world(mouse)};
|
||||||
|
m_scale = std::clamp(m_scale * std::pow(0.8, static_cast<double>(io.MouseWheel)), 0.25, 2000.0);
|
||||||
|
m_centre = {under.x - (mouse.x - middle.x) * m_scale, under.y + (mouse.y - middle.y) * m_scale};
|
||||||
|
m_dirty = true;
|
||||||
|
m_changed = seconds();
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemActivated())
|
||||||
|
{
|
||||||
|
m_pressed = mouse;
|
||||||
|
m_dragging = false;
|
||||||
|
}
|
||||||
|
if (active)
|
||||||
|
{
|
||||||
|
if (glm::length(mouse - m_pressed) > 4.f)
|
||||||
|
m_dragging = true;
|
||||||
|
if (m_dragging && (io.MouseDelta.x != 0.f || io.MouseDelta.y != 0.f))
|
||||||
|
{
|
||||||
|
m_centre.x -= io.MouseDelta.x * m_scale;
|
||||||
|
m_centre.y += io.MouseDelta.y * m_scale;
|
||||||
|
m_dirty = true;
|
||||||
|
m_changed = seconds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto const clicked{ImGui::IsItemDeactivated() && false == m_dragging};
|
||||||
|
if (clicked)
|
||||||
|
pick(to_world(mouse));
|
||||||
|
|
||||||
|
if (m_dirty && seconds() - m_changed > 0.25 && false == m_request.valid())
|
||||||
|
request(Size);
|
||||||
|
|
||||||
|
auto *drawlist{ImGui::GetWindowDrawList()};
|
||||||
|
drawlist->PushClipRect(origin, ImVec2(origin.x + Size.x, origin.y + Size.y), true);
|
||||||
|
drawlist->AddRectFilled(origin, ImVec2(origin.x + Size.x, origin.y + Size.y), IM_COL32(200, 214, 222, 255));
|
||||||
|
if (m_texture != 0)
|
||||||
|
drawlist->AddImage(static_cast<ImTextureID>(m_texture), to_screen({m_image_low.x, m_image_high.y}), to_screen({m_image_high.x, m_image_low.y}), ImVec2(0, 1), ImVec2(1, 0));
|
||||||
|
if (m_picked)
|
||||||
|
{
|
||||||
|
auto const at{to_screen(m_point)};
|
||||||
|
auto const colour{IM_COL32(220, 30, 30, 255)};
|
||||||
|
drawlist->AddCircle(at, 10.f, colour, 24, 2.5f);
|
||||||
|
drawlist->AddLine(ImVec2(at.x - 16.f, at.y), ImVec2(at.x + 16.f, at.y), colour, 2.f);
|
||||||
|
drawlist->AddLine(ImVec2(at.x, at.y - 16.f), ImVec2(at.x, at.y + 16.f), colour, 2.f);
|
||||||
|
}
|
||||||
|
std::string info;
|
||||||
|
if (hovered)
|
||||||
|
{
|
||||||
|
auto const world{to_world(mouse)};
|
||||||
|
auto const degrees{editor_geo::to_degrees(world)};
|
||||||
|
info = format(STR_C("E %.3f km, N %.3f km (%.5f N, %.5f E)"), world.x / 1000.0, world.y / 1000.0, degrees.x, degrees.y);
|
||||||
|
}
|
||||||
|
if (m_request.valid())
|
||||||
|
info += (info.empty() ? "" : " ") + std::string{STR_C("loading the map...")};
|
||||||
|
else if (false == m_error.empty())
|
||||||
|
info += (info.empty() ? "" : " ") + m_error;
|
||||||
|
if (false == info.empty())
|
||||||
|
{
|
||||||
|
auto const size{ImGui::CalcTextSize(info.c_str())};
|
||||||
|
drawlist->AddRectFilled(ImVec2(origin.x + 4.f, origin.y + 4.f), ImVec2(origin.x + 12.f + size.x, origin.y + 8.f + size.y), IM_COL32(0, 0, 0, 170), 4.f);
|
||||||
|
drawlist->AddText(ImVec2(origin.x + 8.f, origin.y + 6.f), IM_COL32(255, 255, 255, 240), info.c_str());
|
||||||
|
}
|
||||||
|
drawlist->PopClipRect();
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_map::request(glm::vec2 const &Size)
|
||||||
|
{
|
||||||
|
m_dirty = false;
|
||||||
|
auto const width{std::clamp(static_cast<int>(Size.x), 64, 2048)};
|
||||||
|
auto const height{std::clamp(static_cast<int>(Size.y), 64, 2048)};
|
||||||
|
glm::dvec2 const half{width * 0.5 * m_scale, height * 0.5 * m_scale};
|
||||||
|
auto const low{m_centre - half};
|
||||||
|
auto const high{m_centre + half};
|
||||||
|
char bbox[160];
|
||||||
|
std::snprintf(bbox, sizeof(bbox), "%.1f,%.1f,%.1f,%.1f", low.x, low.y, high.x, high.y);
|
||||||
|
auto const url{kMapService + "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=osm&STYLES=&SRS=EPSG:2180&BBOX=" + bbox + "&WIDTH=" + std::to_string(width) + "&HEIGHT=" + std::to_string(height) + "&FORMAT=image/png"};
|
||||||
|
m_request = std::async(std::launch::async, [url, low, high]() {
|
||||||
|
image result;
|
||||||
|
result.low = low;
|
||||||
|
result.high = high;
|
||||||
|
std::vector<std::uint8_t> data;
|
||||||
|
if (false == editor_http_get(url, data, result.error))
|
||||||
|
return result;
|
||||||
|
int w{0}, h{0}, components{0};
|
||||||
|
auto *pixels{stbi_load_from_memory(data.data(), static_cast<int>(data.size()), &w, &h, &components, 4)};
|
||||||
|
if (pixels == nullptr)
|
||||||
|
{
|
||||||
|
result.error = STR("The map service sent no image");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.width = w;
|
||||||
|
result.height = h;
|
||||||
|
result.pixels.assign(pixels, pixels + static_cast<std::size_t>(w) * h * 4);
|
||||||
|
stbi_image_free(pixels);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_map::upload()
|
||||||
|
{
|
||||||
|
if (false == m_request.valid() || m_request.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
|
||||||
|
return;
|
||||||
|
auto result{m_request.get()};
|
||||||
|
if (false == result.error.empty() || result.pixels.empty())
|
||||||
|
{
|
||||||
|
m_error = result.error.empty() ? std::string{STR("The map service sent no image")} : STR("No map: ") + result.error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_error.clear();
|
||||||
|
release();
|
||||||
|
auto *renderer{GfxRenderer ? GfxRenderer->GetImguiRenderer() : nullptr};
|
||||||
|
m_texture = renderer != nullptr ? renderer->Create_Image(result.pixels.data(), result.width, result.height) : 0;
|
||||||
|
m_image_low = result.low;
|
||||||
|
m_image_high = result.high;
|
||||||
|
}
|
||||||
80
editor/editorGeoMap.hpp
Normal file
80
editor/editorGeoMap.hpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <future>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
// the place of a scenery in Poland: PUWG 1992 (EPSG:2180), the metres of the Polish maps, as (east, north)
|
||||||
|
namespace editor_geo
|
||||||
|
{
|
||||||
|
|
||||||
|
glm::dvec2 to_puwg(double const Latitude, double const Longitude);
|
||||||
|
// latitude, longitude
|
||||||
|
glm::dvec2 to_degrees(glm::dvec2 const &Point);
|
||||||
|
// two numbers as a map gives them: degrees (latitude, longitude, as the online maps copy them) or the X and Y of PUWG 1992
|
||||||
|
// (north, then east, as geoportal.gov.pl shows them); false when they're neither, or the place isn't in Poland
|
||||||
|
bool parse_place(std::string const &Text, glm::dvec2 &Point);
|
||||||
|
bool in_poland(glm::dvec2 const &Point);
|
||||||
|
|
||||||
|
} // namespace editor_geo
|
||||||
|
|
||||||
|
// the map of Poland to pick a place on: the base map of OpenStreetMap from geoportal.gov.pl, the wheel zooms it, a drag moves
|
||||||
|
// it, a click picks the place
|
||||||
|
class editor_map
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
editor_map() = default;
|
||||||
|
~editor_map();
|
||||||
|
editor_map(editor_map const &) = delete;
|
||||||
|
editor_map &operator=(editor_map const &) = delete;
|
||||||
|
|
||||||
|
// at the cursor, Size big; true when a place was clicked
|
||||||
|
bool render(glm::vec2 const &Size);
|
||||||
|
void pick(glm::dvec2 const &Point);
|
||||||
|
// the map goes there, at most Scale m a pixel
|
||||||
|
void show(glm::dvec2 const &Point, double const Scale = 4.0);
|
||||||
|
bool picked() const { return m_picked; }
|
||||||
|
glm::dvec2 const &point() const { return m_point; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct image
|
||||||
|
{
|
||||||
|
glm::dvec2 low{0.0};
|
||||||
|
glm::dvec2 high{0.0};
|
||||||
|
int width{0};
|
||||||
|
int height{0};
|
||||||
|
std::vector<std::uint8_t> pixels;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
void request(glm::vec2 const &Size);
|
||||||
|
void upload();
|
||||||
|
void release();
|
||||||
|
|
||||||
|
glm::dvec2 m_centre{515000.0, 460000.0};
|
||||||
|
double m_scale{1000.0}; // m a pixel
|
||||||
|
bool m_fitted{false};
|
||||||
|
double m_changed{0.0};
|
||||||
|
bool m_dirty{true};
|
||||||
|
glm::vec2 m_pressed{0.f};
|
||||||
|
bool m_dragging{false};
|
||||||
|
std::future<image> m_request;
|
||||||
|
std::uint64_t m_texture{0};
|
||||||
|
glm::dvec2 m_image_low{0.0};
|
||||||
|
glm::dvec2 m_image_high{0.0};
|
||||||
|
std::string m_error;
|
||||||
|
bool m_picked{false};
|
||||||
|
glm::dvec2 m_point{0.0};
|
||||||
|
};
|
||||||
@@ -11,15 +11,13 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "launcher/scenery_wizard.h"
|
#include "launcher/scenery_wizard.h"
|
||||||
#include "application/application.h"
|
#include "application/application.h"
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
|
#include "editor/editorGeoMap.hpp"
|
||||||
#include "editor/editorOrthophoto.hpp"
|
#include "editor/editorOrthophoto.hpp"
|
||||||
#include "editor/editorSettings.hpp"
|
#include "editor/editorSettings.hpp"
|
||||||
#include "utilities/Globals.h"
|
#include "utilities/Globals.h"
|
||||||
#include "utilities/Logs.h"
|
#include "utilities/Logs.h"
|
||||||
#include "utilities/translation.h"
|
#include "utilities/translation.h"
|
||||||
|
|
||||||
#include "stb/stb_image.h"
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -32,61 +30,6 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
double constexpr kSemiMajor{6378137.0};
|
|
||||||
double constexpr kFlattening{1.0 / 298.257222101};
|
|
||||||
double constexpr kScale{0.9993};
|
|
||||||
double constexpr kFalseEasting{500000.0};
|
|
||||||
double constexpr kFalseNorthing{-5300000.0};
|
|
||||||
double constexpr kCentralMeridian{19.0};
|
|
||||||
glm::dvec2 const kPolandLow{140000.0, 120000.0};
|
|
||||||
glm::dvec2 const kPolandHigh{890000.0, 800000.0};
|
|
||||||
std::string const kMapService{"https://mapy.geoportal.gov.pl/wss/ext/OSM/BaseMap/service"};
|
|
||||||
|
|
||||||
double seconds()
|
|
||||||
{
|
|
||||||
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::dvec2 to_puwg(double const Latitude, double const Longitude)
|
|
||||||
{
|
|
||||||
auto const e2{2.0 * kFlattening - kFlattening * kFlattening};
|
|
||||||
auto const ep2{e2 / (1.0 - e2)};
|
|
||||||
auto const phi{glm::radians(Latitude)};
|
|
||||||
auto const sine{std::sin(phi)};
|
|
||||||
auto const cosine{std::cos(phi)};
|
|
||||||
auto const n{kSemiMajor / std::sqrt(1.0 - e2 * sine * sine)};
|
|
||||||
auto const t{std::tan(phi) * std::tan(phi)};
|
|
||||||
auto const c{ep2 * cosine * cosine};
|
|
||||||
auto const a{glm::radians(Longitude - kCentralMeridian) * cosine};
|
|
||||||
auto const m{kSemiMajor * ((1.0 - e2 / 4.0 - 3.0 * e2 * e2 / 64.0 - 5.0 * e2 * e2 * e2 / 256.0) * phi - (3.0 * e2 / 8.0 + 3.0 * e2 * e2 / 32.0 + 45.0 * e2 * e2 * e2 / 1024.0) * std::sin(2.0 * phi) +
|
|
||||||
(15.0 * e2 * e2 / 256.0 + 45.0 * e2 * e2 * e2 / 1024.0) * std::sin(4.0 * phi) - (35.0 * e2 * e2 * e2 / 3072.0) * std::sin(6.0 * phi))};
|
|
||||||
auto const x{kScale * n * (a + (1.0 - t + c) * std::pow(a, 3) / 6.0 + (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * ep2) * std::pow(a, 5) / 120.0)};
|
|
||||||
auto const y{kScale * (m + n * std::tan(phi) * (a * a / 2.0 + (5.0 - t + 9.0 * c + 4.0 * c * c) * std::pow(a, 4) / 24.0 + (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * ep2) * std::pow(a, 6) / 720.0))};
|
|
||||||
return {kFalseEasting + x, y + kFalseNorthing};
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::dvec2 to_degrees(glm::dvec2 const &Point)
|
|
||||||
{
|
|
||||||
auto const e2{2.0 * kFlattening - kFlattening * kFlattening};
|
|
||||||
auto const ep2{e2 / (1.0 - e2)};
|
|
||||||
auto const x{Point.x - kFalseEasting};
|
|
||||||
auto const m{(Point.y - kFalseNorthing) / kScale};
|
|
||||||
auto const mu{m / (kSemiMajor * (1.0 - e2 / 4.0 - 3.0 * e2 * e2 / 64.0 - 5.0 * e2 * e2 * e2 / 256.0))};
|
|
||||||
auto const e1{(1.0 - std::sqrt(1.0 - e2)) / (1.0 + std::sqrt(1.0 - e2))};
|
|
||||||
auto const phi1{mu + (3.0 * e1 / 2.0 - 27.0 * std::pow(e1, 3) / 32.0) * std::sin(2.0 * mu) + (21.0 * e1 * e1 / 16.0 - 55.0 * std::pow(e1, 4) / 32.0) * std::sin(4.0 * mu) +
|
|
||||||
(151.0 * std::pow(e1, 3) / 96.0) * std::sin(6.0 * mu) + (1097.0 * std::pow(e1, 4) / 512.0) * std::sin(8.0 * mu)};
|
|
||||||
auto const sine{std::sin(phi1)};
|
|
||||||
auto const c1{ep2 * std::cos(phi1) * std::cos(phi1)};
|
|
||||||
auto const t1{std::tan(phi1) * std::tan(phi1)};
|
|
||||||
auto const n1{kSemiMajor / std::sqrt(1.0 - e2 * sine * sine)};
|
|
||||||
auto const r1{kSemiMajor * (1.0 - e2) / std::pow(1.0 - e2 * sine * sine, 1.5)};
|
|
||||||
auto const d{x / (n1 * kScale)};
|
|
||||||
auto const phi{phi1 - (n1 * std::tan(phi1) / r1) * (d * d / 2.0 - (5.0 + 3.0 * t1 + 10.0 * c1 - 4.0 * c1 * c1 - 9.0 * ep2) * std::pow(d, 4) / 24.0 +
|
|
||||||
(61.0 + 90.0 * t1 + 298.0 * c1 + 45.0 * t1 * t1 - 252.0 * ep2 - 3.0 * c1 * c1) * std::pow(d, 6) / 720.0)};
|
|
||||||
auto const lambda{(d - (1.0 + 2.0 * t1 + c1) * std::pow(d, 3) / 6.0 + (5.0 - 2.0 * c1 + 28.0 * t1 - 3.0 * c1 * c1 + 8.0 * ep2 + 24.0 * t1 * t1) * std::pow(d, 5) / 120.0) / std::cos(phi1)};
|
|
||||||
return {glm::degrees(phi), kCentralMeridian + glm::degrees(lambda)};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool valid_name(std::string const &Name)
|
bool valid_name(std::string const &Name)
|
||||||
{
|
{
|
||||||
return false == Name.empty() && std::all_of(Name.begin(), Name.end(), [](char const Character) { return (Character >= 'a' && Character <= 'z') || (Character >= '0' && Character <= '9') || Character == '_' || Character == '-'; });
|
return false == Name.empty() && std::all_of(Name.begin(), Name.end(), [](char const Character) { return (Character >= 'a' && Character <= 'z') || (Character >= '0' && Character <= '9') || Character == '_' || Character == '-'; });
|
||||||
@@ -96,22 +39,16 @@ bool valid_name(std::string const &Name)
|
|||||||
|
|
||||||
ui::scenerywizard_panel::scenerywizard_panel() : ui_panel(STR("New scenery"), false) {}
|
ui::scenerywizard_panel::scenerywizard_panel() : ui_panel(STR("New scenery"), false) {}
|
||||||
|
|
||||||
ui::scenerywizard_panel::~scenerywizard_panel()
|
ui::scenerywizard_panel::~scenerywizard_panel() = default;
|
||||||
{
|
|
||||||
if (m_request.valid())
|
|
||||||
m_request.wait();
|
|
||||||
if (m_texture != 0)
|
|
||||||
glDeleteTextures(1, &m_texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui::scenerywizard_panel::render_contents()
|
void ui::scenerywizard_panel::render_contents()
|
||||||
{
|
{
|
||||||
upload_map();
|
|
||||||
auto const available{ImGui::GetContentRegionAvail()};
|
auto const available{ImGui::GetContentRegionAvail()};
|
||||||
auto const form{360.f * Global.ui_scale};
|
auto const form{360.f * Global.ui_scale};
|
||||||
glm::vec2 const map{std::max(200.f, available.x - form - ImGui::GetStyle().ItemSpacing.x), std::max(200.f, available.y)};
|
glm::vec2 const map{std::max(200.f, available.x - form - ImGui::GetStyle().ItemSpacing.x), std::max(200.f, available.y)};
|
||||||
ImGui::BeginChild("##wizardmap", ImVec2(map.x, map.y), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
ImGui::BeginChild("##wizardmap", ImVec2(map.x, map.y), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
render_map(map - glm::vec2{16.f});
|
if (m_map.render(map - glm::vec2{16.f}))
|
||||||
|
pick(m_map.point());
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginChild("##wizardform", ImVec2(0.f, map.y), true);
|
ImGui::BeginChild("##wizardform", ImVec2(0.f, map.y), true);
|
||||||
@@ -119,151 +56,10 @@ void ui::scenerywizard_panel::render_contents()
|
|||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui::scenerywizard_panel::render_map(glm::vec2 const &Size)
|
|
||||||
{
|
|
||||||
auto const origin{ImGui::GetCursorScreenPos()};
|
|
||||||
if (false == m_fitted)
|
|
||||||
{
|
|
||||||
m_fitted = true;
|
|
||||||
m_centre = (kPolandLow + kPolandHigh) * 0.5;
|
|
||||||
m_scale = std::max((kPolandHigh.x - kPolandLow.x) / Size.x, (kPolandHigh.y - kPolandLow.y) / Size.y);
|
|
||||||
}
|
|
||||||
ImGui::InvisibleButton("##map", ImVec2(Size.x, Size.y));
|
|
||||||
auto const hovered{ImGui::IsItemHovered()};
|
|
||||||
auto const active{ImGui::IsItemActive()};
|
|
||||||
auto const &io{ImGui::GetIO()};
|
|
||||||
glm::vec2 const mouse{io.MousePos.x, io.MousePos.y};
|
|
||||||
glm::vec2 const middle{origin.x + Size.x * 0.5f, origin.y + Size.y * 0.5f};
|
|
||||||
auto const to_world = [&](glm::vec2 const &Screen) { return glm::dvec2{m_centre.x + (Screen.x - middle.x) * m_scale, m_centre.y - (Screen.y - middle.y) * m_scale}; };
|
|
||||||
auto const to_screen = [&](glm::dvec2 const &World) { return ImVec2(middle.x + static_cast<float>((World.x - m_centre.x) / m_scale), middle.y - static_cast<float>((World.y - m_centre.y) / m_scale)); };
|
|
||||||
|
|
||||||
if (hovered && io.MouseWheel != 0.f)
|
|
||||||
{
|
|
||||||
auto const under{to_world(mouse)};
|
|
||||||
m_scale = std::clamp(m_scale * std::pow(0.8, static_cast<double>(io.MouseWheel)), 0.25, 2000.0);
|
|
||||||
m_centre = {under.x - (mouse.x - middle.x) * m_scale, under.y + (mouse.y - middle.y) * m_scale};
|
|
||||||
m_dirty = true;
|
|
||||||
m_changed = seconds();
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemActivated())
|
|
||||||
{
|
|
||||||
m_pressed = mouse;
|
|
||||||
m_dragging = false;
|
|
||||||
}
|
|
||||||
if (active)
|
|
||||||
{
|
|
||||||
if (glm::length(mouse - m_pressed) > 4.f)
|
|
||||||
m_dragging = true;
|
|
||||||
if (m_dragging && (io.MouseDelta.x != 0.f || io.MouseDelta.y != 0.f))
|
|
||||||
{
|
|
||||||
m_centre.x -= io.MouseDelta.x * m_scale;
|
|
||||||
m_centre.y += io.MouseDelta.y * m_scale;
|
|
||||||
m_dirty = true;
|
|
||||||
m_changed = seconds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemDeactivated() && false == m_dragging)
|
|
||||||
pick(to_world(mouse));
|
|
||||||
|
|
||||||
if (m_dirty && seconds() - m_changed > 0.25 && false == m_request.valid())
|
|
||||||
request_map(Size);
|
|
||||||
|
|
||||||
auto *drawlist{ImGui::GetWindowDrawList()};
|
|
||||||
drawlist->PushClipRect(origin, ImVec2(origin.x + Size.x, origin.y + Size.y), true);
|
|
||||||
drawlist->AddRectFilled(origin, ImVec2(origin.x + Size.x, origin.y + Size.y), IM_COL32(200, 214, 222, 255));
|
|
||||||
if (m_texture != 0)
|
|
||||||
drawlist->AddImage((ImTextureID)(static_cast<std::intptr_t>(m_texture)), to_screen({m_image_low.x, m_image_high.y}), to_screen({m_image_high.x, m_image_low.y}));
|
|
||||||
if (m_picked)
|
|
||||||
{
|
|
||||||
auto const at{to_screen(m_point)};
|
|
||||||
auto const colour{IM_COL32(220, 30, 30, 255)};
|
|
||||||
drawlist->AddCircle(at, 10.f, colour, 24, 2.5f);
|
|
||||||
drawlist->AddLine(ImVec2(at.x - 16.f, at.y), ImVec2(at.x + 16.f, at.y), colour, 2.f);
|
|
||||||
drawlist->AddLine(ImVec2(at.x, at.y - 16.f), ImVec2(at.x, at.y + 16.f), colour, 2.f);
|
|
||||||
}
|
|
||||||
std::string info;
|
|
||||||
if (hovered)
|
|
||||||
{
|
|
||||||
auto const world{to_world(mouse)};
|
|
||||||
auto const degrees{to_degrees(world)};
|
|
||||||
info = format(STR_C("E %.3f km, N %.3f km (%.5f N, %.5f E)"), world.x / 1000.0, world.y / 1000.0, degrees.x, degrees.y);
|
|
||||||
}
|
|
||||||
if (m_request.valid())
|
|
||||||
info += (info.empty() ? "" : " ") + std::string{STR_C("loading the map...")};
|
|
||||||
else if (false == m_map_error.empty())
|
|
||||||
info += (info.empty() ? "" : " ") + m_map_error;
|
|
||||||
if (false == info.empty())
|
|
||||||
{
|
|
||||||
auto const size{ImGui::CalcTextSize(info.c_str())};
|
|
||||||
drawlist->AddRectFilled(ImVec2(origin.x + 4.f, origin.y + 4.f), ImVec2(origin.x + 12.f + size.x, origin.y + 8.f + size.y), IM_COL32(0, 0, 0, 170), 4.f);
|
|
||||||
drawlist->AddText(ImVec2(origin.x + 8.f, origin.y + 6.f), IM_COL32(255, 255, 255, 240), info.c_str());
|
|
||||||
}
|
|
||||||
drawlist->PopClipRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui::scenerywizard_panel::request_map(glm::vec2 const &Size)
|
|
||||||
{
|
|
||||||
m_dirty = false;
|
|
||||||
auto const width{std::clamp(static_cast<int>(Size.x), 64, 2048)};
|
|
||||||
auto const height{std::clamp(static_cast<int>(Size.y), 64, 2048)};
|
|
||||||
glm::dvec2 const half{width * 0.5 * m_scale, height * 0.5 * m_scale};
|
|
||||||
auto const low{m_centre - half};
|
|
||||||
auto const high{m_centre + half};
|
|
||||||
char bbox[160];
|
|
||||||
std::snprintf(bbox, sizeof(bbox), "%.1f,%.1f,%.1f,%.1f", low.x, low.y, high.x, high.y);
|
|
||||||
auto const url{kMapService + "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=osm&STYLES=&SRS=EPSG:2180&BBOX=" + bbox + "&WIDTH=" + std::to_string(width) + "&HEIGHT=" + std::to_string(height) + "&FORMAT=image/png"};
|
|
||||||
m_request = std::async(std::launch::async, [url, low, high, width, height]() {
|
|
||||||
map_result result;
|
|
||||||
result.low = low;
|
|
||||||
result.high = high;
|
|
||||||
std::vector<std::uint8_t> data;
|
|
||||||
if (false == editor_http_get(url, data, result.error))
|
|
||||||
return result;
|
|
||||||
int w{0}, h{0}, components{0};
|
|
||||||
auto *pixels{stbi_load_from_memory(data.data(), static_cast<int>(data.size()), &w, &h, &components, 4)};
|
|
||||||
if (pixels == nullptr)
|
|
||||||
{
|
|
||||||
result.error = STR("The map service sent no image");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
result.width = w;
|
|
||||||
result.height = h;
|
|
||||||
result.pixels.assign(pixels, pixels + static_cast<std::size_t>(w) * h * 4);
|
|
||||||
stbi_image_free(pixels);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui::scenerywizard_panel::upload_map()
|
|
||||||
{
|
|
||||||
if (false == m_request.valid() || m_request.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
|
|
||||||
return;
|
|
||||||
auto result{m_request.get()};
|
|
||||||
if (false == result.error.empty() || result.pixels.empty())
|
|
||||||
{
|
|
||||||
m_map_error = result.error.empty() ? std::string{STR("The map service sent no image")} : STR("No map: ") + result.error;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_map_error.clear();
|
|
||||||
if (m_texture == 0)
|
|
||||||
glGenTextures(1, &m_texture);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, m_texture);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, result.width, result.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.pixels.data());
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
m_image_low = result.low;
|
|
||||||
m_image_high = result.high;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui::scenerywizard_panel::pick(glm::dvec2 const &Point)
|
void ui::scenerywizard_panel::pick(glm::dvec2 const &Point)
|
||||||
{
|
{
|
||||||
m_point = Point;
|
m_map.pick(Point);
|
||||||
m_picked = true;
|
auto const degrees{editor_geo::to_degrees(Point)};
|
||||||
auto const degrees{to_degrees(Point)};
|
|
||||||
std::snprintf(m_latlon, sizeof(m_latlon), "%.6f, %.6f", degrees.x, degrees.y);
|
std::snprintf(m_latlon, sizeof(m_latlon), "%.6f, %.6f", degrees.x, degrees.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +98,7 @@ void ui::scenerywizard_panel::render_form()
|
|||||||
ImGui::InputTextWithHint("##title", STR_C("title, written in the file"), m_title, sizeof(m_title));
|
ImGui::InputTextWithHint("##title", STR_C("title, written in the file"), m_title, sizeof(m_title));
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
auto const placed{m_picked || false == m_realistic};
|
auto const placed{m_map.picked() || false == m_realistic};
|
||||||
auto const ready{named && false == exists && placed};
|
auto const ready{named && false == exists && placed};
|
||||||
if (false == ready)
|
if (false == ready)
|
||||||
{
|
{
|
||||||
@@ -322,8 +118,8 @@ void ui::scenerywizard_panel::render_form()
|
|||||||
ImGui::TextUnformatted(STR_C("Centre of the scenery, PUWG 1992"));
|
ImGui::TextUnformatted(STR_C("Centre of the scenery, PUWG 1992"));
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
ImGui::SetTooltip("%s", STR_C("PUWG 1992: the coordinate system of the maps of Poland, in km"));
|
ImGui::SetTooltip("%s", STR_C("PUWG 1992: the coordinate system of the maps of Poland, in km"));
|
||||||
double east{m_point.x / 1000.0};
|
double east{m_map.point().x / 1000.0};
|
||||||
double north{m_point.y / 1000.0};
|
double north{m_map.point().y / 1000.0};
|
||||||
ImGui::SetNextItemWidth(130.f);
|
ImGui::SetNextItemWidth(130.f);
|
||||||
auto changed{ImGui::InputDouble(STR_C("E, km##east"), &east, 0.0, 0.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue)};
|
auto changed{ImGui::InputDouble(STR_C("E, km##east"), &east, 0.0, 0.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue)};
|
||||||
ImGui::SetNextItemWidth(130.f);
|
ImGui::SetNextItemWidth(130.f);
|
||||||
@@ -331,8 +127,7 @@ void ui::scenerywizard_panel::render_form()
|
|||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
pick({east * 1000.0, north * 1000.0});
|
pick({east * 1000.0, north * 1000.0});
|
||||||
m_centre = m_point;
|
m_map.show(m_map.point(), 1000.0);
|
||||||
m_dirty = true;
|
|
||||||
}
|
}
|
||||||
ImGui::SetNextItemWidth(-90.f);
|
ImGui::SetNextItemWidth(-90.f);
|
||||||
auto const entered{ImGui::InputTextWithHint("##latlon", STR_C("latitude, longitude"), m_latlon, sizeof(m_latlon), ImGuiInputTextFlags_EnterReturnsTrue)};
|
auto const entered{ImGui::InputTextWithHint("##latlon", STR_C("latitude, longitude"), m_latlon, sizeof(m_latlon), ImGuiInputTextFlags_EnterReturnsTrue)};
|
||||||
@@ -345,10 +140,8 @@ void ui::scenerywizard_panel::render_form()
|
|||||||
double latitude{0.0}, longitude{0.0};
|
double latitude{0.0}, longitude{0.0};
|
||||||
if (words >> latitude >> longitude && latitude > 45.0 && latitude < 58.0 && longitude > 10.0 && longitude < 28.0)
|
if (words >> latitude >> longitude && latitude > 45.0 && latitude < 58.0 && longitude > 10.0 && longitude < 28.0)
|
||||||
{
|
{
|
||||||
pick(to_puwg(latitude, longitude));
|
pick(editor_geo::to_puwg(latitude, longitude));
|
||||||
m_centre = m_point;
|
m_map.show(m_map.point());
|
||||||
m_scale = std::min(m_scale, 4.0);
|
|
||||||
m_dirty = true;
|
|
||||||
m_status.clear();
|
m_status.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -356,9 +149,9 @@ void ui::scenerywizard_panel::render_form()
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
ImGui::SetTooltip("%s", STR_C("Degrees, as given by the online maps; the map moves there"));
|
ImGui::SetTooltip("%s", STR_C("Degrees, as given by the online maps; the map moves there"));
|
||||||
if (m_picked)
|
if (m_map.picked())
|
||||||
{
|
{
|
||||||
auto const degrees{to_degrees(m_point)};
|
auto const degrees{editor_geo::to_degrees(m_map.point())};
|
||||||
ImGui::TextDisabled(STR_C("%.5f N, %.5f E"), degrees.x, degrees.y);
|
ImGui::TextDisabled(STR_C("%.5f N, %.5f E"), degrees.x, degrees.y);
|
||||||
}
|
}
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
@@ -387,7 +180,7 @@ bool ui::scenerywizard_panel::create_scenery()
|
|||||||
char date[32];
|
char date[32];
|
||||||
std::strftime(date, sizeof(date), "%Y-%m-%d", std::localtime(&now));
|
std::strftime(date, sizeof(date), "%Y-%m-%d", std::localtime(&now));
|
||||||
char reference[96];
|
char reference[96];
|
||||||
std::snprintf(reference, sizeof(reference), "//$g PUWG1992 %.3f %.3f", m_point.x / 1000.0, m_point.y / 1000.0);
|
std::snprintf(reference, sizeof(reference), "//$g PUWG1992 %.3f %.3f", m_map.point().x / 1000.0, m_map.point().y / 1000.0);
|
||||||
if (m_realistic)
|
if (m_realistic)
|
||||||
{
|
{
|
||||||
auto const &stored{EditorSettings.orthophoto()};
|
auto const &stored{EditorSettings.orthophoto()};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "application/uilayer.h"
|
#include "application/uilayer.h"
|
||||||
|
#include "editor/editorGeoMap.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <future>
|
#include <future>
|
||||||
@@ -28,40 +29,12 @@ class scenerywizard_panel : public ui_panel
|
|||||||
void render_contents() override;
|
void render_contents() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct map_result
|
|
||||||
{
|
|
||||||
glm::dvec2 low{0.0};
|
|
||||||
glm::dvec2 high{0.0};
|
|
||||||
int width{0};
|
|
||||||
int height{0};
|
|
||||||
std::vector<std::uint8_t> pixels;
|
|
||||||
std::string error;
|
|
||||||
};
|
|
||||||
|
|
||||||
void render_map(glm::vec2 const &Size);
|
|
||||||
void render_form();
|
void render_form();
|
||||||
void request_map(glm::vec2 const &Size);
|
|
||||||
void upload_map();
|
|
||||||
void pick(glm::dvec2 const &Point);
|
void pick(glm::dvec2 const &Point);
|
||||||
bool create_scenery();
|
bool create_scenery();
|
||||||
|
|
||||||
glm::dvec2 m_centre{515000.0, 460000.0};
|
editor_map m_map;
|
||||||
double m_scale{1000.0};
|
|
||||||
bool m_fitted{false};
|
|
||||||
double m_changed{0.0};
|
|
||||||
bool m_dirty{true};
|
|
||||||
glm::vec2 m_pressed{0.f};
|
|
||||||
bool m_dragging{false};
|
|
||||||
|
|
||||||
std::future<map_result> m_request;
|
|
||||||
unsigned int m_texture{0};
|
|
||||||
glm::dvec2 m_image_low{0.0};
|
|
||||||
glm::dvec2 m_image_high{0.0};
|
|
||||||
std::string m_map_error;
|
|
||||||
|
|
||||||
bool m_picked{false};
|
|
||||||
bool m_realistic{true};
|
bool m_realistic{true};
|
||||||
glm::dvec2 m_point{0.0};
|
|
||||||
char m_filename[64]{};
|
char m_filename[64]{};
|
||||||
char m_title[128]{};
|
char m_title[128]{};
|
||||||
char m_latlon[64]{};
|
char m_latlon[64]{};
|
||||||
|
|||||||
Reference in New Issue
Block a user