Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Plugins/PlanEditor/geometry_placer.h
T
berrux-dev 598fc2560e Complete Qt6 migration: full clean build green across groups G1-G12 incl. runtime plugin set
new_docker_build.sh gains Group 12 (212 GUI/Data plugins); fixes LtsUtilsGuiDds visibility (LTO-stripped exports), LtsProjectionWorkstation unity name shadow, 6 unity-off dirs (dup file-scope statics, ddsftc INTERFACE sources) and reconfigures BUILD_FILTERMANAGEMENT/BUILD_PLUGINS_SERVICEMONITOR=ON
2026-10-20 09:36:57 +02:00

477 lines
15 KiB
C++
Executable File

#pragma once
#include "plan_editor.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <utility>
#include "logging.h"
#include <Map/situation_map_types.h>
#include <Map/coord_translation_functor.h>
#include <Map/map.h>
#include <Map/geometry_model_renderer.h>
#include <Rubberbands/rubberband_factory.h>
#include <GeometryModel/geometry_model_factory.h>
#include <GeometryModel/geometry_node_visitor.h>
#include <MathUtils/math_const.h>
#include <MathUtils/simple_helper.h>
#include <MathUtils/matrix2d.h>
#include <QtCore/QList>
#include <QtWidgets/QGraphicsView>
#include <stdexcept>
namespace SituationMap
{
class Map;
class GeometryModel;
namespace RB { class RubberbandTransform; }
}
namespace PlanEditor
{
const unsigned int RubberbandMinPixelSize = 20;
template<class RubberbandClass> class GeometryPlacer : public QObject
{
public:
static RubberbandClass *create(PlanEditor *planEditor
, SituationMap::Map *map
, SituationMap::GeometryModel *geoModel
, const QList<SituationMap::UniqueMapId> &uidList
, QObject *parent);
bool isValid() const { return (m_previewGeoModel != nullptr); }
void changeStyleAttributes();
private:
GeometryPlacer(PlanEditor *planEditor, SituationMap::Map *map, SituationMap::GeometryModel *, const QList<SituationMap::UniqueMapId> &, QObject *parent);
virtual ~GeometryPlacer();
void applyTransform(SituationMap::GeometryModel *geoModel);
void abortTransform();
void previewTransform(SituationMap::Map *map, const SituationMap::MathUtils::Matrix2D &mat);
void previewTransform(SituationMap::Map *map, const SituationMap::MathUtils::Matrix2D &mat, const SituationMap::GeometryModel::Node &node);
void adjustEllipse(SituationMap::GeometryModel *geoModel, const SituationMap::GeometryModel::Node &p, const QList<QPointF> &v);
PlanEditor *m_planEditor;
QPointer<SituationMap::Map> m_map;
SituationMap::GeometryModel *m_previewGeoModel;
SituationMap::GeometryModelRenderer *m_previewGeoModelRenderer;
QList<SituationMap::UniqueMapId> m_uidList;
RubberbandClass *m_rubberband;
Logger &m_logger;
};
template<class RubberbandClass> RubberbandClass *GeometryPlacer<RubberbandClass>::create(PlanEditor *planEditor
, SituationMap::Map *map
, SituationMap::GeometryModel *geoModel
, const QList<SituationMap::UniqueMapId> &uidList
, QObject *parent)
{
if (!std::is_base_of<SituationMap::RB::RubberbandTransform, RubberbandClass>::value) return nullptr;
GeometryPlacer<RubberbandClass> *gP = new GeometryPlacer<RubberbandClass>(planEditor, map, geoModel, uidList, parent);
if (gP == nullptr) return nullptr;
if (!gP->isValid())
{
delete gP;
gP = nullptr;
return nullptr;
}
return gP->m_rubberband;
}
static const char *const PreviewLayer = "PlanEditorPreview";
template<class RubberbandClass> GeometryPlacer<RubberbandClass>::GeometryPlacer(PlanEditor *planEditor
, SituationMap::Map *map
, SituationMap::GeometryModel *geoModel
, const QList<SituationMap::UniqueMapId> &uidList
, QObject *parent)
: m_planEditor(planEditor)
, m_map(map)
, m_previewGeoModel(nullptr)
, m_previewGeoModelRenderer(nullptr)
, m_uidList()
, m_rubberband(nullptr)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("GeometryPlacer"))
{
if (uidList.isEmpty()) return;
m_previewGeoModel = SituationMap::GeometryModelFactory::singleton()->create();
qreal xmin = std::numeric_limits<qreal>::infinity();
qreal xmax = -std::numeric_limits<qreal>::infinity();
qreal ymin = std::numeric_limits<qreal>::infinity();
qreal ymax = -std::numeric_limits<qreal>::infinity();
m_uidList.reserve(uidList.size());
for (SituationMap::UniqueMapId uid : uidList)
{
const SituationMap::GeometryModel::Node &modelPrimitive = geoModel->findPrimitive(uid);
if (modelPrimitive.isValid())
{
const QList<QPointF> &vertexList = geoModel->getPrimitivePoints(modelPrimitive);
for (const QPointF &p : vertexList )
{
if (p.x() < xmin) xmin = p.x();
if (p.x() > xmax) xmax = p.x();
if (p.y() < ymin) ymin = p.y();
if (p.y() > ymax) ymax = p.y();
}
if (!vertexList.empty())
{
m_uidList << uid;
m_previewGeoModel->copy(modelPrimitive);
}
}
}
if (m_uidList.isEmpty())
{
WARNING_LOG(m_logger) << Q_FUNC_INFO << "Object list of GeometryPlacer is empty.";
return;
}
if ((!std::isfinite(xmin)) && (!std::isfinite(ymin)) && (!std::isfinite(xmax)) && (!std::isfinite(ymax)))
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << "Can't determine bounding box of selected features";
#ifdef LTS_DEBUG
throw std::range_error("Infinte bounding box, abort GeometryPlacer");
#else
return;
#endif
}
previewTransform(map, SituationMap::MathUtils::Matrix2D());
QPointF minCorner(xmin, ymin);
QPointF maxCorner(xmax, ymax);
minCorner = map->geoCoordinateToViewPortWindowPoint(minCorner);
maxCorner = map->geoCoordinateToViewPortWindowPoint(maxCorner);
qreal pixelWidth = maxCorner.x() - minCorner.x();
qreal pixelHeight = maxCorner.y() - minCorner.y();
bool isMinSize = false;
if (fabs(pixelWidth) < RubberbandMinPixelSize)
{
qreal centerX = (minCorner.x() + maxCorner.x()) * 0.5;
qreal dx = RubberbandMinPixelSize * 0.5;
dx = (pixelWidth < 0) ? -dx : dx;
minCorner.setX(centerX - dx);
maxCorner.setX(centerX + dx);
isMinSize = true;
}
if (fabs(pixelHeight) < RubberbandMinPixelSize)
{
qreal centerY = (minCorner.y() + maxCorner.y()) * 0.5;
qreal dy = RubberbandMinPixelSize * 0.5;
dy = (pixelHeight < 0) ? -dy : dy;
minCorner.setY(centerY - dy);
maxCorner.setY(centerY + dy);
isMinSize = true;
}
if (isMinSize)
{
minCorner = map->viewPortWindowPointToGeoCoordinate(minCorner);
maxCorner = map->viewPortWindowPointToGeoCoordinate(maxCorner);
xmin = minCorner.x();
ymin = minCorner.y();
xmax = maxCorner.x();
ymax = maxCorner.y();
}
m_rubberband = dynamic_cast<RubberbandClass *>(map->rubberbandFactory().create<RubberbandClass>(parent));
if (m_rubberband)
{
map->layerFactory().addOverlayLayer(QLatin1String(PreviewLayer), QLatin1String(PreviewLayer));
m_previewGeoModelRenderer = map->createGeometryModelRenderer(QLatin1String(PreviewLayer), m_previewGeoModel);
m_rubberband->setAutoDelete(true);
QVector<QPointF> vertexVector(4);
vertexVector[0] = QPointF(xmin, ymin);
vertexVector[1] = QPointF(xmax, ymin);
vertexVector[2] = QPointF(xmax, ymax);
vertexVector[3] = QPointF(xmin, ymax);
m_rubberband->rectangleVerticies(vertexVector);
QObject::connect(m_rubberband, &SituationMap::RB::Rubberband::apply, this, [this, geoModel]() { applyTransform(geoModel); });
QObject::connect(m_rubberband, &SituationMap::RB::Rubberband::abort, this, [this]() { abortTransform(); });
QObject::connect(m_rubberband, &SituationMap::RB::Rubberband::finished, this, [this]() { delete this; });
QObject::connect(m_rubberband, &SituationMap::RB::RubberbandTransform::updatePreview
, this, [this, map](SituationMap::RB::Rubberband *, const SituationMap::MathUtils::Matrix2D & mat) { previewTransform(map, mat); });
m_rubberband->setUserData(this);
}
else
{
delete m_previewGeoModel;
m_previewGeoModel = nullptr;
}
}
class ChangeStyleVisitor : public SituationMap::GeometryNodeVisitor
{
PlanEditor *m_planEditor;
bool apply(SituationMap::GeometryModel *, const SituationMap::GeometryModel::Node &node, SituationMap::GeometryModel::PrimitiveType nodeType) override
{
switch (nodeType)
{
case SituationMap::GeometryModel::PrimitiveType::MapDefaults : break;
case SituationMap::GeometryModel::PrimitiveType::Node : break;
case SituationMap::GeometryModel::PrimitiveType::RootNode : break;
case SituationMap::GeometryModel::PrimitiveType::Unknown : break;
default: m_planEditor->addStyleAttributes(node, false);
}
return true;
}
public:
ChangeStyleVisitor(PlanEditor *planEditor, SituationMap::GeometryModel *model)
: SituationMap::GeometryNodeVisitor(model), m_planEditor(planEditor)
{;}
~ChangeStyleVisitor() = default;
};
template<class RubberbandClass> void GeometryPlacer<RubberbandClass>::adjustEllipse(SituationMap::GeometryModel *geoModel, const SituationMap::GeometryModel::Node &p, const QList<QPointF> &v)
{
auto distance = [](const QPointF & v0, const QPointF & v1) -> double
{
QPointF dv = v1 - v0;
return sqrt(dv.x() * dv.x() + dv.y() * dv.y());
};
SituationMap::CoordTranslationFunctor *proj = m_map->coordTranslationFunctor();
QPointF centerPoint(v.first());
for (int i = 1; i < 4; ++i) centerPoint += v.at(i);
centerPoint /= 4;
const QPointF &v0 = (proj) ? proj->geographicToMap(v[0]) : v[0];
const QPointF &v1 = (proj) ? proj->geographicToMap(v[1]) : v[1];
const QPointF &v2 = (proj) ? proj->geographicToMap(v[2]) : v[2];
double diameterX = distance(v0, v1);
double diameterY = distance(v1, v2);
double phiSin = (v1.y() - v0.y()) / diameterX;
double phiCos = (v1.x() - v0.x()) / diameterX;
double phi = SituationMap::MathUtils::getMathAngle(QPointF(phiCos, phiSin)) * 180.0 / MathConst::pi;
geoModel->setPrimitiveAttribute(p, SituationMap::GeometryModel::Attribute::Center, QVariant(centerPoint));
geoModel->setPrimitiveAttribute(p, SituationMap::GeometryModel::Attribute::Width, QVariant(diameterX));
geoModel->setPrimitiveAttribute(p, SituationMap::GeometryModel::Attribute::Height, QVariant(diameterY));
geoModel->setPrimitiveAttribute(p, SituationMap::GeometryModel::Attribute::Rotate, QVariant(phi));
};
template<class RubberbandClass> void GeometryPlacer<RubberbandClass>::changeStyleAttributes()
{
ChangeStyleVisitor doChangeStyle(m_planEditor, m_previewGeoModel);
doChangeStyle.traverse();
m_previewGeoModelRenderer->render();
m_map->getQGraphicsView()->viewport()->update();
}
template<class RubberbandClass> void GeometryPlacer<RubberbandClass>::applyTransform(SituationMap::GeometryModel *geoModel)
{
SituationMap::CoordTranslationFunctor *proj = m_map->coordTranslationFunctor();
geoModel->startUndoBlock();
{
const SituationMap::MathUtils::Matrix2D &mat = m_rubberband->transformMatrix();
for (SituationMap::UniqueMapId uid : std::as_const(m_uidList))
{
const SituationMap::GeometryModel::Node &modelPrimitive = geoModel->findPrimitive(uid);
if (modelPrimitive.isValid())
{
QList<QPointF> vertexList = m_previewGeoModel->getPrimitivePoints(modelPrimitive);
QList<QPointF>::iterator vit = vertexList.begin();
if (proj)
{
for (; vit != vertexList.end(); ++vit)
{
const QPointF &pt = proj->geographicToMap(*vit);
*vit = proj->mapToGeographic(mat.apply(pt));
}
}
else
{
for (; vit != vertexList.end(); ++vit)
*vit = mat.apply(*vit);
}
geoModel->setPrimitivePoints(modelPrimitive, vertexList);
if ((geoModel->getType(modelPrimitive) == SituationMap::GeometryModel::PrimitiveType::Ellipse) && (vertexList.size() == 4))
adjustEllipse(geoModel, modelPrimitive, vertexList);
}
}
}
geoModel->endUndoBlock(QObject::tr("apply transform geometry", TR_UNDO_TOKEN));
delete this;
}
template<class RubberbandClass> void GeometryPlacer<RubberbandClass>::abortTransform()
{
delete this;
}
template<class RubberbandClass> void GeometryPlacer<RubberbandClass>::previewTransform(SituationMap::Map *map, const SituationMap::MathUtils::Matrix2D &mat)
{
if (mat.isIdentity()) return;
previewTransform(map, mat, m_previewGeoModel->getRootNode());
m_previewGeoModelRenderer->render();
map->getQGraphicsView()->viewport()->update();
}
template<class RubberbandClass> void GeometryPlacer<RubberbandClass>::previewTransform(SituationMap::Map *map
, const SituationMap::MathUtils::Matrix2D &mat
, const SituationMap::GeometryModel::Node &node)
{
if (mat.isIdentity()) return;
SituationMap::GeometryModel::Node n(node);
SituationMap::CoordTranslationFunctor *proj = m_map->coordTranslationFunctor();
while (n.isValid())
{
SituationMap::GeometryModel::PrimitiveType primitiveType = m_previewGeoModel->getType(n);
if (n.hasChildNodes() && ((primitiveType == SituationMap::GeometryModel::PrimitiveType::RootNode) || (primitiveType == SituationMap::GeometryModel::PrimitiveType::Node)))
previewTransform(map, mat, n.firstChildNode());
else
{
QList<QPointF> vertexList = m_previewGeoModel->getPrimitivePoints(n);
if (!vertexList.isEmpty())
{
QList<QPointF>::iterator vit = vertexList.begin();
if (proj)
{
for (; vit != vertexList.end(); ++vit)
{
const QPointF &pt = proj->geographicToMap(*vit);
*vit = proj->mapToGeographic(mat.apply(pt));
}
}
else
{
for (; vit != vertexList.end(); ++vit)
*vit = mat.apply(*vit);
}
m_previewGeoModel->setPrimitivePoints(n, vertexList);
if ((m_previewGeoModel->getType(n) == SituationMap::GeometryModel::PrimitiveType::Ellipse) && (vertexList.size() == 4))
adjustEllipse(m_previewGeoModel, n, vertexList);
}
}
n = n.next();
}
}
template<class RubberbandClass> GeometryPlacer<RubberbandClass>::~GeometryPlacer()
{
if (m_map)
{
delete (m_previewGeoModel);
delete (m_previewGeoModelRenderer);
if (m_rubberband)
m_map->layerFactory().removeOverlayLayer(QLatin1String(PreviewLayer));
m_map->update();
}
m_previewGeoModel = nullptr;
m_previewGeoModelRenderer = nullptr;
m_rubberband = nullptr;
}
}